-
-
Notifications
You must be signed in to change notification settings - Fork 30.5k
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-126868: Add freelist for compact int objects #126865
base: main
Are you sure you want to change the base?
Changes from 13 commits
0713034
be58ade
3f50b54
fa97302
d72486f
e07c218
328e0c1
e1dc2b3
9df776b
6b73046
db8247e
d1e4aa2
644e85a
9f86b6e
88274d6
1e548bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Increase performance of int by adding a freelist for compact ints. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
#include "pycore_bitutils.h" // _Py_popcount32() | ||
#include "pycore_initconfig.h" // _PyStatus_OK() | ||
#include "pycore_call.h" // _PyObject_MakeTpCall | ||
#include "pycore_freelist.h" // _Py_FREELIST_FREE, _Py_FREELIST_POP | ||
#include "pycore_long.h" // _Py_SmallInts | ||
#include "pycore_object.h" // _PyObject_Init() | ||
#include "pycore_runtime.h" // _PY_NSMALLPOSINTS | ||
|
@@ -42,7 +43,7 @@ static inline void | |
_Py_DECREF_INT(PyLongObject *op) | ||
{ | ||
assert(PyLong_CheckExact(op)); | ||
_Py_DECREF_SPECIALIZED((PyObject *)op, (destructor)PyObject_Free); | ||
_Py_DECREF_SPECIALIZED((PyObject *)op, _PyLong_ExactDealloc); | ||
} | ||
|
||
static inline int | ||
|
@@ -220,11 +221,16 @@ _PyLong_FromMedium(sdigit x) | |
{ | ||
assert(!IS_SMALL_INT(x)); | ||
assert(is_medium_int(x)); | ||
/* We could use a freelist here */ | ||
PyLongObject *v = PyObject_Malloc(sizeof(PyLongObject)); | ||
|
||
// The small int cache is incompatible with _Py_NewReference which is called | ||
// by _Py_FREELIST_POP. | ||
PyLongObject *v = (PyLongObject *)_Py_FREELIST_POP_MEM(ints); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you go back to PyLongObject *v = (PyLongObject *)_Py_FREELIST_POP(ints);
if (v == NULL) {
v = PyObject_Malloc(sizeof(PyLongObject));
if (v == NULL) {
PyErr_NoMemory();
return NULL;
}
_PyObject_Init((PyObject*)v, &PyLong_Type);
}
digit abs_x = x < 0 ? -x : x;
_PyLong_SetSignAndDigitCount(v, x<0?-1:1, 1);
v->long_value.ob_digit[0] = abs_x;
return (PyObject*)v; |
||
if (v == NULL) { | ||
PyErr_NoMemory(); | ||
return NULL; | ||
v = PyObject_Malloc(sizeof(PyLongObject)); | ||
if (v == NULL) { | ||
PyErr_NoMemory(); | ||
return NULL; | ||
} | ||
} | ||
digit abs_x = x < 0 ? -x : x; | ||
_PyLong_SetSignAndDigitCount(v, x<0?-1:1, 1); | ||
|
@@ -3611,24 +3617,43 @@ long_richcompare(PyObject *self, PyObject *other, int op) | |
Py_RETURN_RICHCOMPARE(result, 0, op); | ||
} | ||
|
||
void | ||
_PyLong_ExactDealloc(PyObject *self) | ||
{ | ||
if (_PyLong_IsCompact((PyLongObject *)self)) { | ||
_Py_FREELIST_FREE(ints, self, PyObject_Free); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just realised that we didnt check for if it's a smallint cached value here. It should be rejected there There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nevermind. It's not needed because (theoretically) the refcount of a smallint never hits 0 due to it being immortal, so this code will never be called on it. Can you add an assert to be safe? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This also means my comment about the small ints was wrong. Could you remove that please? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The comment has been adapted. If the refcount can never become zero, then the code in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Fidget-Spinner I think your first remark was right after all and we do need to check for smallints (at least in the 32-bit stable ABI build). I will add the check once there is consensus on #127120 (which might very well be the status quo) |
||
return; | ||
} | ||
PyObject_Free(self); | ||
} | ||
|
||
static void | ||
long_dealloc(PyObject *self) | ||
{ | ||
/* This should never get called, but we also don't want to SEGV if | ||
* we accidentally decref small Ints out of existence. Instead, | ||
* since small Ints are immortal, re-set the reference count. | ||
*/ | ||
PyLongObject *pylong = (PyLongObject*)self; | ||
Fidget-Spinner marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (pylong && _PyLong_IsCompact(pylong)) { | ||
assert(pylong); | ||
|
||
if (_PyLong_IsCompact(pylong)) { | ||
stwodigits ival = medium_value(pylong); | ||
if (IS_SMALL_INT(ival)) { | ||
PyLongObject *small_pylong = (PyLongObject *)get_small_int((sdigit)ival); | ||
if (pylong == small_pylong) { | ||
/* This should never get called, but we also don't want to SEGV if | ||
* we accidentally decref small Ints out of existence. Instead, | ||
* since small Ints are immortal, re-set the reference count. | ||
* | ||
* With a fixed refcount for immortal objects this should not happen | ||
*/ | ||
_Py_SetImmortal(self); | ||
return; | ||
} | ||
} | ||
if (PyLong_CheckExact(self)) { | ||
_Py_FREELIST_FREE(ints, self, PyObject_Free); | ||
return; | ||
} | ||
} | ||
|
||
Py_TYPE(self)->tp_free(self); | ||
} | ||
|
||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
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 need it to be exported like that or could you live with a simple
extern
? If not, can you explain which file needs this export?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.
It's needed by the JIT on Windows, as it's used in bytecodes.c. This is a pretty common pattern in the internal C API unfortunately
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.
For semantic purposes, would it be better to have a macro named differently? (it would be a simple alias but it could help semantics and reviewers)