-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
bpo-40255: Implement Immortal Instances - Optimization 3 #31490
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
Changes from all commits
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 | ||||
---|---|---|---|---|---|---|
|
@@ -27,7 +27,7 @@ | |||||
from types import AsyncGeneratorType, FunctionType | ||||||
from operator import neg | ||||||
from test import support | ||||||
from test.support import (swap_attr, maybe_get_event_loop_policy) | ||||||
from test.support import (cpython_only, swap_attr, maybe_get_event_loop_policy) | ||||||
from test.support.os_helper import (EnvironmentVarGuard, TESTFN, unlink) | ||||||
from test.support.script_helper import assert_python_ok | ||||||
from test.support.warnings_helper import check_warnings | ||||||
|
@@ -2214,6 +2214,29 @@ def __del__(self): | |||||
self.assertEqual(["before", "after"], out.decode().splitlines()) | ||||||
|
||||||
|
||||||
@cpython_only | ||||||
class ImmortalTests(unittest.TestCase): | ||||||
def test_immortal(self): | ||||||
none_refcount = sys.getrefcount(None) | ||||||
true_refcount = sys.getrefcount(True) | ||||||
false_refcount = sys.getrefcount(False) | ||||||
smallint_refcount = sys.getrefcount(100) | ||||||
|
||||||
# Assert that all of these immortal instances have large ref counts | ||||||
self.assertGreater(none_refcount, 1e8) | ||||||
self.assertGreater(true_refcount, 1e8) | ||||||
self.assertGreater(false_refcount, 1e8) | ||||||
self.assertGreater(smallint_refcount, 1e8) | ||||||
|
||||||
# Confirm that the refcount doesn't change even with a new ref to them | ||||||
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.
Suggested change
|
||||||
l = [None, True, False, 100] | ||||||
self.assertEqual(sys.getrefcount(None), none_refcount) | ||||||
self.assertEqual(sys.getrefcount(True), true_refcount) | ||||||
self.assertEqual(sys.getrefcount(False), false_refcount) | ||||||
self.assertEqual(sys.getrefcount(100), smallint_refcount) | ||||||
|
||||||
|
||||||
|
||||||
class TestType(unittest.TestCase): | ||||||
def test_new_type(self): | ||||||
A = type('A', (), {}) | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
This introduces Immortal Instances which allows objects to bypass reference | ||
counting and remain alive throughout the execution of the runtime |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -281,8 +281,12 @@ gc_list_move(PyGC_Head *node, PyGC_Head *list) | |||||
/* Unlink from current list. */ | ||||||
PyGC_Head *from_prev = GC_PREV(node); | ||||||
PyGC_Head *from_next = GC_NEXT(node); | ||||||
_PyGCHead_SET_NEXT(from_prev, from_next); | ||||||
_PyGCHead_SET_PREV(from_next, from_prev); | ||||||
if (from_next) { | ||||||
_PyGCHead_SET_NEXT(from_prev, from_next); | ||||||
} | ||||||
if (from_prev) { | ||||||
_PyGCHead_SET_PREV(from_next, from_prev); | ||||||
} | ||||||
|
||||||
/* Relink at end of new list. */ | ||||||
// list must not have flags. So we can skip macros. | ||||||
|
@@ -1953,6 +1957,48 @@ gc_get_freeze_count_impl(PyObject *module) | |||||
} | ||||||
|
||||||
|
||||||
static int | ||||||
immortalize_object(PyObject *obj, PyGC_Head *permanent_gen) | ||||||
{ | ||||||
if (_Py_IsImmortal(obj)) { | ||||||
return 0; | ||||||
} | ||||||
|
||||||
_Py_SetImmortal(obj); | ||||||
/* Special case for PyCodeObjects since they don't have a tp_traverse */ | ||||||
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.
Suggested change
|
||||||
if (PyCode_Check(obj)) { | ||||||
PyCodeObject *code = (PyCodeObject *)obj; | ||||||
_Py_SetImmortal(code->co_code); | ||||||
_Py_SetImmortal(code->co_consts); | ||||||
_Py_SetImmortal(code->co_names); | ||||||
_Py_SetImmortal(code->co_varnames); | ||||||
_Py_SetImmortal(code->co_freevars); | ||||||
_Py_SetImmortal(code->co_cellvars); | ||||||
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. Make contents of these tuples immortal too. |
||||||
_Py_SetImmortal(code->co_filename); | ||||||
_Py_SetImmortal(code->co_name); | ||||||
_Py_SetImmortal(code->co_linetable); | ||||||
} | ||||||
|
||||||
PyTypeObject* tp = Py_TYPE(obj); | ||||||
if (tp->tp_traverse) { | ||||||
gc_list_move(AS_GC(obj), permanent_gen); | ||||||
tp->tp_traverse(obj, (visitproc)immortalize_object, permanent_gen); | ||||||
} | ||||||
return 0; | ||||||
} | ||||||
|
||||||
PyObject * | ||||||
_PyGC_TransitiveImmortalize(PyObject *obj) { | ||||||
_Py_SetImmortal(obj); | ||||||
Py_TYPE(obj)->tp_traverse( | ||||||
obj, | ||||||
(visitproc)immortalize_object, | ||||||
&_PyThreadState_GET()->interp->gc.permanent_generation.head | ||||||
); | ||||||
Py_RETURN_NONE; | ||||||
} | ||||||
|
||||||
|
||||||
PyDoc_STRVAR(gc__doc__, | ||||||
"This module provides access to the garbage collector for reference cycles.\n" | ||||||
"\n" | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -1705,7 +1705,8 @@ PyTypeObject _PyNone_Type = { | |||||
|
||||||
PyObject _Py_NoneStruct = { | ||||||
_PyObject_EXTRA_INIT | ||||||
1, &_PyNone_Type | ||||||
_Py_IMMORTAL_REFCNT, | ||||||
&_PyNone_Type | ||||||
}; | ||||||
|
||||||
/* NotImplemented is an object that can be used to signal that an | ||||||
|
@@ -1994,7 +1995,9 @@ _Py_NewReference(PyObject *op) | |||||
#ifdef Py_REF_DEBUG | ||||||
_Py_RefTotal++; | ||||||
#endif | ||||||
Py_SET_REFCNT(op, 1); | ||||||
/* Do not use Py_SET_REFCNT to skip the Immortal Instance check. This | ||||||
* API guarantees that an instance will always be set to a refcnt of 1 */ | ||||||
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.
Suggested change
|
||||||
op->ob_refcnt = 1; | ||||||
#ifdef Py_TRACE_REFS | ||||||
_Py_AddToAllObjects(op, 1); | ||||||
#endif | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -1829,6 +1829,10 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, | |||||
if (mod == NULL) { | ||||||
goto error; | ||||||
} | ||||||
// Immortalize top level modules | ||||||
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.
Suggested change
|
||||||
if (tstate->recursion_limit - tstate->recursion_remaining == 1) { | ||||||
_PyGC_TransitiveImmortalize(mod); | ||||||
} | ||||||
} | ||||||
|
||||||
has_from = 0; | ||||||
|
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.