Repository navigation
[HYBRID FRONTEND] Modify hybrid script to new interface; hybrid op supported; enable compilation_database in CMakeList.txt - #1757
Conversation
|
Ideally, we should keep the old function parse as it is. And support a import tvm
@tvm.hybrid.script
def outer_product(a, b):
c = tvm.output((128, 128), dtype)
for j in range(128):
for i in range(128):
c[i,j] = a[i] * b[j]
return c
a = tvm.placeholder((128, ), 'float32', 'a')
b = tvm.placeholder((128, ), 'float32', 'b')
# returns an ExternOp
c = outer_product(a, b)
d = tvm.compute(c.shape, lambda *i: c(*i)+1)
sch = tvm.create_schedule(d.op)
... |
|
OK. Then I know how an op works. |
|
@tqchen but if we explicitly define the outputs, how can we do multi-output op, and how can we support in-place algorithms? |
Multi output caseimport tvm
@tvm.hybrid.script
def outer_product(a, b):
c = tvm.output((128, 128), dtype)
d = tvm.output((128, 128), dtype)
for j in range(128):
for i in range(128):
c[i,j] = a[i] * b[j]
d[i,j] = a[i] + 1
return c, d
a = tvm.placeholder((128, ), 'float32', 'a')
b = tvm.placeholder((128, ), 'float32', 'b')
# returns an ExternOp
c0, c1 = outer_product(a, b)
d = tvm.compute(c.shape, lambda *i: c0(*i)+c1(*i))
sch = tvm.create_schedule(d.op)
.Inplace UpdateI agree that is harder to express an inplace update. The general design goal of the operator relation is that everything is immutable and there is no inplace update. We can still assign c and a to be the same buffer to achieve the effect of of inplace update though. |
|
The main reason I think the above approach is nice is that the code itself is pretty consistent with tvm.compute flow and can be mixed together with other expressions. The only drawback of inplace update can be resolved by supporting inplace update hybrid code but do not support these for extern op for now |
|
I like the old one better for 2 reasons:
import tvm
@tvm.hybrid.script
def outer_product(a, b, c):
for j in range(128):
for i in range(128):
c[i,j] = a[i] * b[j]
a = tvm.placeholder((128, ), 'float32', 'a')
b = tvm.placeholder((128, ), 'float32', 'b')
c = tvm.placeholder((128, ), 'float32', 'b')
# returns c's op
# if it is multi-output, return the tuple of output ops
op = outer_product(a, b, c)
d = tvm.compute(c.shape, lambda *i: c(*i)+1)
sch = tvm.create_schedule(d.op)In general, it is an extension of the current hybrid module. |
|
@were can you update this PR and add a few examples? |
|
@tqchen @kevinthesun op = _ExternOp(input_placeholders, input_buffers, output_buffers, ..., body)
ret = [op.output(i) for i in range(len(output_buffers))]
return ret[0] if len(ret) == 1 else retOriginally, what I am supposed to do is to register an op and return its output tensors, which seems to be OK, but the problem I am facing is that all the output tensors' names are same as the op's name. Just as it is mentioned above, Also, if I want to give a different name to the tensor, how can I specify the relationship between the tensor and op? |
|
The output tensor's name can be viewed as op_name.value0, opname.value1 which have different meaning. Note that under the hood tvm uses pointer uniqueness, so even if the name are the same it is fine |
|
OK. I know know what you mean --- no matter how users name the output tensors, we will finally convert all the tensors to name Then a new problem comes when doing in-place. (or more accurately, it is not a problem. I just want to make sure it is acceptable): out = foo(a)
sch = tvm.create_schedule(a.op)
tvm.build(sch, [out])Say, |
|
For now, we could avoid supporting inplace functions, if we want to create an inplace op, we can do out = foo(a)
sch = tvm.create_schedule(a.op)
buffer = tvm.decl_buffer(shape=out.shape)
tvm.build(sch, [buffer], binds={a:buffer, out:buffer}) |
|
I am refusing not to support in-place because it is some kind of "rollback" --- it is supported before but disabled just because the "glue" to the framework. |
|
The example I gave kinda of support inplace operations |
|
For now, I think it is good enough. |
|
It sounds good to me, as long as the behavior is well documented |
|
@kevinthesun @xqdan @tqchen Can you guys take a look so that I can get it in? |
|
Can we update this place? Since we have an unified routine after this patch. |
| self.func_name = func_name # The name of the function to be lowered | ||
| self.iter_axis = [] | ||
| self.outputs = [] # Output tensors' name | ||
| self.side_effect = set() # Tensors with side effects |
There was a problem hiding this comment.
Just a question, what's side_effect for?
There was a problem hiding this comment.
Later for in-place operation.
I think when we need to support inline/fuse between ComputeOp and HybirdOp, the data dependence between the Ops is important. |
|
@xqdan Actually, current hybrid op is the same as the external op, which cannot be splitted. Thus, I think no inline will be done? |
| } | ||
| auto n = make_node<HybridOpNode>(*this); | ||
| ret = op::ReplaceTensor(ret, rmap); | ||
| ret = op::ReplaceProvideTensor(ret, rmap); |
There was a problem hiding this comment.
Could you put some comments to explain the logic of this function?
There was a problem hiding this comment.
It is a long story. I hope you can get what I am saying inside my updated comments.
There was a problem hiding this comment.
Just curious, "In hybrid script all the tensors have their own names specified by the users." Does this mean that user can write something like "out = my_out_tensor((1, 3, 224, 224), 'float')"? In this case, if out is generated by, say flatten op with name 'tensor', out has different name with flatten op. This is why we need replace tensors, and in output IR, the out tensor name won't be "my_out_tensor" any more?
There was a problem hiding this comment.
Yes. Actually, you can dump both ret in cpp and op.body in python to see this.
There was a problem hiding this comment.
Got it. Maybe we can document this behavior somewhere, such as tvm.hybrid.script, make it transparent to user.
| ret = AttrStmt::make( | ||
| bind_spec, attr::buffer_bind_scope, | ||
| Call::make(Handle(), intrinsic::tvm_tuple, tuple, Call::Intrinsic), ret); | ||
| }; |
There was a problem hiding this comment.
please put comments about why we need binding here
There was a problem hiding this comment.
this is just copy paste from extern_op.cc
| Stmt ret = repl.Mutate(stmt); | ||
| return repl.found ? ret : stmt; | ||
| } | ||
| Expr ReplaceProvideTensor(Expr expr, |
The difference is that, with hybird op, we can have the whole ir context, so it's possible to do some schedule, but external op is just a black box. |
|
@xqdan That's why I opened a new op node. Later more features will be supported and more attributes will be added. |
|
Update tutorial as well? |
|
Thanks @xqdan @kevinthesun for reviews, Thanks @were this is merged! |
…pported; enable compilation_database in CMakeList.txt (apache#1757)
…pported; enable compilation_database in CMakeList.txt (apache#1757)
…pported; enable compilation_database in CMakeList.txt (apache#1757)
Move old hybrid script frontend
to