Repository navigation
The DISPATCH() macro is not as efficient as it could be (move PyThreadState.use_tracing) #87926
Copy link
Copy link
Closed
Labels
3.10 (EOL)end of lifeend of lifeinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)pendingThe issue will be closed if no feedback is providedThe issue will be closed if no feedback is providedperformancePerformance or resource usagePerformance or resource usagetopic-C-API
Description
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee = None
closed_at = None
created_at = <Date 2021-04-07.09:35:49.770>
labels = ['interpreter-core', 'expert-C-API', '3.10', 'performance']
title = 'The DISPATCH() macro is not as efficient as it could be (move PyThreadState.use_tracing)'
updated_at = <Date 2021-11-08.17:16:37.213>
user = 'https://github.com/markshannon'bugs.python.org fields:
activity = <Date 2021-11-08.17:16:37.213>
actor = 'vstinner'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Interpreter Core', 'C API']
creation = <Date 2021-04-07.09:35:49.770>
creator = 'Mark.Shannon'
dependencies = []
files = []
hgrepos = []
issue_num = 43760
keywords = ['patch']
message_count = 46.0
messages = ['390410', '390522', '390951', '393379', '393384', '393385', '393389', '393390', '393401', '393403', '393404', '393407', '393410', '393425', '393459', '393466', '393666', '402150', '402162', '402216', '402225', '402231', '402234', '402235', '402358', '402360', '402362', '402415', '402416', '402426', '402432', '402434', '402481', '402496', '402525', '402594', '402603', '403166', '403217', '404020', '404024', '404025', '404173', '404197', '404600', '405968']
nosy_count = 13.0
nosy_names = ['gvanrossum', 'rhettinger', 'jpe', 'scoder', 'vstinner', 'petr.viktorin', 'lukasz.langa', 'Mark.Shannon', 'hroncok', 'pablogsal', 'miss-islington', 'erlendaasland', 'Xtrem532']
pr_nums = ['25244', '25276', '28474', '28498', '28527', '28529', '28542', '28723', '29032']
priority = None
resolution = None
stage = 'patch review'
status = 'open'
superseder = None
type = 'performance'
url = 'https://bugs.python.org/issue43760'
versions = ['Python 3.10']Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
3.10 (EOL)end of lifeend of lifeinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)pendingThe issue will be closed if no feedback is providedThe issue will be closed if no feedback is providedperformancePerformance or resource usagePerformance or resource usagetopic-C-API
Activity
markshannon commentedon Apr 7, 2021
The DISPATCH() macro has two failings.
Its check for tracing involves too much pointer chaser.
The logic assumes that computed-gotos is the "fast path" which makes switch dispatch, and therefore Python on Windows unnecessarily slow.
markshannon commentedon Apr 8, 2021
New changeset 28d28e0 by Mark Shannon in branch 'master':
bpo-43760: Streamline dispatch sequence for machines without computed gotos. (GH-25244)
28d28e0
markshannon commentedon Apr 13, 2021
New changeset 9e7b207 by Mark Shannon in branch 'master':
bpo-43760: Speed up check for tracing in interpreter dispatch (bpo-25276)
9e7b207
hroncok commentedon May 10, 2021
I am afraid the "Speed up check for tracing in interpreter dispatch" brought some backwards incompatible changes:
yappi/_yappi.c:1261:9: error: ‘PyThreadState’ {aka ‘struct _ts’} has no member named ‘use_tracing’; did you mean ‘tracing’?
1261 | ts->use_tracing = 1;
| ^~~~~~~~~~~
| tracing
This is not mentioned in https://docs.python.org/3.10/whatsnew/3.10.html and I haven't noticed the use_tracing member being deprecated. I am confused. Should this happened?
hroncok commentedon May 10, 2021
Fedora packages affected (that we know of now):
greenlet: https://bugzilla.redhat.com/show_bug.cgi?id=1957784
dipy: https://bugzilla.redhat.com/show_bug.cgi?id=1958203
yappi: https://bugzilla.redhat.com/show_bug.cgi?id=1958896
smartcols: https://bugzilla.redhat.com/show_bug.cgi?id=1958938
markshannon commentedon May 10, 2021
At yappi/_yappi.c:1261 sets an undocumented field on a CPython internal data structure.
What did you believe that was supposed to do? use_tracing is not documented anywhere.
We could add the field back and ignore it, but I doubt that would help you much.
markshannon commentedon May 10, 2021
If there is no C-API function that supports your needs, feel free to suggest one.
hroncok commentedon May 10, 2021
Disclaimer: I have not written the code nor do I understand what is trying to achieve. I merely collect the data and report the problems to the package maintainers.
It just seems to me that a non-underscored (and hence public) member variable on a non-underscored (and hence public) structure should not suddenly go missing. Although, I am not familiar with the rules that define what part of the API falls under https://www.python.org/dev/peps/pep-0497/
hroncok commentedon May 10, 2021
scikit-learn: https://bugzilla.redhat.com/show_bug.cgi?id=1958976
gcc: sklearn/cluster/_k_means_fast.c
In file included from /usr/lib64/python3.10/site-packages/numpy/core/include/numpy/ndarraytypes.h:1944,
from /usr/lib64/python3.10/site-packages/numpy/core/include/numpy/ndarrayobject.h:12,
from /usr/lib64/python3.10/site-packages/numpy/core/include/numpy/arrayobject.h:4,
from sklearn/cluster/_k_means_fast.c:635:
/usr/lib64/python3.10/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:17:2: warning: #warning "Using deprecated NumPy API, disable it with " "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-Wcpp]
17 | #warning "Using deprecated NumPy API, disable it with " \
| ^~~~~~~
sklearn/cluster/_k_means_fast.c: In function ‘__Pyx_call_return_trace_func’:
sklearn/cluster/_k_means_fast.c:1596:15: error: ‘PyThreadState’ {aka ‘struct _ts’} has no member named ‘use_tracing’; did you mean ‘tracing’?
1596 | tstate->use_tracing = 0;
| ^~~~~~~~~~~
| tracing
sklearn/cluster/_k_means_fast.c:1602:15: error: ‘PyThreadState’ {aka ‘struct _ts’} has no member named ‘use_tracing’; did you mean ‘tracing’?
1602 | tstate->use_tracing = 1;
| ^~~~~~~~~~~
| tracing
The usage comes from https://github.com/cython/cython/blob/master/Cython/Utility/Profile.c
encukou commentedon May 10, 2021
PEP-0497 is rejected; the active one is PEP-387, which says "backwards incompatibility" means preexisting code ceases to comparatively function after a change.
So, this does look like a backwards-incompatible change.
Unfortunately, not all of the C API is documented, so unless it's explicitly marked private, people will use it :(
markshannon commentedon May 10, 2021
But what does "use it" mean?
What does setting
tstate->use_tracing = 1do?There is no documented behavior, so how do we know what assumptions people are making about what happens when they set some field to 1?
As I said, we could keep the field and ignore it, but that seems worse.
rhettinger commentedon May 10, 2021
I don't think the PEP meant to restrict individual struct member such as this. For example, we were able to switch from byte code to word code without violating the intended rules. Consider asking Brett and Benjamin for clarification. I would think that if a new function were introduced to provide a reliable way to determine whether tracing was enabled, that would suffice for external packages to have a minimally disruptive migration path.
vstinner commentedon May 10, 2021
I understand that some projects manually call the profile and/or trace functions, and temporarily set use_tracing 0 while calling these functions.
Some projects restore use_tracing to the correct value (compute the efficient value), some projects simply set use_tracing to 1.
I see 3 use cases:
We can add 3 functions:
PyThreadState_EnableTracing(tstate) would do something like:
If we added these functions, I can then add an implementation for Python 3.9 and older to my https://github.com/pythoncapi/pythoncapi_compat project for backward compatibility.
The problem is that some projects also increase temporarily ts->tracing. Since I would like to make PyThreadState opaque, I would prefer to hide this access behind a function call as well. Maybe we need an API to call profile and/or trace functions?
--
According to the bugzilla compiler errors:
It has already been fixed:
It uses:
It uses:
It uses "tstate->use_tracing = 0;".
It uses:
Simplified code:
--------------
}
[-]The DISPATCH() macro is not as efficient as it could be.[/-][+]The DISPATCH() macro is not as efficient as it could be (move PyThreadState.use_tracing)[/+]30 remaining items
gvanrossum commentedon Sep 25, 2021
Ah, I think the docs need to be clarified a bit. Here's what I was missing:
The key thing to know here is that there are three state variables;
c_tracefunc,c_profilefunc(on the thread state), anduse_tracing(on the C frame).Normally
use_tracingis initialized to false if both functions are NULL, and true otherwise (if at least one of the functions is set).Disabling means setting
use_tracingto false regardless. Resetting means settinguse_tracingto the value computed above.There's also a fourth variable,
tstate->tracing, which indicates whether a tracing function is active (i.e., it has been called and hasn't exited yet). This can be incremented and decremented. But none of the proposed APIs affect it.Would it be reasonable to just put these APIs in pythoncapi_compat, instead of in the stdlib? (It would be yet one more selling point for people to start using that. :-)
pablogsal commentedon Oct 4, 2021
New changeset 78184fa by Pablo Galindo (Miss Islington (bot)) in branch '3.10':
bpo-43760: Document PyThreadState.use_tracing removal (GH-28527) (GH-28529)
78184fa
markshannon commentedon Oct 5, 2021
New changeset bd627eb by Mark Shannon in branch 'main':
bpo-43760: Check for tracing using 'bitwise or' instead of branch in dispatch. (GH-28723)
bd627eb
vstinner commentedon Oct 15, 2021
New changeset 547d26a by Victor Stinner in branch 'main':
bpo-43760: Add PyThreadState_EnterTracing() (GH-28542)
547d26a
vstinner commentedon Oct 15, 2021
I created changes to use it:
vstinner commentedon Oct 15, 2021
PyThreadState.cframe.use_tracing format changed again: set value set to 0 or 255.
bd627eb
vstinner commentedon Oct 18, 2021
Merged:
vstinner commentedon Oct 18, 2021
New changeset 034f607 by Victor Stinner in branch 'main':
bpo-43760: Rename _PyThreadState_DisableTracing() (GH-29032)
034f607
vstinner commentedon Oct 21, 2021
I created #29121 to add PyThreadState_SetProfile() and PyThreadState_SetTrace() functions.
vstinner commentedon Nov 8, 2021
greenlet now uses PyThreadState_EnterTracing() and PyThreadState_LeaveTracing() rather than accessing directly use_tracing:
python-greenlet/greenlet@9b49da5
On Python 3.10, it implements these functions with:
---
This code was copied from my https://github.com/pythoncapi/pythoncapi_compat project. (I wrote the greenlet change.)
iritkatriel commentedon Sep 13, 2022
Is there anything left to do here?
vstinner commentedon Sep 15, 2022
IMO we are done, and I close the issue. See the issue #84128 for making PyThreadState opaque.
This part was fixed early. Following comments were more about PyThreadState incompatible changes, how to migrate existing C extensions to Python 3.11 and how to design a new API which no longer access directly PyThreadState changes. In fact, that's already the topic of the issue #84128 that I created in 2020 and so we can continue the discussion there.
The main change related to PyThreadState was the use_tracing member which was moved. I added PyThreadState_EnterTracing() and PyThreadState_LeaveTracing() functions to Python 3.11 for that and so projects already use it.
I abandoned this PR. @pablogsal added PyEval_SetProfileAllThreads() and PyEval_SetTraceAllThreads() functions to Python 3.12 (commit e34c82a) which should fit the use case, with a different design.