Skip to content

Commit 630c8df

Browse files
authored
bpo-38858: Small integer per interpreter (GH-17315)
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.
1 parent f501db2 commit 630c8df

File tree

5 files changed

+56
-37
lines changed

5 files changed

+56
-37
lines changed

Include/internal/pycore_pylifecycle.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ PyAPI_FUNC(int) _Py_IsLocaleCoercionTarget(const char *ctype_loc);
3333

3434
extern PyStatus _PyUnicode_Init(void);
3535
extern int _PyStructSequence_Init(void);
36-
extern int _PyLong_Init(void);
36+
extern int _PyLong_Init(PyThreadState *tstate);
3737
extern PyStatus _PyFaulthandler_Init(int enable);
3838
extern int _PyTraceMalloc_Init(int enable);
3939
extern PyObject * _PyBuiltin_Init(PyThreadState *tstate);
@@ -76,7 +76,7 @@ extern void _PyGC_Fini(PyThreadState *tstate);
7676
extern void _PyType_Fini(void);
7777
extern void _Py_HashRandomization_Fini(void);
7878
extern void _PyUnicode_Fini(PyThreadState *tstate);
79-
extern void _PyLong_Fini(void);
79+
extern void _PyLong_Fini(PyThreadState *tstate);
8080
extern void _PyFaulthandler_Fini(void);
8181
extern void _PyHash_Fini(void);
8282
extern void _PyTraceMalloc_Fini(void);

Include/internal/pycore_pystate.h

+12
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ struct _ceval_runtime_state {
5656

5757
typedef PyObject* (*_PyFrameEvalFunction)(struct _frame *, int);
5858

59+
#define _PY_NSMALLPOSINTS 257
60+
#define _PY_NSMALLNEGINTS 5
61+
5962
// The PyInterpreterState typedef is in Include/pystate.h.
6063
struct _is {
6164

@@ -139,6 +142,15 @@ struct _is {
139142
int atbol;
140143
} listnode;
141144
} parser;
145+
146+
#if _PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS > 0
147+
/* Small integers are preallocated in this array so that they
148+
can be shared.
149+
The integers that are preallocated are those in the range
150+
-_PY_NSMALLNEGINTS (inclusive) to _PY_NSMALLPOSINTS (not inclusive).
151+
*/
152+
PyLongObject* small_ints[_PY_NSMALLNEGINTS + _PY_NSMALLPOSINTS];
153+
#endif
142154
};
143155

144156
PyAPI_FUNC(struct _is*) _PyInterpreterState_LookUpID(PY_INT64_T);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Each Python subinterpreter now has its own "small integer singletons":
2+
numbers in [-5; 257] range. It is no longer possible to change the number of
3+
small integers at build time by overriding ``NSMALLNEGINTS`` and
4+
``NSMALLPOSINTS`` macros: macros should now be modified manually in
5+
``pycore_pystate.h`` header file.

Objects/longobject.c

+28-31
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
/* XXX The functional organization of this file is terrible */
44

55
#include "Python.h"
6+
#include "pycore_pystate.h" /* _Py_IsMainInterpreter() */
67
#include "longintrepr.h"
78

89
#include <float.h>
@@ -15,12 +16,8 @@ class int "PyObject *" "&PyLong_Type"
1516
[clinic start generated code]*/
1617
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ec0275e3422a36e3]*/
1718

18-
#ifndef NSMALLPOSINTS
19-
#define NSMALLPOSINTS 257
20-
#endif
21-
#ifndef NSMALLNEGINTS
22-
#define NSMALLNEGINTS 5
23-
#endif
19+
#define NSMALLPOSINTS _PY_NSMALLPOSINTS
20+
#define NSMALLNEGINTS _PY_NSMALLNEGINTS
2421

2522
_Py_IDENTIFIER(little);
2623
_Py_IDENTIFIER(big);
@@ -35,13 +32,6 @@ PyObject *_PyLong_Zero = NULL;
3532
PyObject *_PyLong_One = NULL;
3633

3734
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
38-
/* Small integers are preallocated in this array so that they
39-
can be shared.
40-
The integers that are preallocated are those in the range
41-
-NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
42-
*/
43-
static PyLongObject* small_ints[NSMALLNEGINTS + NSMALLPOSINTS] = {0};
44-
4535
#define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS)
4636
#define IS_SMALL_UINT(ival) ((ival) < NSMALLPOSINTS)
4737

@@ -53,7 +43,8 @@ static PyObject *
5343
get_small_int(sdigit ival)
5444
{
5545
assert(IS_SMALL_INT(ival));
56-
PyObject *v = (PyObject*)small_ints[ival + NSMALLNEGINTS];
46+
PyThreadState *tstate = _PyThreadState_GET();
47+
PyObject *v = (PyObject*)tstate->interp->small_ints[ival + NSMALLNEGINTS];
5748
Py_INCREF(v);
5849
#ifdef COUNT_ALLOCS
5950
if (ival >= 0)
@@ -5782,7 +5773,7 @@ PyLong_GetInfo(void)
57825773
}
57835774

57845775
int
5785-
_PyLong_Init(void)
5776+
_PyLong_Init(PyThreadState *tstate)
57865777
{
57875778
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
57885779
for (Py_ssize_t i=0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++) {
@@ -5797,37 +5788,43 @@ _PyLong_Init(void)
57975788
Py_SIZE(v) = size;
57985789
v->ob_digit[0] = (digit)abs(ival);
57995790

5800-
small_ints[i] = v;
5791+
tstate->interp->small_ints[i] = v;
58015792
}
58025793
#endif
5803-
_PyLong_Zero = PyLong_FromLong(0);
5804-
if (_PyLong_Zero == NULL) {
5805-
return 0;
5806-
}
58075794

5808-
_PyLong_One = PyLong_FromLong(1);
5809-
if (_PyLong_One == NULL) {
5810-
return 0;
5811-
}
5795+
if (_Py_IsMainInterpreter(tstate)) {
5796+
_PyLong_Zero = PyLong_FromLong(0);
5797+
if (_PyLong_Zero == NULL) {
5798+
return 0;
5799+
}
58125800

5813-
/* initialize int_info */
5814-
if (Int_InfoType.tp_name == NULL) {
5815-
if (PyStructSequence_InitType2(&Int_InfoType, &int_info_desc) < 0) {
5801+
_PyLong_One = PyLong_FromLong(1);
5802+
if (_PyLong_One == NULL) {
58165803
return 0;
58175804
}
5805+
5806+
/* initialize int_info */
5807+
if (Int_InfoType.tp_name == NULL) {
5808+
if (PyStructSequence_InitType2(&Int_InfoType, &int_info_desc) < 0) {
5809+
return 0;
5810+
}
5811+
}
58185812
}
58195813

58205814
return 1;
58215815
}
58225816

58235817
void
5824-
_PyLong_Fini(void)
5818+
_PyLong_Fini(PyThreadState *tstate)
58255819
{
5826-
Py_CLEAR(_PyLong_One);
5827-
Py_CLEAR(_PyLong_Zero);
5820+
if (_Py_IsMainInterpreter(tstate)) {
5821+
Py_CLEAR(_PyLong_One);
5822+
Py_CLEAR(_PyLong_Zero);
5823+
}
5824+
58285825
#if NSMALLNEGINTS + NSMALLPOSINTS > 0
58295826
for (Py_ssize_t i = 0; i < NSMALLNEGINTS + NSMALLPOSINTS; i++) {
5830-
Py_CLEAR(small_ints[i]);
5827+
Py_CLEAR(tstate->interp->small_ints[i]);
58315828
}
58325829
#endif
58335830
}

Python/pylifecycle.c

+9-4
Original file line numberDiff line numberDiff line change
@@ -576,10 +576,11 @@ pycore_init_types(PyThreadState *tstate)
576576
if (_PyStatus_EXCEPTION(status)) {
577577
return status;
578578
}
579+
}
579580

580-
if (!_PyLong_Init()) {
581-
return _PyStatus_ERR("can't init longs");
582-
}
581+
582+
if (!_PyLong_Init(tstate)) {
583+
return _PyStatus_ERR("can't init longs");
583584
}
584585

585586
if (is_main_interp) {
@@ -1251,7 +1252,11 @@ finalize_interp_types(PyThreadState *tstate, int is_main_interp)
12511252
_PyList_Fini();
12521253
_PySet_Fini();
12531254
_PyBytes_Fini();
1254-
_PyLong_Fini();
1255+
}
1256+
1257+
_PyLong_Fini(tstate);
1258+
1259+
if (is_main_interp) {
12551260
_PyFloat_Fini();
12561261
_PyDict_Fini();
12571262
_PySlice_Fini();

0 commit comments

Comments
 (0)