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

Reduce the work to add a new expr: unify the structure of exprs #2190

Merged
merged 52 commits into from
Nov 18, 2022

Conversation

zasdfgbnm
Copy link
Collaborator

@zasdfgbnm zasdfgbnm commented Nov 15, 2022

I added a new data member to Expr: std::vector<Val*> attributes_. Subclasses of Expr are now not allowed to have their own data member in the class. Instead, if a data member is neither an input nor an output, then the subclass should register it as an attribute. Some subclass of Expr needs to store plain data that is not a Val, such as DataType, bool, etc. These plain data are not Val, therefore impossible to store as an attribute. To work around this, I created a class template <typename T> class Attribute : public Val which can be used to store data of type T.

By following the contract that subclass of Expr must store data inside inputs, outputs, or attributes, every expr type can be constructed with the following ctor:

Expr(IrBuilderPasskey, std::vector<Val*> inputs, std::vector<Val*> outputs, std::vector<Val*> attributes);

And IR cloning and shallow copying now become independent of expr type. Now adding a new expr type does not need to manually write down the cloning constructor, sameAs, and shallowCopy. They can all be inherited from Expr. This is a big save that helps me to reduce ~800 lines of code.

I also refactored IrCloner to move the type dispatch as a member function.

I believe that with my change in this PR, we can also greatly simplify IR mutator. But I will not do it in this PR.

I recommend starting the review from torch/csrc/jit/codegen/cuda/ir_base_nodes.h.

@zasdfgbnm zasdfgbnm changed the base branch from devel to no-expr-type November 15, 2022 23:55
@zasdfgbnm zasdfgbnm changed the title WIP Reduce the work to add a new expr: unify the structure of exprs Nov 16, 2022
@zasdfgbnm zasdfgbnm changed the title Reduce the work to add a new expr: unify the structure of exprs [WIP] Reduce the work to add a new expr: unify the structure of exprs Nov 16, 2022
@@ -8,74 +8,6 @@ namespace jit {
namespace fuser {
namespace cuda {

//! Clone an IR node, forwarding the arguments to the IrCloner constructor.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

moved to header file

@@ -15,20 +16,6 @@ class Kernel;

class IrCloner;

// Passkey for builder to register properties with statements, and to call
Copy link
Collaborator Author

@zasdfgbnm zasdfgbnm Nov 17, 2022

Choose a reason for hiding this comment

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

Moved to a separate file torch/csrc/jit/codegen/cuda/ir_builder_key.h so it can be included in torch/csrc/jit/codegen/cuda/ir_base_nodes.h.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh, I did that too in #2197 😄

@zasdfgbnm zasdfgbnm changed the title [WIP] Reduce the work to add a new expr: unify the structure of exprs Reduce the work to add a new expr: unify the structure of exprs Nov 17, 2022
@zasdfgbnm zasdfgbnm marked this pull request as ready for review November 17, 2022 09:40
// Creates a new instance of the expression with all its field copied.
// Note that unlike IrCloner, this function only do a shallow copy
virtual Expr* shallowCopy() const = 0;
Expr* shallowCopy() const;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This method now is not even virtual

Comment on lines 74 to 80
#define DECLARE_CLONE \
virtual Statement* clone(IrCloner* ir_cloner) const override;

#define DEFINE_CLONE(ClassName) \
Statement* ClassName::clone(IrCloner* ir_cloner) const { \
return IrBuilder::clone(this, ir_cloner); \
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we undef these somewhere? Otherwise, we should use names like PYTORCH_NVFUSER_DECLARE_CLONE.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think so. These macros will be used in cpp files. I will rename it as NVFUSER_DECLARE_CLONE.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Renamed

//! to hold non-IR data, such as DataType, std::vector<int>, etc. Please don't
//! use this class to hold IR nodes or their pointers.
template <typename T>
class TORCH_CUDA_CU_API PlainVal : public Val {
Copy link
Collaborator

Choose a reason for hiding this comment

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

So, this is intended to hold non-IR data, but then why is it a subclass of Val?

Copy link
Collaborator

Choose a reason for hiding this comment

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

So, this is used to hold IR node attributes. Does it need to be a subclass of Val?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Because attributes_ are vectors of Val* and I want to push plain values into attributes_. Maybe I should change attributes_ to a vector of Statement*? Currently, lots of kernel IR has expr members, and I am storing them as something like PlainVal<Statement*>; maybe making attributes_ a vector of Statement* would make it easier because I can directly store it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, I see. Yeah, we want to store proper IR-node values as well. Can it be std:vector<std::any>?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not today. std::any is C++17 but PyTorch now is still using C++14

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, right. Can it be a vector of unique_ptr?

I just feel odd to have arbitrary attributes, like a DataType and a bool flag, as a Val. Please convince me if you really want to use PlainVal.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I believe the biggest advantage of making it a Val is that, IrCloner will automatically help you do deep copy when necessary. For example, if I have an attribute bool, and I clone the expr, then I will also have a new PlainVal<bool> object for the new expr. If later, I changed the new expr's bool attribute, the old one's bool attribute is not changed. But if we manage this by hand, we could make mistakes that, when cloning an expr, the old and new expr are pointing to the same object for its attribute, and if we change one, the other is also changed.

So to answer your question:

Can it be a vector of unique_ptr?

Yes. Actually, it SHOULD be a unique_ptr instead of a shared_ptr, for the reason I described above.

But without using PlainVal, we will have to manually do a deep copy of the std::vector<std::unique_ptr<AbstractExprAttribute>> attributes_. It's a little inconvenient, but should be more efficient.

I don't have a strong preference about which one we should choose.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh, wait, no. It is not "a little inconvenient", but super convenient. Without knowing T, we can not do a deep copy.

Copy link
Collaborator

Choose a reason for hiding this comment

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

OK, that makes sense. (I assume you meant "super inconvenient")

Let's use PlainVal, but I'd prefer a name like Attribute, which I think a little more clearly implies what it's supposed to be used for.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Renamed

//! to hold non-IR data, such as DataType, std::vector<int>, etc. Please don't
//! use this class to hold IR nodes or their pointers.
template <typename T>
class TORCH_CUDA_CU_API PlainVal : public Val {
Copy link
Collaborator

Choose a reason for hiding this comment

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

So, this is used to hold IR node attributes. Does it need to be a subclass of Val?

const std::vector<Val*>& initVals() const {
return init_vals_;
std::vector<Val*> initVals() const {
return {attributes().begin() + 2, attributes().end()};
Copy link
Collaborator

Choose a reason for hiding this comment

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

A bit concerning that we need to make sure we use correct offsets in the attribute vector. It seems like we are effectively losing static type safety, although the overall IR classes are more consistent.

Given that typically there are not many attributes in each class, I don't think the above concern is critical.

Copy link
Collaborator

@naoyam naoyam left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for the refactoring.

@zasdfgbnm zasdfgbnm merged commit 50861ec into devel Nov 18, 2022
@zasdfgbnm zasdfgbnm deleted the structured-expr branch November 18, 2022 08:27
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.

2 participants