Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WithFields for Tuples #9533

Merged
merged 20 commits into from
Nov 24, 2021
Merged

WithFields for Tuples #9533

merged 20 commits into from
Nov 24, 2021

Conversation

electriclilies
Copy link
Contributor

@electriclilies electriclilies commented Nov 18, 2021

This is a draft PR implementing WithFields on tuples.

WithFields is parallel to WithAttrs in behavior. It returns this* if all the fields passed in are the same as the ones already in this. Otherwise, it creates a copy of the tuple with the new fields.

The motivation for WithFields is to ensure that spans, checked types, and other fields on nodes are propagated correctly during passes so we can rely on that information. If we decide to go ahead with this change, I will also implement WithFields for all other relay exprs.

@@ -71,6 +71,29 @@ Tuple::Tuple(tvm::Array<relay::Expr> fields, Span span) {
data_ = std::move(n);
}

Tuple Tuple::WithFields(Optional<Array<Expr>> opt_fields, Optional<Span> opt_span) {
Array<Expr> fields = opt_fields.value_or(get()->fields);
Copy link
Member

@tqchen tqchen Nov 18, 2021

Choose a reason for hiding this comment

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

make it as a global function https://github.com/apache/tvm/blob/main/include/tvm/ir/attrs.h#L371, so that you do std::move and COW when there is a single copy.

Copy link
Member

@tqchen tqchen Nov 18, 2021

Choose a reason for hiding this comment

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

To elaborate, x.WithFields in the current case might mutate x if it is the only copy.

auto backup_fields = x.fields;
y = x.WithFields(..., ...)
// if x is the only copy, it will change x itself. Which may not be the intention of the API.

// this check will fail, and that could against the original intention of x.WithFields(that x should not change)
CHECK(x->fields.same_as(backup_fields));

instead if we do

y = WithFields(std::move(x), .., ...)_

and x is the only copy, we know for certainly that x will not be used in the future and it is safe to re-use the memory space

Copy link
Contributor Author

@electriclilies electriclilies Nov 18, 2021

Choose a reason for hiding this comment

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

The intention is to mutate if x is the only copy.

Copy link
Member

@tqchen tqchen Nov 18, 2021

Choose a reason for hiding this comment

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

I see, thanks for the explaination, if it is a mution semantics, then we might want to rename it as SetXYZ(or COWSet?), like Array.Set.

It might be worth thinking a bit about the tradeoff since the original semantics on the python and c++ side is WithAttrs do not mutate the original value that is passed in(unless it is moved in and ensures that there is no followup), the api name With is also deliberately chosen over SetXYZ to show that intention(that we do not modify the original value). So it would be good to make it align with the WithAttrs semantics.

It is indeed quite subtle difference but these are very interesting design choices here.

A common usage pattern of with_attrs may ends up look like this:

x = PrimFunc(args, body)
# y is a new copy that comes with a new attr
# x remains as the original case
y = x.with_attr("new_attr", value)

record([x, y])

So in the c++ API

// y do not reuse x' sapce
y = WithAttrs(x, attr)
record({y, x})

Instead if you do

// compiler could decide that y can reuse x, because x is no longer used, the call becomes a move
y = WithAttrs(x, attr)
// x is not used as a followup work

And with a deliberate move where x is the only copy, we ensures that x's space get reused

y = WithAttrs(std::move(x), attr)
// x is not used as a followup work

Copy link
Contributor Author

@electriclilies electriclilies Nov 18, 2021

Choose a reason for hiding this comment

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

OK, thanks for the feedback. I just changed the signature to mirror WithAttrs and I removed the COW functionality. Now it will return the argument tuple if all the fields passed in are the same, otherwise it creates a copy and returns that.

bool all_fields_unchanged = true;
if (fields.size() == tuple->fields.size()) {
for (uint i = 0; i < fields.size(); i++) {
all_fields_unchanged &= fields[i] == (tuple->fields[i]);
Copy link
Member

Choose a reason for hiding this comment

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

consider use same_as to avoid future overload of operator ==

if (all_fields_unchanged) {
return std::move(tuple);
} else {
return Tuple(std::move(fields), std::move(span));
Copy link
Member

@tqchen tqchen Nov 18, 2021

Choose a reason for hiding this comment

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

it is useful to actually invoke COW here.

TNode* node = input.CopyOnWrite();
Again it is a bit sutble here.

If the tuple is moved in and a single copy, it is actually useful to directly reuse that part of the memory. This will allow calls like below to only create one copy instead of two

x = Tuple(fields, span);
x = WithFields(std::move(x), new_fields, span)

This is also the reason why WithAttrs is a global function(so there won't be ambiguity in such case).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see, Mark and I were confused about what you were saying before. Changed it back to use COW

@electriclilies electriclilies marked this pull request as ready for review November 19, 2021 01:03
@electriclilies electriclilies changed the title [Draft] WithFields for Tuples WithFields for Tuples Nov 19, 2021
@electriclilies
Copy link
Contributor Author

I think this is ready for review now, pending CI. @mbs-octoml @mikepapadim PTAL!

@mikepapadim
Copy link
Contributor

LGTM

Copy link
Member

@tqchen tqchen left a comment

Choose a reason for hiding this comment

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

one minor nit, otherwise LGTM!

TupleNode* cow_tuple_node = tuple.CopyOnWrite();
cow_tuple_node->fields = fields;
cow_tuple_node->span = span;
return GetRef<Tuple>(cow_tuple_node);
Copy link
Member

Choose a reason for hiding this comment

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

nit, you can directly return tuple here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done :)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for all your help with the subtleties!

@tqchen
Copy link
Member

tqchen commented Nov 20, 2021

Hmm, looking into the MSVC error, not sure if it is related to include order or something

@electriclilies
Copy link
Contributor Author

electriclilies commented Nov 20, 2021

@tqchen Thanks for looking! I'm going to resubmit at EOD and see if it goes away, it looked like it might be flaky to me but I wanted to see if anything else broke further along in CI since it's still running.

@tqchen
Copy link
Member

tqchen commented Nov 20, 2021

no worries, we are not in a hurry. this seems was a compiler error

@jroesch jroesch merged commit 3c48cad into apache:main Nov 24, 2021
dchauhan-arm pushed a commit to dchauhan-arm/tvm that referenced this pull request Nov 29, 2021
mehrdadh pushed a commit to mehrdadh/tvm that referenced this pull request Dec 1, 2021
mehrdadh pushed a commit to mehrdadh/tvm that referenced this pull request Dec 1, 2021
ylc pushed a commit to ylc/tvm that referenced this pull request Jan 7, 2022
yangulei pushed a commit to yangulei/tvm that referenced this pull request Jan 11, 2022
yangulei pushed a commit to yangulei/tvm that referenced this pull request Jan 12, 2022
ylc pushed a commit to ylc/tvm that referenced this pull request Jan 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants