8000 gh-127119: Faster check for small ints in long_dealloc (GH-127620) · python/cpython@a292216 · GitHub
[go: up one dir, main page]

Skip to content
Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit a292216

Browse files
authored
gh-127119: Faster check for small ints in long_dealloc (GH-127620)
1 parent 3a974e3 commit a292216

File tree

6 files changed

+48
-45
lines changed

6 files changed

+48
-45
lines changed

Include/cpython/longintrepr.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ typedef long stwodigits; /* signed variant of twodigits */
7676
- 1: Zero
7777
- 2: Negative
7878
79-
The third lowest bit of lv_tag is reserved for an immortality flag, but is
80-
not currently used.
79+
The third lowest bit of lv_tag is
80+
set to 1 for the small ints.
8181
8282
In a normalized number, ob_digit[ndigits-1] (the most significant
8383
digit) is never zero. Also, in all cases, for all valid i,

Include/internal/pycore_long.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,14 @@ PyAPI_FUNC(int) _PyLong_Size_t_Converter(PyObject *, void *);
159159

160160
/* Long value tag bits:
161161
* 0-1: Sign bits value = (1-sign), ie. negative=2, positive=0, zero=1.
162-
* 2: Reserved for immortality bit
162+
* 2: Set to 1 for the small ints
163163
* 3+ Unsigned digit count
164164
*/
165165
#define SIGN_MASK 3
166166
#define SIGN_ZERO 1
167167
#define SIGN_NEGATIVE 2
168168
#define NON_SIZE_BITS 3
169+
#define IMMORTALITY_BIT_MASK (1 << 2)
169170

170171
/* The functions _PyLong_IsCompact and _PyLong_CompactValue are defined
171172
* in Include/cpython/longobject.h, since they need to be inline.
@@ -196,7 +197,7 @@ PyAPI_FUNC(int) _PyLong_Size_t_Converter(PyObject *, void *);
196197
static inline int
197198
_PyLong_IsNonNegativeCompact(const PyLongObject* op) {
198199
assert(PyLong_Check(op));
199-
return op->long_value.lv_tag <= (1 << NON_SIZE_BITS);
200+
return ((op->long_value.lv_tag & ~IMMORTALITY_BIT_MASK) <= (1 << NON_SIZE_BITS));
200201
}
201202

202203

@@ -298,7 +299,7 @@ _PyLong_FlipSign(PyLongObject *op) {
298299
.long_value = { \
299300
.lv_tag = TAG_FROM_SIGN_AND_SIZE( \
300301
(val) == 0 ? 0 : ((val) < 0 ? -1 : 1), \
301-
(val) == 0 ? 0 : 1), \
302+
(val) == 0 ? 0 : 1) | IMMORTALITY_BIT_MASK, \
302303
{ ((val) >= 0 ? (val) : -(val)) }, \
303304
} \
304305
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Slightly optimize the :class:`int` deallocator.

Modules/_testcapi/immortal.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#include "parts.h"
22

3+
#define Py_BUILD_CORE
4+
#include "internal/pycore_long.h" // IMMORTALITY_BIT_MASK
5+
36
int verify_immortality(PyObject *object)
47
{
58
assert(_Py_IsImmortal(object));
@@ -26,7 +29,17 @@ static PyObject *
2629
test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored))
2730
{
2831
for (int i = -5; i <= 256; i++) {
29-
assert(verify_immortality(PyLong_FromLong(i)));
32+
PyObject *obj = PyLong_FromLong(i);
33+
assert(verify_immortality(obj));
34+
int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK;
35+
assert(has_int_immortal_bit);
36+
}
37+
for (int i = 257; i <= 260; i++) {
38+
PyObject *obj = PyLong_FromLong(i);
39+
assert(obj);
40+
int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK;
41+
assert(!has_int_immortal_bit);
42+
Py_DECREF(obj);
3043
}
3144
Py_RETURN_NONE;
3245
}

Objects/longobject.c

Lines changed: 25 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3651,32 +3651,25 @@ long_richcompare(PyObject *self, PyObject *other, int op)
36513651
}
36523652

36533653
static inline int
3654-
compact_int_is_small(PyObject *self)
3654+
/// Return 1 if the object is one of the immortal small ints
3655+
_long_is_small_int(PyObject *op)
36553656
{
3656-
PyLongObject *pylong = (PyLongObject *)self;
3657-
assert(_PyLong_IsCompact(pylong));
3658-
stwodigits ival = medium_value(pylong);
3659-
if (IS_SMALL_INT(ival)) {
3660-
PyLongObject *small_pylong = (PyLongObject *)get_small_int((sdigit)ival);
3661-
if (pylong == small_pylong) {
3662-
return 1;
3663-
}
3664-
}
3665-
return 0;
3657+
PyLongObject *long_object = (PyLongObject *)op;
3658+
int is_small_int = (long_object->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0;
3659+
assert((!is_small_int) || PyLong_CheckExact(op));
3660+
return is_small_int;
36663661
}
36673662

36683663
void
36693664
_PyLong_ExactDealloc(PyObject *self)
36703665
{
36713666
assert(PyLong_CheckExact(self));
3667+
if (_long_is_small_int(self)) {
3668+
// See PEP 683, section Accidental De-Immortalizing for details
3669+
_Py_SetImmortal(self);
3670+
return;
3671+
}
36723672
if (_PyLong_IsCompact((PyLongObject *)self)) {
3673-
#ifndef Py_GIL_DISABLED
3674-
if (compact_int_is_small(self)) {
3675-
// See PEP 683, section Accidental De-Immortalizing for details
3676-
_Py_SetImmortal(self);
3677-
return;
3678-
}
3679-
#endif
36803673
_Py_FREELIST_FREE(ints, self, PyObject_Free);
36813674
return;
36823675
}
@@ -3686,24 +3679,20 @@ _PyLong_ExactDealloc(PyObject *self)
36863679
static void
36873680
long_dealloc(PyObject *self)
36883681
{
3689-
assert(self);
3690-
if (_PyLong_IsCompact((PyLongObject *)self)) {
3691-
if (compact_int_is_small(self)) {
3692-
/* This should never get called, but we also don't want to SEGV if
3693-
* we accidentally decref small Ints out of existence. Instead,
3694-
* since small Ints are immortal, re-set the reference count.
3695-
*
3696-
* See PEP 683, section Accidental De-Immortalizing for details
3697-
*/
3698-
_Py_SetImmortal(self);
3699-
return;
3700-
}
3701-
if (PyLong_CheckExact(self)) {
3702-
_Py_FREELIST_FREE(ints, self, PyObject_Free);
3703-
return;
3704-
}
3682+
if (_long_is_small_int(self)) {
3683+
/* This should never get called, but we also don't want to SEGV if
3684+
* we accidentally decref small Ints out of existence. Instead,
3685+
* since small Ints are immortal, re-set the reference count.
3686+
*
3687+
* See PEP 683, section Accidental De-Immortalizing for details
3688+
*/
3689+
_Py_SetImmortal(self);
3690+
return;
3691+
}
3692+
if (PyLong_CheckExact(self) && _PyLong_IsCompact((PyLongObject *)self)) {
3693+
_Py_FREELIST_FREE(ints, self, PyObject_Free);
3694+
return;
37053695
}
3706-
37073696
Py_TYPE(self)->tp_free(self);
37083697
}
37093698

@@ -6065,7 +6054,7 @@ long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase)
60656054
return NULL;
60666055
}
60676056
assert(PyLong_Check(newobj));
6068-
newobj->long_value.lv_tag = tmp->long_value.lv_tag;
6057+
newobj->long_value.lv_tag = tmp->long_value.lv_tag & ~IMMORTALITY_BIT_MASK;
60696058
for (i = 0; i < n; i++) {
60706059
newobj->long_value.ob_digit[i] = tmp->long_value.ob_digit[i];
60716060
}

Tools/gdb/libpython.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,7 @@ class PyLongObjectPtr(PyObjectPtr):
890890

891891
def proxyval(self, visited):
892892
'''
893-
Python's Include/longinterpr.h has this declaration:
893+
Python's Include/cpython/longinterpr.h has this declaration:
894894
895895
typedef struct _PyLongValue {
896896
uintptr_t lv_tag; /* Number of digits, sign and flags */
@@ -909,8 +909,7 @@ def proxyval(self, visited):
909909
- 0: Positive
910910
- 1: Zero
911911
- 2: Negative
912-
The third lowest bit of lv_tag is reserved for an immortality flag, but is
913-
not currently used.
912+
The third lowest bit of lv_tag is set to 1 for the small ints and 0 otherwise.
914913
915914
where SHIFT can be either:
916915
#define PyLong_SHIFT 30

0 commit comments

Comments
 (0)
0