8000 Immortalize known immortals · python/cpython@2f9fa29 · GitHub
[go: up one dir, main page]

Skip to content

Commit 2f9fa29

Browse files
Immortalize known immortals
1 parent 36e0a9a commit 2f9fa29

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+207
-137
lines changed

Include/object.h

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,54 @@ typedef struct _typeobject PyTypeObject;
8181
/* PyObject_HEAD defines the initial segment of every PyObject. */
8282
#define PyObject_HEAD PyObject ob_base;
8383

84+
/* [RFC] Should we enable Immortal Instances by Default? */
85+
#define Py_IMMORTAL_OBJECTS
86+
87+
/* Immortalizing causes the instance to not participate in reference counting.
88+
* Thus, an immortal object will be kept alive until the runtime finalization.
89+
* This avoids an unnecessary copy-on-write for applications that share
90+
* a common python heap across many processes. */
91+
#ifdef Py_IMMORTAL_OBJECTS
92+
93+
/* The GC bit-shifts refcounts left by two, and after that shift we still
94+
* need this to be >> 0, so leave three high zero bits (the sign bit and
95+
* room for a shift of two.) */
96+
static const Py_ssize_t _Py_IMMORTAL_BIT_POS = 4;
97+
static const Py_ssize_t _Py_IMMORTAL_BIT = 1L << (8 * sizeof(Py_ssize_t) - _Py_IMMORTAL_BIT_POS);
98+
99+
#endif /* Py_IMMORTAL_OBJECTS */
100+
84101
#define PyObject_HEAD_INIT(type) \
85102
{ _PyObject_EXTRA_INIT \
86103
1, type },
87104

105+
#ifdef Py_IMMORTAL_OBJECTS
106+
107+
#define PyObject_HEAD_IMMORTAL_INIT(type) \
108+
{ _PyObject_EXTRA_INIT _Py_IMMORTAL_BIT, type },
109+
110+
#else
111+
112+
#define PyObject_HEAD_IMMORTAL_INIT(type) \
113+
{ _PyObject_EXTRA_INIT 1, type },
114+
115+
#endif /* Py_IMMORTAL_OBJECTS */
116+
88117
#define PyVarObject_HEAD_INIT(type, size) \
89118
{ PyObject_HEAD_INIT(type) size },
90119

120+
#ifdef Py_IMMORTAL_OBJECTS
121+
122+
#define PyVarObject_HEAD_IMMORTAL_INIT(type, size) \
123+
{ PyObject_HEAD_IMMORTAL_INIT(type) size },
124+
125+
#else
126+
127+
#define PyVarObject_HEAD_IMMORTAL_INIT(type, size) \
128+
{ PyObject_HEAD_INIT(type) size },
129+
130+
#endif /* Py_IMMORTAL_OBJECTS */
131+
91132
/* PyObject_VAR_HEAD defines the initial segment of all variable-size
92133
* container objects. These end with a declaration of an array with 1
93134
* element, but enough space is malloc'ed so that the array actually
@@ -124,32 +165,21 @@ typedef struct {
124165
#define Py_TYPE(ob) (_PyObject_CAST(ob)->ob_type)
125166
#define Py_SIZE(ob) (_PyVarObject_CAST(ob)->ob_size)
126167

168+
#ifdef Py_IMMORTAL_OBJECTS
127169

128-
/* [RFC] Should we enable Immortal Instances by Default? */
129-
#define Py_IMMORTAL_INSTANCES
130-
131-
/* Immortalizing causes the instance to not participate in reference counting.
132-
* Thus, an immortal object will be kept alive until the runtime finalization.
133-
* This avoids an unnecessary copy-on-write for applications that share
134-
* a common python heap across many processes. */
135-
#ifdef Py_IMMORTAL_INSTANCES
136-
137-
/* The GC bit-shifts refcounts left by two, and after that shift we still
138-
* need this to be >> 0, so leave three high zero bits (the sign bit and
139-
* room for a shift of two.) */
140-
static const Py_ssize_t kImmortalBit = 1L << (8 * sizeof(Py_ssize_t) - 4);
170+
PyAPI_FUNC(PyObject *) _PyGC_ImmortalizeHeap(void);
141171

142172
static inline int _Py_IsImmortal(PyObject *op)
143173
{
144-
return (op->ob_refcnt & kImmortalBit) != 0;
174+
return (op->ob_refcnt & _Py_IMMORTAL_BIT) != 0;
145175
}
146176

147177
static inline void _Py_SetImmortal(PyObject *op)
148178
{
149-
op->ob_refcnt = kImmortalBit;
179+
op->ob_refcnt = _Py_IMMORTAL_BIT;
150180
}
151181

152-
#endif /* Py_IMMORTAL_INSTANCES */
182+
#endif /* Py_IMMORTAL_OBJECTS */
153183

154184

155185
static inline int _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {
@@ -158,11 +188,11 @@ static inline int _Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {
158188
#define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST_CONST(ob), type)
159189

160190
static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) {
161-
#ifdef Py_IMMORTAL_INSTANCES
191+
#ifdef Py_IMMORTAL_OBJECTS
162192
if (_Py_IsImmortal(ob)) {
163193
return;
164194
}
165-
#endif /* Py_IMMORTAL_INSTANCES */
195+
#endif /* Py_IMMORTAL_OBJECTS */
166196
ob->ob_refcnt = refcnt;
167197
}
168198
#define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt)
@@ -433,11 +463,11 @@ static inline void _Py_INCREF(PyObject *op)
433463
#ifdef Py_REF_DEBUG
434464
_Py_RefTotal++;
435465
#endif
436-
#ifdef Py_IMMORTAL_INSTANCES
466+
#ifdef Py_IMMORTAL_OBJECTS
437467
if (_Py_IsImmortal(op)) {
438468
return;
439469
}
440-
#endif /* Py_IMMORTAL_INSTANCES */
470+
#endif /* Py_IMMORTAL_OBJECTS */
441471
op->ob_refcnt++;
442472
}
443473

@@ -452,11 +482,11 @@ static inline void _Py_DECREF(
452482
#ifdef Py_REF_DEBUG
453483
_Py_RefTotal--;
454484
#endif
455-
#ifdef Py_IMMORTAL_INSTANCES
485+
#ifdef Py_IMMORTAL_OBJECTS
456486
if (_Py_IsImmortal(op)) {
457487
return;
458488
}
459-
#endif /* Py_IMMORTAL_INSTANCES */
489+
#endif /* Py_IMMORTAL_OBJECTS */
460490
if (--op->ob_refcnt != 0) {
461491
#ifdef Py_REF_DEBUG
462492
if (op->ob_refcnt < 0) {

Lib/test/test_gc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,11 +1042,13 @@ class Z:
10421042
# These tests need to be run in a separate process since gc.immortalize_heap
10431043
# will mess up with the reference count of other tests
10441044
class GCImmortalizeTests(unittest.TestCase):
1045+
@unittest.skipUnless(hasattr(gc, "is_immortal")),
10451046
def test_not_immortal(self):
10461047
obj = []
10471048
self.assertFalse(gc.is_immortal(obj))
10481049

10491050
@unittest.skipIf("win" in sys.platform, 'needs fix under Windows')
1051+
@unittest.skipUnless(hasattr(gc, "is_immortal")),
10501052
def test_is_immortal(self):
10511053
code = """if 1:
10521054
import gc
@@ -1058,6 +1060,7 @@ def test_is_immortal(self):
10581060
self.assertEqual(out.strip(), b'True')
10591061

10601062
@unittest.skipIf("win" in sys.platform, 'needs fix under Windows')
1063+
@unittest.skipUnless(hasattr(gc, "is_immortal")),
10611064
def test_post_immortalize(self):
10621065
code = """if 1:
10631066
import gc
@@ -1069,6 +1072,7 @@ def test_post_immortalize(self):
10691072
self.assertEqual(out.strip(), b'False')
10701073

10711074
@unittest.skipIf("win" in sys.platform, 'needs fix under Windows')
1075+
@unittest.skipUnless(hasattr(gc, "is_immortal")),
10721076
def test_become_tracked_after_immortalize(self):
10731077
code = """if 1:
10741078
import gc

Modules/clinic/gcmodule.c.h

Lines changed: 17 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/gcmodule.c

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1940,6 +1940,7 @@ gc_get_freeze_count_impl(PyObject *module)
19401940
}
19411941

19421942

1943+
#ifdef Py_IMMORTAL_OBJECTS
19431944
/*[clinic input]
19441945
gc.is_immortal -> bool
19451946
@@ -1953,15 +1954,11 @@ static int
19531954
gc_is_immortal_impl(PyObject *module, PyObject *instance)
19541955
/*[clinic end generated code: output=743a163cb6aaf384 input=815182ca5c5d81e4]*/
19551956
{
1956-
#ifdef Py_IMMORTAL_INSTANCES
19571957
return _Py_IsImmortal(instance);
1958-
#else
1959-
return 0;
1960-
#endif /* Py_IMMORTAL_INSTANCES */
19611958
}
1959+
#endif /* Py_IMMORTAL_OBJECTS */
19621960

1963-
1964-
#ifdef Py_IMMORTAL_INSTANCES
1961+
#ifdef Py_IMMORTAL_OBJECTS
19651962
static int
19661963
immortalize_object(PyObject *obj, PyObject *Py_UNUSED(ignored))
19671964
{
@@ -1981,19 +1978,26 @@ immortalize_object(PyObject *obj, PyObject *Py_UNUSED(ignored))
19811978
}
19821979
return 0;
19831980
}
1984-
#endif /* Py_IMMORTAL_INSTANCES */
1981+
#endif /* Py_IMMORTAL_OBJECTS */
19851982

1983+
#ifdef Py_IMMORTAL_OBJECTS
19861984
/*[clinic input]
19871985
gc.immortalize_heap
19881986
19891987
Immortalize all instances accessible through the GC roots.
19901988
[clinic start generated code]*/
19911989

19921990
static PyObject *
1993-
gc_immortalize_heap_impl(PyObject *module)
1991+
gc_immortalize_heap_impl(PyObject *Py_UNUSED(ignored))
19941992
/*[clinic end generated code: output=a7bb85fe2e27e4ae input=ca1709e4667c0623]*/
19951993
{
1996-
#ifdef Py_IMMORTAL_INSTANCES
1994+
return _PyGC_ImmortalizeHeap();
1995+
}
1996+
#endif /* Py_IMMORTAL_OBJECTS */
1997+
1998+
#ifdef Py_IMMORTAL_OBJECTS
1999+
PyObject *
2000+
_PyGC_ImmortalizeHeap(void) {
19972001
PyGC_Head *gc, *list;
19982002
PyThreadState *tstate = _PyThreadState_GET();
19992003
GCState *gcstate = &tstate->interp->gc;
@@ -2002,7 +2006,7 @@ gc_immortalize_heap_impl(PyObject *module)
20022006
PyGC_Collect();
20032007

20042008
/* Move all instances into the permanent generation */
2005-
gc_freeze_impl(module);
2009+
gc_freeze_impl(NULL);
20062010

20072011
/* Immortalize all instances in the permanent generation */
20082012
list = &gcstate->permanent_generation.head;
@@ -2017,9 +2021,9 @@ gc_immortalize_heap_impl(PyObject *module)
20172021
Py_TYPE(FROM_GC(gc))->tp_traverse(
20182022
FROM_GC(gc), (visitproc)immortalize_object, NULL);
20192023
}
2020-
#endif /* Py_IMMORTAL_INSTANCES */
20212024
Py_RETURN_NONE;
20222025
}
2026+
#endif /* Py_IMMORTAL_OBJECTS */
20232027

20242028

20252029
PyDoc_STRVAR(gc__doc__,
@@ -2040,8 +2044,10 @@ PyDoc_STRVAR(gc__doc__,
20402044
"is_finalized() -- Returns true if a given object has been already finalized.\n"
20412045
"get_referrers() -- Return the list of objects that refer to an object.\n"
20422046
"get_referents() -- Return the list of objects that an object refers to.\n"
2047+
#ifdef Py_IMMORTAL_OBJECTS
20432048
"immortalize_heap() -- Immortalize all instances accessible through the GC roots.\n"
20442049
"is_immortal() -- Check if an object has been immortalized.\n"
2050+
#endif /* Py_IMMORTAL_OBJECTS */
20452051
"freeze() -- Freeze all tracked objects and ignore them for future collections.\n"
20462052
"unfreeze() -- Unfreeze all objects in the permanent generation.\n"
20472053
"get_freeze_count() -- Return the number of objects in the permanent generation.\n");
@@ -2067,8 +2073,10 @@ static PyMethodDef GcMethods[] = {
20672073
GC_FREEZE_METHODDEF
20682074
GC_UNFREEZE_METHODDEF
20692075
GC_GET_FREEZE_COUNT_METHODDEF
2076+
#ifdef Py_IMMORTAL_OBJECTS
20702077
GC_IMMORTALIZE_HEAP_METHODDEF
20712078
GC_IS_IMMORTAL_METHODDEF
2079+
#endif /* Py_IMMORTAL_OBJECTS */
20722080
{NULL, NULL} /* Sentinel */
20732081
};
20742082

Modules/main.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,11 @@ pymain_main(_PyArgv *args)
659659
pymain_exit_error(status);
660660
}
661661

662+
#ifdef Py_IMMORTAL_OBJECTS
663+
/* Most of the objects alive at this point will stay alive throughout the
664+
* lifecycle of the runtime. Immortalize to avoid the GC and refcnt costs */
665+
_PyGC_ImmortalizeHeap();
666+
#endif /* Py_IMMORTAL_OBJECTS */
662667
return Py_RunMain();
663668
}
664669

Objects/boolobject.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ static PyNumberMethods bool_as_number = {
132132
/* The type object for bool. Note that this cannot be subclassed! */
133133

134134
PyTypeObject PyBool_Type = {
135-
PyVarObject_HEAD_INIT(&PyType_Type, 0)
135+
PyVarObject_HEAD_IMMORTAL_INIT(&PyType_Type, 0)
136136
"bool",
137137
sizeof(struct _longobject),
138138
0,
@@ -175,11 +175,11 @@ PyTypeObject PyBool_Type = {
175175
/* The objects representing bool values False and True */
176176

177177
struct _longobject _Py_FalseStruct = {
178-
PyVarObject_HEAD_INIT(&PyBool_Type, 0)
178+
PyVarObject_HEAD_IMMORTAL_INIT(&PyBool_Type, 0)
179179
{ 0 }
180180
};
181181

182182
struct _longobject _Py_TrueStruct = {
183-
PyVarObject_HEAD_INIT(&PyBool_Type, 1)
183+
PyVarObject_HEAD_IMMORTAL_INIT(&PyBool_Type, 1)
184184
{ 1 }
185185
};

Objects/bytearrayobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2262,7 +2262,7 @@ Construct a mutable bytearray object from:\n\
22622262
static PyObject *bytearray_iter(PyObject *seq);
22632263

22642264
PyTypeObject PyByteArray_Type = {
2265-
PyVarObject_HEAD_INIT(&PyType_Type, 0)
2265+
PyVarObject_HEAD_IMMORTAL_INIT(&PyType_Type, 0)
22662266
"bytearray",
22672267
sizeof(PyByteArrayObject),
22682268
0,
@@ -2408,7 +2408,7 @@ static PyMethodDef bytearrayiter_methods[] = {
24082408
};
24092409

24102410
PyTypeObject PyByteArrayIter_Type = {
2411-
PyVarObject_HEAD_INIT(&PyType_Type, 0)
2411+
PyVarObject_HEAD_IMMORTAL_INIT(&PyType_Type, 0)
24122412
"bytearray_iterator", /* tp_name */
24132413
sizeof(bytesiterobject), /* tp_basicsize */
24142414
0, /* tp_itemsize */

Objects/bytesobject.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2808,7 +2808,7 @@ Construct an immutable array of bytes from:\n\
28082808
static PyObject *bytes_iter(PyObject *seq);
28092809

28102810
PyTypeObject PyBytes_Type = {
2811-
PyVarObject_HEAD_INIT(&PyType_Type, 0)
2811+
PyVarObject_HEAD_IMMORTAL_INIT(&PyType_Type, 0)
28122812
"bytes",
28132813
PyBytesObject_SIZE,
28142814
sizeof(char),
@@ -3089,7 +3089,7 @@ static PyMethodDef striter_methods[] = {
30893089
};
30903090

30913091
PyTypeObject PyBytesIter_Type = {
3092-
PyVarObject_HEAD_INIT(&PyType_Type, 0)
3092+
PyVarObject_HEAD_IMMORTAL_INIT(&PyType_Type, 0)
30933093
"bytes_iterator", /* tp_name */
30943094
sizeof(striterobject), /* tp_basicsize */
30953095
0, /* tp_itemsize */

Objects/capsule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ Python import mechanism to link to one another.\n\
297297
");
298298

299299
PyTypeObject PyCapsule_Type = {
300-
PyVarObject_HEAD_INIT(&PyType_Type, 0)
300+
PyVarObject_HEAD_IMMORTAL_INIT(&PyType_Type, 0)
301301
"PyCapsule", /*tp_name*/
302302
sizeof(PyCapsule), /*tp_basicsize*/
303303
0, /*tp_itemsize*/

Objects/cellobject.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ static PyGetSetDef cell_getsetlist[] = {
157157
};
158158

159159
PyTypeObject PyCell_Type = {
160-
PyVarObject_HEAD_INIT(&PyType_Type, 0)
160+
PyVarObject_HEAD_IMMORTAL_INIT(&PyType_Type, 0)
161161
"cell",
162162
sizeof(PyCellObject),
163163
0,

0 commit comments

Comments
 (0)
0