Allow arbitrary namespaces for Symbols#9018
Conversation
5d7b07d to
d977863
Compare
|
@ezyang can you take a look since I think you were the last to work on interned_strings.cpp |
d977863 to
0e8b803
Compare
facebook-github-bot
left a comment
There was a problem hiding this comment.
@zdevito has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
|
If it's just a raw string, do you really want it to be interned? I'm not convinced that there isn't secretly a namespace lurking in the background here. |
|
@ezyang This is for FunctionSchema, where sometimes I want a raw string, because it is referring to a function name and I want it reported to the user as they wrote it. Other times it is used as the schema for an operator and I need the Symbol for the operator to be fully qualified. More generally, I think having symbol act as a true interned string is useful because they are useful in various places. |
facebook-github-bot
left a comment
There was a problem hiding this comment.
@zdevito has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
|
OK, but for example, say a user manages to hand you a function named "onnx::Blah" (I'm sure, somewhere, Python lets you put awful names on functions). Aren't you going to interpret this as an actually ONNX namespaced thing? |
|
Those two uses are already in different namespaces based on how those values are used. For a function, the name field is used to look it up in the lexical environment. Builtin operators are looked up in the operator registry. But FunctionSchema is used to represent the valid arguments in either case. This way, the code doesn't have to convert from interned string to std::string to use this data structure. Similarly, we really don't need the attr:: namespace either. Only onnx:: and aten:: actually need disambiguation. |
|
Let me build this and see if I can come up with an example of what I'm nervous about. |
|
I think my primary objection to the changes in this patch is that it makes it easier to do the wrong thing. In the previous world, namespaces were mandatory. If you called a function In the previous world, you could have whatever junk you wanted after the namespace. In the previous world, the mandatory namespace was used as a way to say what strings mean. Yes, technically (I remember in our original discussion, you were not 100% pleased with the namespacing refactor, and suggested we refactor Symbol into an interned string concept, which is just plain interned strings with no semantics, and a symbol concept, where we have semantics. I still think this is a good idea; a definite improvement over the old design. Though, I'm not sure you need it for your use case.) |
I think we conceptually disagree here. I view Symbols as interned strings, full stop. They are a version of the string that makes switch statements possible in C++. I don't want them to be any more or less than that. The problem with the namespaces before this patch is that they removed this functionality -- there exists strings that I cannot represent with symbols. The schema stuff that I am doing is now introducing the first use where I need that functionality back. We should like take this conversation online sometime next week, since the PR is part of a chain needed in refactoring the way schema and the interpreter work. |
Why is that? What if we simply allowed using a C++-like convention for the global namespace (think |
0e8b803 to
9403e35
Compare
|
Ok, I updated this so that namespaces are always required, which I believe addresses @ezyang 's concerns. I still think this is worth landing because it simplifies the interned_strings implementation. |
torch/csrc/jit/test_jit.cpp
Outdated
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
torch/csrc/jit/interned_strings.cpp
Outdated
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This changes how Symbol is implemented. Now the namespace of a symbol is itself a Symbol in the 'namespaces' namespace. This allows Symbol to be used with arbitrary namespaces. This also simplifies the implementation. Like with string conversion, builtin primitives go through a fast path for namespace lookup while registered symbols require holding a lock and reading an array entry to lookup the namespace.
9403e35 to
27d7867
Compare
facebook-github-bot
left a comment
There was a problem hiding this comment.
@zdevito has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
facebook-github-bot
left a comment
There was a problem hiding this comment.
@zdevito has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
…n with human-readable schema. (#8885) Summary: This is a series of two commits that should probably be read separately. They are stacked on top of #9018 since the second commit requires it for correctness. Commit 1 ======= This commit is the first in a series that will clean up how we handle declaring operators and intrinsics in the JIT to make it more modular and readable. This introduces readable declarations that can be used to register operators and switches gen_jit_dispatch to generate this schema. A follow up PR will remove the dispatch keys like "add-3" and resolve ops directly based on the registered schema, further simplifying the generation process. * Switches schema over to parsed declarations, in the future this will allow something like: ``` registry.register_intrinsic("foo(Tensor a, Tensor b) -> Tensor", [](Stack& stack) { ... }) ``` This will allow the scalable registration of intrinsics for lists, tuples, and other ops, as long as meta-data for these ops (e.g. derivatives and size propagation routines). The declarations resemble those used by PythonArgParser but have been singificantly cleaned up to minimize the number of types that can appear in the declaration. We should strive to get the other parts of PyTorch switched over to this restricted declaration set when possible, but it is too much to do in a single PR. My hope is that eventually we will use a very similar language to describe declarations in C10, and this can serve as a guide for that. Parsing is done using the script lexer, so it is very robust to whitespace and extensible for future types. This removes the other way we encoded schema, and makes it easier to see what schema are registered. Current generated declarations: https://gist.github.com/zdevito/a96a17766fb3a098d69a91ee00abaaf6 * Switches how we handle attempting to use an integer in the place of a fixed-sized int list, such as in conv (e.g. 'int[3] stride=1'). Now that we can statically distinguish between int and Tensor, we handle the expansion as an implicit conversion in the compiler. This allows us to simplify the interpreter since it no longer needs to handle the conversion itself. * Schema declarations have been changed so that they match the type system in the IR exactly. In particular, attribute_info which was used by liftConstantAttributes has been dropped and constant attributes are lifted purely based on the type of the input. Type conversions in compiler have been simplified due to this change. * Error highlighting in ErrorReport now only reports at most 20 lines of code, to make reading where an error occurred easier. Commit 2 ======= This commit unifies aten_dispatch and aten_schema into a single Operator object that both contains schema and implementation information. In the future we can use this object to also contain functionality like shape prop and autodiff needed by all operators. Operators are registered globally, and dispatch logic uses the schema information to figure out which variant to use. Descriptor keys, a frequent source of inscrutable debug errors, have been removed. * Introduce Operator, to replace TensorOp. Unlike TensorOp, we use Operator for all op implementations, including primitives that may occur in the graphs. The only exceptions are ops that are only known to the interpreter like jumps, and GraphExecutors where we need to record additional debug info. * Adds a global registry for Operator implementations. aten_dispatch.cpp turns into register_aten_ops.cpp, which registers all the Operators for aten with the operator registry. register_prim_ops.cpp now contains the implementations for primitive operators that used to be in the interpreter. This means that it is now safe to use `getOperation(node)` to lookup the true interpreter function for the node, which will simplify const-propagation passes. * Remove addInterpreterOpHandler in favor of global operator registry. * Instead of descriptors, we match Node arguments directly against FunctionSchema describing expected inputs in `matchSchema`. `matchSchema` knows how parse both attributes and positional inputs from a node and match it to the appropriate registered operator. Debug error messages when we try to run an invalid operator are significantly improved: they now automatically display the schema for the op with the same name that are registered. * Merge aten_schema into regsiter_aten_ops. Each Operator takes a string schema which is parsed to determine when to dispatch to that op. * Cleans up gen_jit_dispatch.py now that we do not need to write out descriptors. In particular, skip_scalar_overloads can be removed since Richard's code sorts declarations to put Tensor, Tensor declarations first. * remove matchSchemaAndLiftConstantAttributes and use emitBuiltinCall instead to remove code duplication * refactor stack manipulation functions into a separate header file. Pull Request resolved: #8885 Reviewed By: jamesr66a Differential Revision: D8751048 Pulled By: zdevito fbshipit-source-id: 312aabfbf88307c5f6ab947b6caf691468b94557
Summary: Context: I am updating jit::FunctionSchema to use `Symbol name;` rather than `std::string name`. Sometimes the name refers to a builtin thing like `prim::UnpackTuple`, sometimes to an aten operator like `aten::add`, and sometimes just to a raw string, like `my_method_foo` that really doesn't belong in any namespace and should be printed to the user in that form. For this last case, I want the ability to create a raw Symbol again, like was previously possible, that just represents an interned string. This PR enables that use, keeps the other functionality still possible, and simplifies interned_string's implementation a bit. This changes how Symbol is implemented. Now the namespace of a symbol is optional and the namespaces themselves are Symbols. This allows Symbol to be used with arbitrary namespaces, and allows you to use Symbol as an simple interned string using via fromQualString and toQualString without :: in the string. This also simplifies the implementation. Like with string conversion, builtin primitives go through a fast path for namespace lookup while registered symbols require holding a lock and reading an array entry to lookup the namespace. Note: alexnet expect file update is from a previous commit. It doesn't run in CI because pytorch vision is not installed. Closes pytorch#9018 Reviewed By: SsnL Differential Revision: D8690449 Pulled By: zdevito fbshipit-source-id: b65ee57704641d7294fe115c5470cf55d406458f
…n with human-readable schema. (pytorch#8885) Summary: This is a series of two commits that should probably be read separately. They are stacked on top of pytorch#9018 since the second commit requires it for correctness. Commit 1 ======= This commit is the first in a series that will clean up how we handle declaring operators and intrinsics in the JIT to make it more modular and readable. This introduces readable declarations that can be used to register operators and switches gen_jit_dispatch to generate this schema. A follow up PR will remove the dispatch keys like "add-3" and resolve ops directly based on the registered schema, further simplifying the generation process. * Switches schema over to parsed declarations, in the future this will allow something like: ``` registry.register_intrinsic("foo(Tensor a, Tensor b) -> Tensor", [](Stack& stack) { ... }) ``` This will allow the scalable registration of intrinsics for lists, tuples, and other ops, as long as meta-data for these ops (e.g. derivatives and size propagation routines). The declarations resemble those used by PythonArgParser but have been singificantly cleaned up to minimize the number of types that can appear in the declaration. We should strive to get the other parts of PyTorch switched over to this restricted declaration set when possible, but it is too much to do in a single PR. My hope is that eventually we will use a very similar language to describe declarations in C10, and this can serve as a guide for that. Parsing is done using the script lexer, so it is very robust to whitespace and extensible for future types. This removes the other way we encoded schema, and makes it easier to see what schema are registered. Current generated declarations: https://gist.github.com/zdevito/a96a17766fb3a098d69a91ee00abaaf6 * Switches how we handle attempting to use an integer in the place of a fixed-sized int list, such as in conv (e.g. 'int[3] stride=1'). Now that we can statically distinguish between int and Tensor, we handle the expansion as an implicit conversion in the compiler. This allows us to simplify the interpreter since it no longer needs to handle the conversion itself. * Schema declarations have been changed so that they match the type system in the IR exactly. In particular, attribute_info which was used by liftConstantAttributes has been dropped and constant attributes are lifted purely based on the type of the input. Type conversions in compiler have been simplified due to this change. * Error highlighting in ErrorReport now only reports at most 20 lines of code, to make reading where an error occurred easier. Commit 2 ======= This commit unifies aten_dispatch and aten_schema into a single Operator object that both contains schema and implementation information. In the future we can use this object to also contain functionality like shape prop and autodiff needed by all operators. Operators are registered globally, and dispatch logic uses the schema information to figure out which variant to use. Descriptor keys, a frequent source of inscrutable debug errors, have been removed. * Introduce Operator, to replace TensorOp. Unlike TensorOp, we use Operator for all op implementations, including primitives that may occur in the graphs. The only exceptions are ops that are only known to the interpreter like jumps, and GraphExecutors where we need to record additional debug info. * Adds a global registry for Operator implementations. aten_dispatch.cpp turns into register_aten_ops.cpp, which registers all the Operators for aten with the operator registry. register_prim_ops.cpp now contains the implementations for primitive operators that used to be in the interpreter. This means that it is now safe to use `getOperation(node)` to lookup the true interpreter function for the node, which will simplify const-propagation passes. * Remove addInterpreterOpHandler in favor of global operator registry. * Instead of descriptors, we match Node arguments directly against FunctionSchema describing expected inputs in `matchSchema`. `matchSchema` knows how parse both attributes and positional inputs from a node and match it to the appropriate registered operator. Debug error messages when we try to run an invalid operator are significantly improved: they now automatically display the schema for the op with the same name that are registered. * Merge aten_schema into regsiter_aten_ops. Each Operator takes a string schema which is parsed to determine when to dispatch to that op. * Cleans up gen_jit_dispatch.py now that we do not need to write out descriptors. In particular, skip_scalar_overloads can be removed since Richard's code sorts declarations to put Tensor, Tensor declarations first. * remove matchSchemaAndLiftConstantAttributes and use emitBuiltinCall instead to remove code duplication * refactor stack manipulation functions into a separate header file. Pull Request resolved: pytorch#8885 Reviewed By: jamesr66a Differential Revision: D8751048 Pulled By: zdevito fbshipit-source-id: 312aabfbf88307c5f6ab947b6caf691468b94557
Context: I am updating jit::FunctionSchema to use
Symbol name;rather thanstd::string name. Sometimes the name refers to a builtin thing likeprim::UnpackTuple, sometimes to an aten operator likeaten::add, and sometimes just to a raw string, likemy_method_foothat really doesn't belong in any namespace and should be printed to the user in that form. For this last case, I want the ability to create a raw Symbol again, like was previously possible, that just represents an interned string. This PR enables that use, keeps the other functionality still possible, and simplifies interned_string's implementation a bit.This changes how Symbol is implemented. Now the namespace of a symbol
is optional and the namespaces themselves are Symbols.
This allows Symbol to be used with arbitrary namespaces, and allows
you to use Symbol as an simple interned string using via fromQualString
and toQualString without :: in the string. This also simplifies the
implementation. Like with string conversion, builtin primitives go
through a fast path for namespace lookup while registered symbols require
holding a lock and reading an array entry to lookup the namespace.
Note: alexnet expect file update is from a previous commit. It doesn't run in CI because pytorch vision is not installed.