Skip to content

[HYBRID FRONTEND] Modify hybrid script to new interface; hybrid op supported; enable compilation_database in CMakeList.txt - #1757

Merged
tqchen merged 29 commits into
apache:masterfrom
were:hybrid-op
Nov 19, 2018
Merged

tqchen merged 29 commits into
apache:masterfrom
were:hybrid-op

Conversation

@were

@were were commented Sep 21, 2018 •

Copy link
Copy Markdown
Contributor

Move old hybrid script frontend

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)

to

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) 

@tqchen tqchen self-assigned this Sep 22, 2018
@tqchen

tqchen commented Sep 22, 2018 •

Copy link
Copy Markdown
Member

Ideally, we should keep the old function parse as it is. And support a__call__ of the hybrid script that calls parse and then generate an ExternOp.

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)
...

@were

were commented Sep 22, 2018

Copy link
Copy Markdown
Contributor Author

OK. Then I know how an op works.

@were

were commented Sep 23, 2018

Copy link
Copy Markdown
Contributor Author

@tqchen but if we explicitly define the outputs, how can we do multi-output op, and how can we support in-place algorithms?

@tqchen

tqchen commented Sep 23, 2018

Copy link
Copy Markdown
Member

Multi output case

import 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 Update

I 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.

@tqchen

tqchen commented Sep 23, 2018

Copy link
Copy Markdown
Member

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

@were

were commented Sep 23, 2018 •

Copy link
Copy Markdown
Contributor Author

I like the old one better for 2 reasons:

  1. Calling a hybrid script becomes more like calling a TVM built module, which makes code clean when doing sw-emulation verification
  2. In-place algorithm support will be rollback, if we use the new style
    Why don't we do this?
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.

@tqchen tqchen added status: need update need update based on feedbacks and removed status: need review labels Sep 29, 2018
@were were changed the title [HYBRID FRONTEND] Support hybrid op. [HYBRID FRONTEND] Modify hybrid script to new interface. Oct 3, 2018
@tqchen

tqchen commented Oct 13, 2018

Copy link
Copy Markdown
Member

@were can you update this PR and add a few examples?

@were

were commented Oct 14, 2018

Copy link
Copy Markdown
Contributor Author

@tqchen @kevinthesun
I am facing a problem like this:

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 ret

Originally, 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, return c, d gives different names to the output tensors.

Also, if I want to give a different name to the tensor, how can I specify the relationship between the tensor and op?

@tqchen

tqchen commented Oct 14, 2018

Copy link
Copy Markdown
Member

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

@were

were commented Oct 15, 2018

Copy link
Copy Markdown
Contributor Author

OK. I know know what you mean --- no matter how users name the output tensors, we will finally convert all the tensors to name foo.v0, foo.v1, and etc.

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, foo is an in-place function call which transforms a to out. They are essentially the same buffer with alias, but I CANNOT use a when compilation. I need to force users to pass out, or the compiler cannot find the buffer in the binds.

@tqchen

tqchen commented Oct 15, 2018

Copy link
Copy Markdown
Member

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})

@were

were commented Oct 15, 2018

Copy link
Copy Markdown
Contributor Author

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.

@tqchen

tqchen commented Oct 15, 2018

Copy link
Copy Markdown
Member

The example I gave kinda of support inplace operations

@were

were commented Oct 15, 2018

Copy link
Copy Markdown
Contributor Author

For now, I think it is good enough.
My question is that we have already have buffer binds stored in Op's attributes, so can we do it spontaneously?

@tqchen

tqchen commented Oct 15, 2018

Copy link
Copy Markdown
Member

It sounds good to me, as long as the behavior is well documented

@were were changed the title [HYBRID FRONTEND] Modify hybrid script to new interface. [HYBRID FRONTEND] Modify hybrid script to new interface; hybrid op supported; enable compilation_database in CMakeList.txt Nov 15, 2018
@were

were commented Nov 15, 2018

Copy link
Copy Markdown
Contributor Author

@kevinthesun @xqdan @tqchen Can you guys take a look so that I can get it in?

@xqdan

xqdan commented Nov 16, 2018

Copy link
Copy Markdown
Contributor

Can we update this place? Since we have an unified routine after this patch.
https://github.com/dmlc/tvm/blob/master/python/tvm/build_module.py#L343

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a question, what's side_effect for?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later for in-place operation.

@xqdan

xqdan commented Nov 16, 2018

Copy link
Copy Markdown
Contributor

I believe TVM has no global (inter-op) check which leverages the input/output information based on side-effect free assumption.

If 2.'s bold assumption is not true or not acceptable for further extension of this project, I will pick the second solution in 1.

I think when we need to support inline/fuse between ComputeOp and HybirdOp, the data dependence between the Ops is important.

@were

were commented Nov 16, 2018

Copy link
Copy Markdown
Contributor Author

@xqdan Actually, current hybrid op is the same as the external op, which cannot be splitted. Thus, I think no inline will be done?

Comment thread src/op/hybrid_op.cc
}
auto n = make_node<HybridOpNode>(*this);
ret = op::ReplaceTensor(ret, rmap);
ret = op::ReplaceProvideTensor(ret, rmap);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you put some comments to explain the logic of this function?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a long story. I hope you can get what I am saying inside my updated comments.

@kevinthesun kevinthesun Nov 17, 2018 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Actually, you can dump both ret in cpp and op.body in python to see this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. Maybe we can document this behavior somewhere, such as tvm.hybrid.script, make it transparent to user.

Comment thread src/op/hybrid_op.cc
ret = AttrStmt::make(
bind_spec, attr::buffer_bind_scope,
Call::make(Handle(), intrinsic::tvm_tuple, tuple, Call::Intrinsic), ret);
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please put comments about why we need binding here

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is just copy paste from extern_op.cc

Comment thread src/op/op_util.cc Outdated
Stmt ret = repl.Mutate(stmt);
return repl.found ? ret : stmt;
}
Expr ReplaceProvideTensor(Expr expr,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only for Stmt is enough

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jesus, let me fix it!

@xqdan

xqdan commented Nov 16, 2018

Copy link
Copy Markdown
Contributor

@xqdan Actually, current hybrid op is the same as the external op, which cannot be splitted. Thus, I think no inline will be done?

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.

@were

were commented Nov 16, 2018

Copy link
Copy Markdown
Contributor Author

@xqdan That's why I opened a new op node. Later more features will be supported and more attributes will be added.

@kevinthesun

Copy link
Copy Markdown
Contributor

Update tutorial as well?

@xqdan xqdan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM now

@tqchen
tqchen merged commit 7a01476 into apache:master Nov 19, 2018
@tqchen

tqchen commented Nov 19, 2018

Copy link
Copy Markdown
Member

Thanks @xqdan @kevinthesun for reviews, Thanks @were this is merged!

@tqchen tqchen added status: accepted and removed status: need review status: need update need update based on feedbacks labels Nov 19, 2018

@junrushao junrushao left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@were
were deleted the hybrid-op branch December 14, 2018 04:00
FrozenGene pushed a commit to FrozenGene/tvm that referenced this pull request Dec 27, 2018
…pported; enable compilation_database in CMakeList.txt (apache#1757)
wweic pushed a commit to neo-ai/tvm that referenced this pull request Feb 20, 2019
…pported; enable compilation_database in CMakeList.txt (apache#1757)
wweic pushed a commit to neo-ai/tvm that referenced this pull request Feb 20, 2019
…pported; enable compilation_database in CMakeList.txt (apache#1757)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants