Consider:
class MyObject: pass
def func():
o = MyObject()
o.__dict__
for _ in range(100):
o.foo = "bar"
o.baz = "qux"
for _ in range(100):
func()
opcode[STORE_ATTR_INSTANCE_VALUE].specialization.miss : 20382
opcode[STORE_ATTR_INSTANCE_VALUE].execution_count : 21167
The STORE_ATTR_INSTANCE_VALUE has a guard _GUARD_DORV_NO_DICT that ensures that the object does not have a managed dictionary:
|
EXIT_IF(_PyObject_GetManagedDict(owner_o)); |
However, the specializer for STORE_ATTR_INSTANCE_VALUE does not take that into account. It only checks that the inline values are valid:
|
if (type->tp_flags & Py_TPFLAGS_INLINE_VALUES && _PyObject_InlineValues(owner)->valid) { |
|
PyDictKeysObject *keys = ((PyHeapTypeObject *)type)->ht_cached_keys; |
|
assert(PyUnicode_CheckExact(name)); |
|
Py_ssize_t index = _PyDictKeys_StringLookup(keys, name); |
|
assert (index != DKIX_ERROR); |
|
if (index == DKIX_EMPTY) { |
|
SPECIALIZATION_FAIL(base_op, SPEC_FAIL_ATTR_NOT_IN_KEYS); |
|
return 0; |
|
} |
|
assert(index >= 0); |
|
char *value_addr = (char *)&_PyObject_InlineValues(owner)->values[index]; |
|
Py_ssize_t offset = value_addr - (char *)owner; |
|
if (offset != (uint16_t)offset) { |
|
SPECIALIZATION_FAIL(base_op, SPEC_FAIL_OUT_OF_RANGE); |
|
return 0; |
|
} |
|
write_u32(cache->version, type->tp_version_tag); |
|
cache->index = (uint16_t)offset; |
|
instr->op.code = values_op; |
|
} |
I'm not sure if we should change the guard or change specialize.c
Linked PRs
Consider:
The
STORE_ATTR_INSTANCE_VALUEhas a guard_GUARD_DORV_NO_DICTthat ensures that the object does not have a managed dictionary:cpython/Python/bytecodes.c
Line 2269 in 760872e
However, the specializer for
STORE_ATTR_INSTANCE_VALUEdoes not take that into account. It only checks that the inline values are valid:cpython/Python/specialize.c
Lines 867 to 886 in 760872e
I'm not sure if we should change the guard or change
specialize.cLinked PRs
STORE_ATTR_INSTANCE_VALUEspecialization check #125612STORE_ATTR_INSTANCE_VALUEspecialization check (GH-125612) #127698