Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
use approach 2
  • Loading branch information
Fidget-Spinner committed Oct 28, 2021
commit 39afbd4831e65ad92273c3558e8b5b38ffb04e26
4 changes: 2 additions & 2 deletions Include/internal/pycore_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,8 @@ int _Py_Specialize_BinaryAdd(PyObject *left, PyObject *right, _Py_CODEUNIT *inst
int _Py_Specialize_BinaryMultiply(PyObject *left, PyObject *right, _Py_CODEUNIT *instr);
int _Py_Specialize_CallFunction(PyObject *callable, _Py_CODEUNIT *instr, int nargs, SpecializedCacheEntry *cache, PyObject *builtins);

#define PRINT_SPECIALIZATION_STATS 0
#define PRINT_SPECIALIZATION_STATS_DETAILED 0
#define PRINT_SPECIALIZATION_STATS 1
#define PRINT_SPECIALIZATION_STATS_DETAILED 1
#define PRINT_SPECIALIZATION_STATS_TO_FILE 0

#ifdef Py_DEBUG
Expand Down
3 changes: 3 additions & 0 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,9 @@ void _PyObject_ClearInstanceAttributes(PyObject *self);
void _PyObject_FreeInstanceAttributes(PyObject *self);
int _PyObject_IsInstanceDictEmpty(PyObject *);

PyObject *_PyType_FindNameInMRO(PyTypeObject *type, PyObject *name, int *error,
Py_ssize_t *mro_index);

#ifdef __cplusplus
}
#endif
Expand Down
12 changes: 8 additions & 4 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3776,8 +3776,9 @@ _PyType_GetModuleByDef(PyTypeObject *type, struct PyModuleDef *def)
/* Internal API to look for a name through the MRO, bypassing the method cache.
This returns a borrowed reference, and might set an exception.
'error' is set to: -1: error with exception; 1: error without exception; 0: ok */
static PyObject *
find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
PyObject *
_PyType_FindNameInMRO(PyTypeObject *type, PyObject *name, int *error,
Py_ssize_t *mro_index)
{
Py_ssize_t i, n;
PyObject *mro, *res, *base, *dict;
Expand Down Expand Up @@ -3830,6 +3831,9 @@ find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
}
}
*error = 0;
if (mro_index != NULL) {
*mro_index = i;
}
done:
Py_DECREF(mro);
return res;
Expand Down Expand Up @@ -3858,7 +3862,7 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
/* We may end up clearing live exceptions below, so make sure it's ours. */
assert(!PyErr_Occurred());

res = find_name_in_mro(type, name, &error);
res = _PyType_FindNameInMRO(type, name, &error, NULL);
/* Only put NULL results into cache if there was no error. */
if (error) {
/* It's not ideal to clear the error condition,
Expand Down Expand Up @@ -8330,7 +8334,7 @@ update_one_slot(PyTypeObject *type, slotdef *p)
assert(!PyErr_Occurred());
do {
/* Use faster uncached lookup as we won't get any cache hits during type setup. */
descr = find_name_in_mro(type, p->name_strobj, &error);
descr = _PyType_FindNameInMRO(type, p->name_strobj, &error, NULL);
if (descr == NULL) {
if (error == -1) {
/* It is unlikely but not impossible that there has been an exception
Expand Down
60 changes: 41 additions & 19 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -1477,21 +1477,6 @@ eval_frame_handle_pending(PyThreadState *tstate)
STAT_INC(LOAD_##attr_or_method, hit); \
Py_INCREF(res);

// shared by LOAD_ATTR_CLASS and LOAD_METHOD_CLASS
#define LOAD_CLASS_ATTR_OR_METHOD(attr_or_method) \
SpecializedCacheEntry *caches = GET_CACHE(); \
_PyAttrCache *cache1 = &caches[-1].attr; \
_PyObjectCache *cache2 = &caches[-2].obj; \
DEOPT_IF(!PyType_Check(cls), LOAD_##attr_or_method); \
DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != cache1->tp_version, \
LOAD_##attr_or_method); \
assert(cache1->tp_version != 0); \
STAT_INC(LOAD_##attr_or_method, hit); \
PyObject *res = cache2->obj; \
assert(res != NULL); \
Py_INCREF(res); \


static int
trace_function_entry(PyThreadState *tstate, InterpreterFrame *frame)
{
Expand Down Expand Up @@ -3765,10 +3750,35 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr

TARGET(LOAD_ATTR_CLASS) {
assert(cframe.use_tracing == 0);
PyObject *cls = TOP();
LOAD_CLASS_ATTR_OR_METHOD(ATTR);
PyObject *owner = TOP();
PyObject *res;
SpecializedCacheEntry *caches = GET_CACHE();
_PyAdaptiveEntry *cache0 = &caches[0].adaptive;
_PyAttrCache *cache1 = &caches[-1].attr;
_PyObjectCache *cache2 = &caches[-2].obj;
assert(cache1->tp_version != 0);
DEOPT_IF(!PyType_Check(owner), LOAD_ATTR);
PyTypeObject *tp = (PyTypeObject *)owner;

DEOPT_IF(tp->tp_mro != cache2->obj, LOAD_ATTR);
PyTypeObject *real_owner = (PyTypeObject *)PyTuple_GET_ITEM(tp->tp_mro,
cache0->index);
assert(PyType_Check(real_owner));
PyDictObject *dict = (PyDictObject *)real_owner->tp_dict;
assert(dict != NULL);
assert(PyDict_CheckExact((PyObject *)dict));
DEOPT_IF(dict->ma_keys->dk_version != cache1->tp_version, LOAD_ATTR);
PyObject *name = GETITEM(names, cache0->original_oparg);
uint32_t hint = cache1->dk_version_or_hint;
DEOPT_IF(hint >= (size_t)dict->ma_keys->dk_nentries, LOAD_ATTR);
PyDictKeyEntry *ep = DK_ENTRIES(dict->ma_keys) + hint;
DEOPT_IF(ep->me_key != name, LOAD_ATTR);
res = ep->me_value;
DEOPT_IF(res == NULL, LOAD_ATTR);
STAT_INC(LOAD_ATTR, hit);
Py_INCREF(res);
SET_TOP(res);
Py_DECREF(cls);
Py_DECREF(owner);
DISPATCH();
}

Expand Down Expand Up @@ -4578,8 +4588,20 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
TARGET(LOAD_METHOD_CLASS) {
/* LOAD_METHOD, for class methods */
assert(cframe.use_tracing == 0);
SpecializedCacheEntry *caches = GET_CACHE();
_PyAttrCache *cache1 = &caches[-1].attr;
_PyObjectCache *cache2 = &caches[-2].obj;

PyObject *cls = TOP();
LOAD_CLASS_ATTR_OR_METHOD(METHOD);
DEOPT_IF(!PyType_Check(cls), LOAD_METHOD);
DEOPT_IF(((PyTypeObject *)cls)->tp_version_tag != cache1->tp_version,
LOAD_METHOD);
assert(cache1->tp_version != 0);

STAT_INC(LOAD_METHOD, hit);
PyObject *res = cache2->obj;
assert(res != NULL);
Py_INCREF(res);
SET_TOP(NULL);
Py_DECREF(cls);
PUSH(res);
Expand Down
88 changes: 76 additions & 12 deletions Python/specialize.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@

#include <stdlib.h> // rand()

// Forward declarations
static int specialize_class_load_method(PyObject *owner, _Py_CODEUNIT *instr,
PyObject *name, _PyAttrCache *cache1, _PyObjectCache *cache2, int oparg,
int oparg_class);

/* For guidance on adding or extending families of instructions see
* ./adaptive.md
Expand Down Expand Up @@ -693,6 +689,77 @@ specialize_dict_access(
}
}

#if COLLECT_SPECIALIZATION_STATS_DETAILED
static int load_method_fail_kind(DesciptorClassification kind);
#endif

static int
specialize_class_load_attr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name,
_PyAdaptiveEntry *cache0, _PyAttrCache *cache1, _PyObjectCache *cache2)
{
PyTypeObject *tp = (PyTypeObject *)owner;
Py_ssize_t mro_index;
int error;
if (tp->tp_dict == NULL) {
if (PyType_Ready(tp) < 0) {
return 1;
}
}
PyObject *descr = NULL;
DesciptorClassification kind = analyze_descriptor(tp, name, &descr, 0);
switch (kind) {
case METHOD:
case NON_DESCRIPTOR:
break;
default:
SPECIALIZATION_FAIL(LOAD_METHOD, load_method_fail_kind(kind));
return 1;
}
if (!PyType_HasFeature(tp, Py_TPFLAGS_VALID_VERSION_TAG)) {
return 1;
}
PyObject *res = _PyType_FindNameInMRO(tp, name, &error, &mro_index);
if (res == NULL || error) {
if (error == -1) {
PyErr_Clear();
}
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_EXPECTED_ERROR);
return 1;
}
if (mro_index != (uint16_t)mro_index) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE);
return 1;
}
assert(tp->tp_mro != NULL);
assert(PyTuple_CheckExact(tp->tp_mro));
assert(mro_index >= 0);
assert(mro_index < PyTuple_GET_SIZE(tp->tp_mro));

PyObject *real_owner = PyTuple_GET_ITEM(tp->tp_mro, mro_index);
assert(PyType_Check(real_owner));
PyDictObject *real_owner_dict = (PyDictObject *)((PyTypeObject *)real_owner)->tp_dict;
PyObject *value = NULL;
Py_ssize_t hint = _PyDict_GetItemHint(real_owner_dict, name, -1, &value);
assert(hint != DKIX_ERROR);
assert(hint != DKIX_EMPTY);
if (hint != (uint32_t)hint) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_RANGE);
return -1;
}
uint32_t keys_version = _PyDictKeys_GetVersionForCurrentState(
real_owner_dict->ma_keys);
if (keys_version == 0) {
SPECIALIZATION_FAIL(LOAD_ATTR, SPEC_FAIL_OUT_OF_VERSIONS);
return -1;
}
cache0->index = (uint16_t)mro_index;
cache1->dk_version_or_hint = (uint32_t)hint;
cache1->tp_version = keys_version;
cache2->obj = tp->tp_mro;
*instr = _Py_MAKECODEUNIT(LOAD_ATTR_CLASS, _Py_OPARG(*instr));
return 0;
}

int
_Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, SpecializedCacheEntry *cache)
{
Expand All @@ -716,8 +783,7 @@ _Py_Specialize_LoadAttr(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name, Sp
}
}
if (PyType_Check(owner)) {
err = specialize_class_load_method(owner, instr, name, cache1, cache2,
LOAD_ATTR, LOAD_ATTR_CLASS);
err = specialize_class_load_attr(owner, instr, name, cache0, cache1, cache2);
if (err) {
goto fail;
}
Expand Down Expand Up @@ -923,8 +989,7 @@ load_method_fail_kind(DesciptorClassification kind)

static int
specialize_class_load_method(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name,
_PyAttrCache *cache1, _PyObjectCache *cache2, int oparg,
int oparg_class)
_PyAttrCache *cache1, _PyObjectCache *cache2)
{

PyObject *descr = NULL;
Expand All @@ -935,10 +1000,10 @@ specialize_class_load_method(PyObject *owner, _Py_CODEUNIT *instr, PyObject *nam
case NON_DESCRIPTOR:
cache1->tp_version = ((PyTypeObject *)owner)->tp_version_tag;
cache2->obj = descr;
*instr = _Py_MAKECODEUNIT(oparg_class, _Py_OPARG(*instr));
*instr = _Py_MAKECODEUNIT(LOAD_METHOD_CLASS, _Py_OPARG(*instr));
return 0;
default:
SPECIALIZATION_FAIL(oparg, load_method_fail_kind(kind));
SPECIALIZATION_FAIL(LOAD_METHOD, load_method_fail_kind(kind));
return -1;
}
}
Expand Down Expand Up @@ -968,8 +1033,7 @@ _Py_Specialize_LoadMethod(PyObject *owner, _Py_CODEUNIT *instr, PyObject *name,
}
}
if (PyType_Check(owner)) {
int err = specialize_class_load_method(owner, instr, name, cache1, cache2,
LOAD_METHOD, LOAD_METHOD_CLASS);
int err = specialize_class_load_method(owner, instr, name, cache1, cache2);
if (err) {
goto fail;
}
Expand Down