bpo-30156: Remove property_descr_get() optimization (GH-9541)

property_descr_get() uses a "cached" tuple to optimize function
calls. But this tuple can be discovered in debug mode with
sys.getobjects(). Remove the optimization, it's not really worth it
and it causes 3 different crashes last years.

Microbenchmark:

./python -m perf timeit -v \
    -s "from collections import namedtuple; P = namedtuple('P', 'x y'); p = P(1, 2)" \
    --duplicate 1024 "p.x"

Result:

Mean +- std dev: [ref] 32.8 ns +- 0.8 ns -> [patch] 40.4 ns +- 1.3 ns: 1.23x slower (+23%)
This commit is contained in:
Victor Stinner 2018-10-01 03:03:22 -07:00 committed by GitHub
parent 9df100286b
commit e972c13624
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 9 additions and 28 deletions

View File

@ -0,0 +1,4 @@
The C function ``property_descr_get()`` uses a "cached" tuple to optimize
function calls. But this tuple can be discovered in debug mode with
:func:`sys.getobjects()`. Remove the optimization, it's not really worth it
and it causes 3 different crashes last years.

View File

@ -1331,42 +1331,19 @@ property_dealloc(PyObject *self)
static PyObject *
property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
static PyObject * volatile cached_args = NULL;
PyObject *args;
PyObject *ret;
propertyobject *gs = (propertyobject *)self;
if (obj == NULL || obj == Py_None) {
Py_INCREF(self);
return self;
}
propertyobject *gs = (propertyobject *)self;
if (gs->prop_get == NULL) {
PyErr_SetString(PyExc_AttributeError, "unreadable attribute");
return NULL;
}
args = cached_args;
cached_args = NULL;
if (!args) {
args = PyTuple_New(1);
if (!args)
return NULL;
_PyObject_GC_UNTRACK(args);
}
Py_INCREF(obj);
PyTuple_SET_ITEM(args, 0, obj);
ret = PyObject_Call(gs->prop_get, args, NULL);
if (cached_args == NULL && Py_REFCNT(args) == 1) {
assert(PyTuple_GET_SIZE(args) == 1);
assert(PyTuple_GET_ITEM(args, 0) == obj);
cached_args = args;
Py_DECREF(obj);
}
else {
assert(Py_REFCNT(args) >= 1);
_PyObject_GC_TRACK(args);
Py_DECREF(args);
}
return ret;
PyObject *args[1] = {obj};
return _PyObject_FastCall(gs->prop_get, args, 1);
}
static int