-
-
Notifications
You must be signed in to change notification settings - Fork 32k
bpo-38858: Small integer per interpreter #17315
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
Conversation
Example:
The subinterpreter and the main interpreter don't have the same small integer singletons for numbers 1 and 2. |
That's a temporary solution which I consider as an acceptable compromise. Small integer singletons are not really part of the Python semantics. It's more the opposite, we advice to not use "is" operator to compare numbers. That's why Python starts to emit such SyntaxWarning:
|
I rebased my PR to fix a confict. |
Should I run a benchmark? If yes, which kind of benchmark? |
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.
mostly LGTM
Also, would it make sense to add any tests for this? I'm not sure it would add much.
#ifndef NSMALLNEGINTS | ||
#define NSMALLNEGINTS 5 | ||
#endif | ||
#define NSMALLPOSINTS _PY_NSMALLPOSINTS |
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.
This is effectively a backward-incompatible change (though it only affects folks building their own python binary). So there should be a note in the whatsnew doc (porting section) indicating what folks must do if they want the previous behavior.
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.
Do you really expect that anyone would recompile their Python with different NSMALLPOSINTS and NSMALLNEGINTS constants? Disabling these singletons is likely to make Python faster. I'm not sure about increasing the number of singletons. I don't expect any significant performance difference.
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.
I completed the NEWS entry. I prefer to not document it in the What's New in Python 3.9 document. IMHO it's too low-level and obscure.
@@ -53,7 +43,8 @@ static PyObject * | |||
get_small_int(sdigit ival) | |||
{ | |||
assert(IS_SMALL_INT(ival)); | |||
PyObject *v = (PyObject*)small_ints[ival + NSMALLNEGINTS]; | |||
PyThreadState *tstate = _PyThreadState_GET(); |
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.
I'm guessing this was simpler than adding a tstate arg to get_small_int()
...
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.
I began by passing tstate to get_small_int() but I don't see any benefit, since no caller requires tstate, or already have tstate, currently. For now, it seems simpler to only get tstate inside get_small_int(). I don't expect any performance issue for now.
@@ -53,7 +43,8 @@ static PyObject * | |||
get_small_int(sdigit ival) | |||
{ | |||
assert(IS_SMALL_INT(ival)); | |||
PyObject *v = (PyObject*)small_ints[ival + NSMALLNEGINTS]; | |||
PyThreadState *tstate = _PyThreadState_GET(); | |||
PyObject *v = (PyObject*)tstate->interp->small_ints[ival + NSMALLNEGINTS]; |
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.
This isn't a static value any more, so is it possible to run into problems during interpreter/runtime finalization?
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.
I'm not sure of what you mean. A subinterpreter is supposed to not leak any object to other interpreters, right? If an subinterpreter object survives after the subinterpreter is destroyed, it's a bug, no?
if (_PyLong_One == NULL) { | ||
return 0; | ||
} | ||
if (_Py_IsMainInterpreter(tstate)) { |
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.
Yuck!
It would be great to have a comment here about how we would like to get rid of this special case. Bonus points if you open an issue for that and link to it in the comment. :)
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.
I have no plan to remove _PyLong_One and _PyLong_Zero yet. It seems like there are too many things to do for subinterpreters, I am not excited by long TODO lists. They depress me, as if I will never be able to go to the end. I like to make tiny incremental changes :-)
In general, searching for "_Py_IsMainInterpreter" became a nice hint for "subinterpreters TODO".
When you're done making the requested changes, leave the comment: |
benchmark? Certainly the normal suite. On top of that, it might be nice to see a "worst-case" benchmark: code that heavily exercises the code paths that have calls to |
Here you have. In short, I see no performance regression (maybe speedups, but I'm not sure about these ones :-p). pyperformance benchmark results, ignoring differences smaller than 1%:
pyperformance benchmark results, ignoring differences smaller than 1%:
|
Each Python subinterpreter now has its own "small integer singletons": numbers in [-5; 257] range. It is no longer possible to change the number of small integers at build time by overriding NSMALLNEGINTS and NSMALLPOSINTS macros: macros should now be modified manually in pycore_pystate.h header file. For now, continue to share _PyLong_Zero and _PyLong_One singletons between all subinterpreters.
I would prefer until we get ride of _PyLong_Zero and _PyLong_One before going up to unit tests. Right now, you may get the subinterpreter singletons, or the main interpreter singletons depending on which function is called... This PR is a small step towards more isolated subinterpreters. It doesn't solve all issues at once ;-) |
As I wrote, I know that this change is not complete nor perfect. It's a small step towards better isolated subinterpreters. I chose to not document the backward incompatible change in What's New in Python 3.9, but only in the Changelog. IMHO it's enough, since it's really an obscure low-level feature (number of small integer singletons). |
Thanks for the review @ericsnowcurrently. I hope that I addressed most of your remarks ;-) |
Each Python subinterpreter now has its own "small integer singletons": numbers in [-5; 257] range. It is no longer possible to change the number of small integers at build time by overriding NSMALLNEGINTS and NSMALLPOSINTS macros: macros should now be modified manually in pycore_pystate.h header file. For now, continue to share _PyLong_Zero and _PyLong_One singletons between all subinterpreters.
Each Python subinterpreter now has its own "small integer
singletons": numbers in [-5; 257] range.
For now, continue to share _PyLong_Zero and _PyLong_One singletons
between all subinterpreters.
It is no longer possible to change the number of small integers at
build time by overriden macros: pycore_pystate.h macros should now be
modified manually.
https://bugs.python.org/issue38858