GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — gh-131798: JIT: Further optimize `_CALL_ISINSTANCE` for class tuples by tomasr8 · Pull Request #134543 · python/cpython Conversation
|
| self.assertIn("_BUILD_TUPLE", uops) | ||
| self.assertIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops) |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
_BUILD_TUPLE is preventing us from optimizing out _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW.
The bytecode is basically:
LOAD_CONST LOAD_CONST _BUILD_TUPLE _POP_CALL_TWO_LOAD_CONST_INLINE_BORROW
To optimize this, we'd need some special handling for _BUILD_TUPLE in remove_unneeded_uops.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Got it, might be worth looking into next if you're up for it! Could be tricky, though.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
When you're done making the requested changes, leave the comment: |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
So that was a lie 😆 Anyway, I think I addressed all your comments! :) I have made the requested changes; please review again |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Thanks for making the requested changes! @brandtbucher: please review the changes made to this pull request. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Looks good, just a few suggestions:
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| self.assertIn("_BUILD_TUPLE", uops) | ||
| self.assertIn("_POP_CALL_TWO_LOAD_CONST_INLINE_BORROW", uops) |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Got it, might be worth looking into next if you're up for it! Could be tricky, though.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| for _ in range(n): | ||
| # One of the classes is unknown, so we can't narrow | ||
| # to True or False, only bool | ||
| y = isinstance(42, (eval('str'), int)) |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I'm confused, why can't we narrow to True? We can't remove the call, but the result is known.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Yep, that was a brainfart 😄 I somehow conflated narrowing to True/False with replacing the op, but we can obviously just narrow without removing the call as you said!
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| def testfunc(n): | ||
| x = 0 | ||
| for _ in range(n): | ||
| y = isinstance(42, ()) |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I had to pause and think for a second to figure out what this would even do. Nice edge case.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| // It could potentially define its own __instancecheck__ | ||
| // method so we can only deduce bool. | ||
| out = NULL; | ||
| break; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Could this be continue? We could still narrow to True later to handle cases like the one I called out above, right?
| break; | |
| continue; |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Yup, see #134543 (comment). I just needed to add some extra bookkeeping to know when we can replace the call and when not.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| } | ||
| PyTypeObject *cls_o = (PyTypeObject *)sym_get_const(ctx, item); | ||
| if (cls_o && | ||
| sym_matches_type(item, &PyType_Type) && |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a comment explaining that this is to protect against metaclasses definine __instancecheck__.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
How would you formulate it? I don't think of it as specifically a guard for __instancecheck__ but basically PyObject_TypeCheck adapted to the JIT optimizer.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
We're not only checking that the object is a subclass of type, we're also checking that it is an exact instance of type itself. We care about this second condition because it guarantees that __instancecheck__ doesn't exist (otherwise we would need to look it up to check if it exists or not).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the clarification, I added a comment :)
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Off-topic, but something I've been thinking about that you may want to check out next: removing bounds checks when indexing into strings and tuples with a constant index. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Thanks for the review! Super helpful as always :) I'll leave this open for a few days so @Fidget-Spinner can also have a look :)
Sure, I can look into that! I guess this will also require knowing the string/tuple length? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| if (cls_o && | ||
| // Ensure that item is an exact instance of `type` ensuring that | ||
| // there is no __instancecheck__ defined. | ||
| sym_matches_type(item, &PyType_Type) && |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Wait, wouldn't this only work on something that is isinstance(x, type), like the actual type itself, not any type like int? Sorry, I'm confused!
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Hmm I don't believe so. If item is e.g. int then sym_matches_type(item, &PyType_Type) should return true
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
For example something like this seems to work:
JitOptRef ref_type = _Py_uop_sym_new_const(ctx, (PyObject *)&PyLong_Type); TEST_PREDICATE(_Py_uop_sym_matches_type(ref_type, &PyType_Type), "int is not a type");
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
That''s kinda strange. The code for _Py_uop_sym_matches_type is:
_Py_uop_sym_get_type(sym) == typ
so it's a pointer comparison, not a subclass check.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The subclass check is done after with PyType_IsSubtype if that's what you mean? The current version of _CALL_ISINSTANCE (which does not handle tuples) works the same way though. @brandtbucher, what do you think?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I'm really sorry Tomas, but the more I look at the code, the more I feel the complexity is not worth it in this case. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
We need a test for a type with __instancecheck__, otherwise it LGTM, and you can merge after the test is added
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
This PR is stale because it has been open for 30 days with no activity. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.