mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-127119: Faster check for small ints in long_dealloc (GH-127620)
This commit is contained in:
parent
3a974e39d5
commit
a29221675e
6 changed files with 48 additions and 45 deletions
|
@ -76,8 +76,8 @@ typedef long stwodigits; /* signed variant of twodigits */
|
||||||
- 1: Zero
|
- 1: Zero
|
||||||
- 2: Negative
|
- 2: Negative
|
||||||
|
|
||||||
The third lowest bit of lv_tag is reserved for an immortality flag, but is
|
The third lowest bit of lv_tag is
|
||||||
not currently used.
|
set to 1 for the small ints.
|
||||||
|
|
||||||
In a normalized number, ob_digit[ndigits-1] (the most significant
|
In a normalized number, ob_digit[ndigits-1] (the most significant
|
||||||
digit) is never zero. Also, in all cases, for all valid i,
|
digit) is never zero. Also, in all cases, for all valid i,
|
||||||
|
|
|
@ -159,13 +159,14 @@ PyAPI_FUNC(int) _PyLong_Size_t_Converter(PyObject *, void *);
|
||||||
|
|
||||||
/* Long value tag bits:
|
/* Long value tag bits:
|
||||||
* 0-1: Sign bits value = (1-sign), ie. negative=2, positive=0, zero=1.
|
* 0-1: Sign bits value = (1-sign), ie. negative=2, positive=0, zero=1.
|
||||||
* 2: Reserved for immortality bit
|
* 2: Set to 1 for the small ints
|
||||||
* 3+ Unsigned digit count
|
* 3+ Unsigned digit count
|
||||||
*/
|
*/
|
||||||
#define SIGN_MASK 3
|
#define SIGN_MASK 3
|
||||||
#define SIGN_ZERO 1
|
#define SIGN_ZERO 1
|
||||||
#define SIGN_NEGATIVE 2
|
#define SIGN_NEGATIVE 2
|
||||||
#define NON_SIZE_BITS 3
|
#define NON_SIZE_BITS 3
|
||||||
|
#define IMMORTALITY_BIT_MASK (1 << 2)
|
||||||
|
|
||||||
/* The functions _PyLong_IsCompact and _PyLong_CompactValue are defined
|
/* The functions _PyLong_IsCompact and _PyLong_CompactValue are defined
|
||||||
* in Include/cpython/longobject.h, since they need to be inline.
|
* in Include/cpython/longobject.h, since they need to be inline.
|
||||||
|
@ -196,7 +197,7 @@ PyAPI_FUNC(int) _PyLong_Size_t_Converter(PyObject *, void *);
|
||||||
static inline int
|
static inline int
|
||||||
_PyLong_IsNonNegativeCompact(const PyLongObject* op) {
|
_PyLong_IsNonNegativeCompact(const PyLongObject* op) {
|
||||||
assert(PyLong_Check(op));
|
assert(PyLong_Check(op));
|
||||||
return op->long_value.lv_tag <= (1 << NON_SIZE_BITS);
|
return ((op->long_value.lv_tag & ~IMMORTALITY_BIT_MASK) <= (1 << NON_SIZE_BITS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -298,7 +299,7 @@ _PyLong_FlipSign(PyLongObject *op) {
|
||||||
.long_value = { \
|
.long_value = { \
|
||||||
.lv_tag = TAG_FROM_SIGN_AND_SIZE( \
|
.lv_tag = TAG_FROM_SIGN_AND_SIZE( \
|
||||||
(val) == 0 ? 0 : ((val) < 0 ? -1 : 1), \
|
(val) == 0 ? 0 : ((val) < 0 ? -1 : 1), \
|
||||||
(val) == 0 ? 0 : 1), \
|
(val) == 0 ? 0 : 1) | IMMORTALITY_BIT_MASK, \
|
||||||
{ ((val) >= 0 ? (val) : -(val)) }, \
|
{ ((val) >= 0 ? (val) : -(val)) }, \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Slightly optimize the :class:`int` deallocator.
|
|
@ -1,5 +1,8 @@
|
||||||
#include "parts.h"
|
#include "parts.h"
|
||||||
|
|
||||||
|
#define Py_BUILD_CORE
|
||||||
|
#include "internal/pycore_long.h" // IMMORTALITY_BIT_MASK
|
||||||
|
|
||||||
int verify_immortality(PyObject *object)
|
int verify_immortality(PyObject *object)
|
||||||
{
|
{
|
||||||
assert(_Py_IsImmortal(object));
|
assert(_Py_IsImmortal(object));
|
||||||
|
@ -26,7 +29,17 @@ static PyObject *
|
||||||
test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored))
|
test_immortal_small_ints(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||||
{
|
{
|
||||||
for (int i = -5; i <= 256; i++) {
|
for (int i = -5; i <= 256; i++) {
|
||||||
assert(verify_immortality(PyLong_FromLong(i)));
|
PyObject *obj = PyLong_FromLong(i);
|
||||||
|
assert(verify_immortality(obj));
|
||||||
|
int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK;
|
||||||
|
assert(has_int_immortal_bit);
|
||||||
|
}
|
||||||
|
for (int i = 257; i <= 260; i++) {
|
||||||
|
PyObject *obj = PyLong_FromLong(i);
|
||||||
|
assert(obj);
|
||||||
|
int has_int_immortal_bit = ((PyLongObject *)obj)->long_value.lv_tag & IMMORTALITY_BIT_MASK;
|
||||||
|
assert(!has_int_immortal_bit);
|
||||||
|
Py_DECREF(obj);
|
||||||
}
|
}
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3651,32 +3651,25 @@ long_richcompare(PyObject *self, PyObject *other, int op)
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
compact_int_is_small(PyObject *self)
|
/// Return 1 if the object is one of the immortal small ints
|
||||||
|
_long_is_small_int(PyObject *op)
|
||||||
{
|
{
|
||||||
PyLongObject *pylong = (PyLongObject *)self;
|
PyLongObject *long_object = (PyLongObject *)op;
|
||||||
assert(_PyLong_IsCompact(pylong));
|
int is_small_int = (long_object->long_value.lv_tag & IMMORTALITY_BIT_MASK) != 0;
|
||||||
stwodigits ival = medium_value(pylong);
|
assert((!is_small_int) || PyLong_CheckExact(op));
|
||||||
if (IS_SMALL_INT(ival)) {
|
return is_small_int;
|
||||||
PyLongObject *small_pylong = (PyLongObject *)get_small_int((sdigit)ival);
|
|
||||||
if (pylong == small_pylong) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_PyLong_ExactDealloc(PyObject *self)
|
_PyLong_ExactDealloc(PyObject *self)
|
||||||
{
|
{
|
||||||
assert(PyLong_CheckExact(self));
|
assert(PyLong_CheckExact(self));
|
||||||
|
if (_long_is_small_int(self)) {
|
||||||
|
// See PEP 683, section Accidental De-Immortalizing for details
|
||||||
|
_Py_SetImmortal(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (_PyLong_IsCompact((PyLongObject *)self)) {
|
if (_PyLong_IsCompact((PyLongObject *)self)) {
|
||||||
#ifndef Py_GIL_DISABLED
|
|
||||||
if (compact_int_is_small(self)) {
|
|
||||||
// See PEP 683, section Accidental De-Immortalizing for details
|
|
||||||
_Py_SetImmortal(self);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
_Py_FREELIST_FREE(ints, self, PyObject_Free);
|
_Py_FREELIST_FREE(ints, self, PyObject_Free);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3686,24 +3679,20 @@ _PyLong_ExactDealloc(PyObject *self)
|
||||||
static void
|
static void
|
||||||
long_dealloc(PyObject *self)
|
long_dealloc(PyObject *self)
|
||||||
{
|
{
|
||||||
assert(self);
|
if (_long_is_small_int(self)) {
|
||||||
if (_PyLong_IsCompact((PyLongObject *)self)) {
|
/* This should never get called, but we also don't want to SEGV if
|
||||||
if (compact_int_is_small(self)) {
|
* we accidentally decref small Ints out of existence. Instead,
|
||||||
/* This should never get called, but we also don't want to SEGV if
|
* since small Ints are immortal, re-set the reference count.
|
||||||
* we accidentally decref small Ints out of existence. Instead,
|
*
|
||||||
* since small Ints are immortal, re-set the reference count.
|
* See PEP 683, section Accidental De-Immortalizing for details
|
||||||
*
|
*/
|
||||||
* See PEP 683, section Accidental De-Immortalizing for details
|
_Py_SetImmortal(self);
|
||||||
*/
|
return;
|
||||||
_Py_SetImmortal(self);
|
}
|
||||||
return;
|
if (PyLong_CheckExact(self) && _PyLong_IsCompact((PyLongObject *)self)) {
|
||||||
}
|
_Py_FREELIST_FREE(ints, self, PyObject_Free);
|
||||||
if (PyLong_CheckExact(self)) {
|
return;
|
||||||
_Py_FREELIST_FREE(ints, self, PyObject_Free);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_TYPE(self)->tp_free(self);
|
Py_TYPE(self)->tp_free(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6065,7 +6054,7 @@ long_subtype_new(PyTypeObject *type, PyObject *x, PyObject *obase)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
assert(PyLong_Check(newobj));
|
assert(PyLong_Check(newobj));
|
||||||
newobj->long_value.lv_tag = tmp->long_value.lv_tag;
|
newobj->long_value.lv_tag = tmp->long_value.lv_tag & ~IMMORTALITY_BIT_MASK;
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
newobj->long_value.ob_digit[i] = tmp->long_value.ob_digit[i];
|
newobj->long_value.ob_digit[i] = tmp->long_value.ob_digit[i];
|
||||||
}
|
}
|
||||||
|
|
|
@ -890,7 +890,7 @@ class PyLongObjectPtr(PyObjectPtr):
|
||||||
|
|
||||||
def proxyval(self, visited):
|
def proxyval(self, visited):
|
||||||
'''
|
'''
|
||||||
Python's Include/longinterpr.h has this declaration:
|
Python's Include/cpython/longinterpr.h has this declaration:
|
||||||
|
|
||||||
typedef struct _PyLongValue {
|
typedef struct _PyLongValue {
|
||||||
uintptr_t lv_tag; /* Number of digits, sign and flags */
|
uintptr_t lv_tag; /* Number of digits, sign and flags */
|
||||||
|
@ -909,8 +909,7 @@ class PyLongObjectPtr(PyObjectPtr):
|
||||||
- 0: Positive
|
- 0: Positive
|
||||||
- 1: Zero
|
- 1: Zero
|
||||||
- 2: Negative
|
- 2: Negative
|
||||||
The third lowest bit of lv_tag is reserved for an immortality flag, but is
|
The third lowest bit of lv_tag is set to 1 for the small ints and 0 otherwise.
|
||||||
not currently used.
|
|
||||||
|
|
||||||
where SHIFT can be either:
|
where SHIFT can be either:
|
||||||
#define PyLong_SHIFT 30
|
#define PyLong_SHIFT 30
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue