Skip to content
This repository has been archived by the owner on Nov 17, 2023. It is now read-only.

in-place reshape ops #14053

Merged
merged 3 commits into from
Jul 28, 2019
Merged

in-place reshape ops #14053

merged 3 commits into from
Jul 28, 2019

Conversation

szha
Copy link
Member

@szha szha commented Feb 2, 2019

Description

make NDArray fluent methods for expand_dims/flatten/squeeze in-place reshapes.

Checklist

Essentials

Please feel free to remove inapplicable items for your PR.

Changes

  • make NDArray fluent methods for expand_dims/flatten/squeeze in-place reshapes.

Comments

@szha szha force-pushed the inplace_reshape branch 2 times, most recently from 58e9144 to 0f36d95 Compare February 2, 2019 06:52
@ZhennanQin
Copy link
Contributor

Have you tried this with mkldnn enabled? E.g reshape the output of mkldnn convolution?

@szha
Copy link
Member Author

szha commented Feb 2, 2019

@ZhennanQin I didn't test that specifically based on the assumption that the backend implementation should not cause existing APIs such as NDArray.reshape or NDArray.shape to break.

@stephenrawls
Copy link
Contributor

@szha -- Thanks for putting in this patch!

I have a couple questions about the operator expand_dims, which I understand you are actually changing the python code to not use that operator, but I think still has relevance.

(1) I originally discovered this because I was using the C API to call MXImperativeInvoke() using the expand_dims operator, and I noticed it was causing a copy. This fix only effects the Python version when operating on ndarrays, not users of the expand_dims operator.

I see that there is a path in the operator that checks if it is an in-place operation. Presumably it uses that path if I pass an output array that is the same NDArrayHandle as the input array? But what if I still need the original input array handle, and I want to create a new output array handle with the expanded dim but still not make a copy?

(2) In the issue I created you commented that: "For symbol (and thus the hybridized version), since in-place identity is possible it should not matter".

Can you talk a little more about that? I assume you mean that in this case:

x_expanded = x.expand_dims(1)
y = x_expanded + foo

The engine can figure out that x is not needed again, and can thus turn the expand_dims(1) into an in-place operation that doesn't make a copy?

I'm not very familiar with how this part of the code works, so what happens if you had code that looked like this?

x_expanded = x.expand_dims(1)
y = x_expanded + foo
z = 2 * x

i.e. the code still makes a reference to the original x, and thus presumably the engine can't decide to use the in-place version of expand_dims in that case, right? So I guess my question is -- Does the ability for the Syblolic / hybridized engine to elide the copy depend on the code not referencing the un-expanded version of the array after calling expand_dims()? If so, it seems like there will still be some use cases where an unexpected copy is happening.

@szha
Copy link
Member Author

szha commented Feb 3, 2019

@stephenrawls

This fix only effects the Python version when operating on ndarrays, not users of the expand_dims operator.

Right. The reshape operators always return a copy. This is because in imperative mode there is no complete computation graph to be analyzed and see whether a copy is needed or not.

I see that there is a path in the operator that checks if it is an in-place operation. Presumably it uses that path if I pass an output array that is the same NDArrayHandle as the input array?

Right, it is done through output=original_array argument to the operators. It does not work for autograd (i.e. Gluon's training mode) as autograd doesn't support in-place operation.

But what if I still need the original input array handle, and I want to create a new output array handle with the expanded dim but still not make a copy?

As long as it's not used in the context of autograd, you can save the original handle before in-place operation. Though the output of in-place op shares the space with the input, it's still a separate handle.

For symbol (and thus the hybridized version), since in-place identity is possible it should not matter.

To elaborate, in symbolic mode, the backend can see the complete computation graph, and as a result it can analyze the graph (through node coloring) and figure out whether nodes can share the same space. FInplaceIdentity is the graph node attribute that expresses whether this should be allowed in order for compiler to plan memory. For details see 3rdparty/tvm/nnvm/src/pass/plan_memory.cc.

Note that such memory plan happens before the engine/scheduler work happens, and it happens for both symbolic executor and hybridized Gluon HybridBlock.

@stephenrawls
Copy link
Contributor

@szha -- Thanks for the detailed explanation, that helps a lot.

So just for clarity, I see that in the operator definition for expand_dims, it sets the FInPlaceIdentity attribute to true:
https://github.com/apache/incubator-mxnet/blob/45d1a1e6ff9c58cfc75f72651c1bf671ac7f1885/src/operator/tensor/matrix_op.cc#L400-L419

I didn't see anything in the plan_memory.cc file that depended on autograd / training.

So should I take it that the code is smart enough to not allocate new memory for the expand_dims operator in symbolic mode, i.e. not to make a copy, even during training when using autograd?

If so, then I think I understand it, and I see what you mean about only needing to fix this for the Python ndarray use case.

Thanks again!
Stephen

@vandanavk
Copy link
Contributor

@mxnet-label-bot add [pr-awaiting-review, NDArray]

@marcoabreu marcoabreu added NDArray pr-awaiting-review PR is waiting for code review labels Feb 4, 2019
@szha
Copy link
Member Author

szha commented Feb 5, 2019

@stephenrawls actually, autograd comes into play in pure imperative mode. MXNet's autograd is trace based and does not support in-place operation such as input = mx.nd.expand_dims(input, ..., output=input). You're actually right in that it doesn't help the case where you write it in imperative mode in C++. In order to make use of the graph pass for saving memory, you need to use Symbol API to express the model and use symbolic execution (or its counter part for Gluon, which is CachedOp).

@szha szha requested a review from reminisce February 5, 2019 06:56
@TaoLv
Copy link
Member

TaoLv commented Feb 5, 2019

Does it mean that with these changes python API will have different behavior compared with other frontend languages?

@szha
Copy link
Member Author

szha commented Feb 5, 2019

@TaoLv Other language bindings don't seem to have the fluent methods for reshaping NDArray.

@szha
Copy link
Member Author

szha commented Feb 6, 2019

The current implementation would break backward compatibility for in-place update.

@szha
Copy link
Member Author

szha commented Feb 11, 2019

I plan on adding an inplace argument to these fluent methods to allow users to specify whether in-place operation should be used.

@ankkhedia
Copy link
Contributor

@reminisce Could you please help review the PR?

@szha
Copy link
Member Author

szha commented Feb 15, 2019

I haven't finished this PR yet as I haven't got around to add the in-place option.

@ankkhedia
Copy link
Contributor

@mxnet-label-bot add [pr-work-in-progress]

@marcoabreu marcoabreu added the pr-work-in-progress PR is still work in progress label Feb 15, 2019
@ankkhedia
Copy link
Contributor

@mxnet-label-bot remove [pr-awaiting-review]

@marcoabreu marcoabreu removed the pr-awaiting-review PR is waiting for code review label Feb 15, 2019
@szha szha removed the pr-work-in-progress PR is still work in progress label Feb 19, 2019
Copy link
Contributor

@reminisce reminisce left a comment

Choose a reason for hiding this comment

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

Please add unit tests.

python/mxnet/ndarray/ndarray.py Outdated Show resolved Hide resolved
@szha
Copy link
Member Author

szha commented Feb 22, 2019

@reminisce good catch. I was relying on existing tests before adding the inplace option but forgot to add tests for them afterwards.

@szha
Copy link
Member Author

szha commented Mar 3, 2019

Added tests

Copy link
Member

@wkcn wkcn left a comment

Choose a reason for hiding this comment

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

LGTM : )

I have a question.

It seems that in-place flag does not support Symbol.
In gluon, F.expand_dim(a, inplace=True) will raise inplace not found when hybridizing the model.

Could we add warnings in mxnet/symbol.py ?

@szha
Copy link
Member Author

szha commented Mar 6, 2019

@wkcn good point on symbol and HybridBlock. I'm not sure if a warning should be added as it can get very verbose.

@szha
Copy link
Member Author

szha commented Mar 6, 2019

Nonetheless it should not throw an error when using inplace with hybridize. Let me see how to best deal with it.

@karan6181
Copy link
Contributor

@szha Thank you for your contributions! Is this PR work in progress?

@abhinavs95
Copy link
Contributor

@szha is this PR ready to merge?

@piyushghai
Copy link
Contributor

@szha Is this good to merge ?

@Roshrini Roshrini added the pr-awaiting-response PR is reviewed and waiting for contributor to respond label Apr 16, 2019
Copy link
Member

@Roshrini Roshrini left a comment

Choose a reason for hiding this comment

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

LGTM

@szha
Copy link
Member Author

szha commented Apr 17, 2019

Thanks for all the reviews. I plan to add a dummy option in the symbol version of the reshape ops so that it won't throw error when people use this option in the context of Gluon

@roywei
Copy link
Member

roywei commented Apr 30, 2019

@mxnet-label-bot add[pr-work-in-progress]

@marcoabreu marcoabreu added the pr-work-in-progress PR is still work in progress label Apr 30, 2019
@roywei
Copy link
Member

roywei commented Apr 30, 2019

@mxnet-label-bot remove [pr-awaiting-response]

@marcoabreu marcoabreu removed the pr-awaiting-response PR is reviewed and waiting for contributor to respond label Apr 30, 2019
@karan6181
Copy link
Contributor

@szha Did you get a chance to add a dummy option in the symbol version? Thanks!

@szha szha force-pushed the inplace_reshape branch 3 times, most recently from eb1da49 to d58bd77 Compare May 27, 2019 06:05
@piyushghai
Copy link
Contributor

@szha @wkcn This PR is ready to be merged I guess ?

@mxnet-label-bot Update [NDArray, pr-awaiting-merge]

@marcoabreu marcoabreu added pr-awaiting-merge Review and CI is complete. Ready to Merge and removed pr-work-in-progress PR is still work in progress labels Jun 7, 2019
@szha
Copy link
Member Author

szha commented Jun 8, 2019

@piyushghai thanks for pinging. I wanted to hold onto this for the 1.5 release code freeze.

@abhinavs95
Copy link
Contributor

@szha is this good to go now?

@szha szha merged commit 3ececb3 into apache:master Jul 28, 2019
@szha szha deleted the inplace_reshape branch July 28, 2019 02:33
anirudhacharya pushed a commit to anirudhacharya/mxnet that referenced this pull request Aug 20, 2019
* in-place reshape ops

* add inplace option

* add dummy arguments to symbol
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
NDArray pr-awaiting-merge Review and CI is complete. Ready to Merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

expand_dims() makes copy instead of simply reshaping