Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34k
gh-117139: Set up the tagged evaluation stack#117186
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
gh-117139: Set up the tagged evaluation stack #117186
Uh oh!
There was an error while loading. Please reload this page.
Conversation
Fidget-Spinner commented Mar 23, 2024 • edited by bedevere-app bot
Loading Uh oh!
There was an error while loading. Please reload this page.
edited by bedevere-app bot
Uh oh!
There was an error while loading. Please reload this page.
colesbury commented Mar 26, 2024 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
I'm a confused by your description of approach 2:
That does not look like the approach in the PR, which adds new functions that take tagged values. The approach in the PR seems correct. We can change the signatures of internal-only APIs as needed.
If you untag in-place on the stack, you need to I think it's better to do the untagging off the evaluation stack. In the common case, you can have some fixed size |
Include/object.h Outdated
| typedefunion{ | ||
| PyObject*obj; | ||
| uintptr_tbits; | ||
| } _Py_TaggedObject; | ||
| #definePy_OBJECT_TAG (0b0) | ||
| #ifdefPy_GIL_DISABLED | ||
| #definePy_CLEAR_TAG(tagged) ((PyObject *)((tagged).bits & ~(Py_OBJECT_TAG))) | ||
| #else | ||
| #definePy_CLEAR_TAG(tagged) ((PyObject *)(uintptr_t)((tagged).bits)) | ||
| #endif | ||
| #definePy_OBJ_PACK(obj) ((_Py_TaggedObject){.bits = (uintptr_t)(obj)}) | ||
| #definePy_TAG_CAST(o) ((_Py_TaggedObject){.obj = (o)}) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should not make any of these macros or data structures part of the public C API.
Fidget-Spinner commented Mar 26, 2024
I did a mix of both -- for internal functions I converted them to tagged form. For stuff that might be used by other people eg.
Ok I will think about that. That does make sense -- to pass to a scratch buffer for now. If we exceed the buffer I assume we need to untag the C stack then. One thing we might want to be wary of is overflowing the C recursion stack with the current recursion limit. Since this will make |
Fidget-Spinner commented Mar 26, 2024 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
Also to note: every |
colesbury commented Mar 26, 2024
That's undefined behavior in C -- let's avoid it if possible. If we're concerned about external users, add a new function that takes tagged references (and keep the old one unused, if desired).
If we exceed the fixed size buffer we should allocate enough space for a temporary buffer using
I would structure this so that the small, temporary buffer is only used when we perform a vectorcall (and not to a Python function). For example, in pseudo-code: #defineN 5 PyObject*PyObject_VectorcallTagged(PyObject*callable, const_Py_TaggedObject*tagged, size_tnargs, PyObject*kwnames){PyObject*args[N]; if (nargs >= N){returnPyObject_VectorcallTaggedSlow(callable, tagged, nargs, kwnames)} untag(args, tagged, nargs); returnPyObject_Vectorcall(callable, args, nargs, kwnames)} PyObject*PyObject_VectorcallTaggedSlow(PyObject*callable, const_Py_TaggedObject*tagged, size_tnargs, PyObject*kwnames){PyObject*args=PyMem_Malloc(nargs*sizeof(PyObject*)); if (args==NULL) ... untag(args, tagged, nargs); PyObject*res=PyObject_Vectorcall(callable, args, nargs, kwnames); PyMem_Free(args); returnres} |
Fidget-Spinner commented Mar 26, 2024
Thanks for the clear explanation! I will address them tomorrow. |
gvanrossum commented Mar 27, 2024
Hm, I now recall that the problem of untagging arrays of arguments sunk my attempt (I got it to work, but it was too slow). Hope you fare better! |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
nineteendo commented Apr 25, 2024
Could you also configure pre-commit? https://devguide.python.org/getting-started/setup-building/#install-pre-commit index 2bd9c40..3aa7dea 100644 --- a/Tools/cases_generator/analyzer.py+++ b/Tools/cases_generator/analyzer.py@@ -359,7 +359,7 @@ def has_error_without_pop(op: parser.InstDef) -> bool: "Py_XDECREF_STACKREF", "Py_INCREF_STACKREF", "Py_XINCREF_TAGGED", - "Py_NewRef_StackRef", + "Py_NewRef_StackRef", "Py_INCREF", "_PyManagedDictPointer_IsValues", "_PyObject_GetManagedDict", |
Fidget-Spinner commented Apr 26, 2024
!buildbot nogil |
bedevere-bot commented Apr 26, 2024
🤖 New build scheduled with the buildbot fleet by @Fidget-Spinner for commit d59145b 🤖 The command will test the builders whose names match following regular expression: The builders matched are:
|
Fidget-Spinner commented Apr 26, 2024 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
Here are results from Sam's microbenchmark suite testing scalability. Note that I've only implemented deferred refcounting for method and function calls. Notice that All tests pass except ASAN. I am now happy with the state this PR is in. So I will close this and start upstreaming things in peices. |
Fidget-Spinner commented Apr 26, 2024 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
Benchmarks for the default build suggest a 1% speedup https://github.com/faster-cpython/benchmarking-public/blob/main/results/bm-20240426-3.13.0a6%2B-d59145b/bm-20240426-linux-x86_64-Fidget%252dSpinner-tagged_evaluation_st-3.13.0a6%2B-d59145b-vs-base.md |
This PR is up mostly just for discussion. It compiles (with warnings) and passes (Ubuntu) tests as of now.
The main point of contention is how do we deal with bytecode that expect arrays of untagged objects? E.g. vectorcall takes an array from the stack of objects. There are two general approaches I can think of:
For example I currently have a
_PyList_FromArrayStealand a_PyList_FromTaggedArraySteal. With the 2nd approach, I just need some bitwise operations in_PyList_FromArrayStealand it should be good to go. Then I can remove_PyList_FromTaggedArraySteal. What do y'all think?However, we still have to untag everything if we call escaping functions that call 3rd party code (e.g. vectorcall). This is only safe right now for CPython's own C API.