Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.2k
gh-132042: Remove resolve_slotdups to speedup class creation#132156
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
56d13fc1eed75dbba66d6bac95a58d1f5be8cf19e8b0ad87579a165da85329475c17fbfc17a68736bca41341ed93efb9caa792e9db6fafa91459c1608740af0370d597a57f7cc2372f19f48eb317d02656d5589ca7af5bd7f62d59b09deaf04539ccaf8ce309db0885dbf869f94347730a4c36905df4847a61ed3211d043a4c4d8e82466ccf0b40c725acb4a5db0944b185436c1884f5File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Improve class creation times by up to 12% by pre-computing type slots | ||
| just once. Patch by Sergey Miryanov. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -11422,6 +11422,11 @@ static pytype_slotdef slotdefs[] = { | ||
| {NULL} | ||
| }; | ||
| /* Stores the number of times where slotdefs has elements with same name. | ||
| This counter precalculated by _PyType_InitSlotDefs() when the main | ||
| interpreter starts. */ | ||
| static uint8_t slotdefs_name_counts[Py_ARRAY_LENGTH(slotdefs)]; | ||
| /* Given a type pointer and an offset gotten from a slotdef entry, return a | ||
| pointer to the actual slot. This is not quite the same as simply adding | ||
| the offset to the type pointer, since it takes care to indirect through the | ||
| @@ -11464,61 +11469,6 @@ slotptr(PyTypeObject *type, int ioffset) | ||
| return (void **)ptr; | ||
| } | ||
| /* Return a slot pointer for a given name, but ONLY if the attribute has | ||
| exactly one slot function. The name must be an interned string. */ | ||
| static void ** | ||
| resolve_slotdups(PyTypeObject *type, PyObject *name) | ||
| { | ||
| /* XXX Maybe this could be optimized more -- but is it worth it? */ | ||
| #ifdef Py_GIL_DISABLED | ||
| pytype_slotdef *ptrs[MAX_EQUIV]; | ||
| pytype_slotdef **pp = ptrs; | ||
| /* Collect all slotdefs that match name into ptrs. */ | ||
| for (pytype_slotdef *p = slotdefs; p->name_strobj; p++) { | ||
| if (p->name_strobj == name) | ||
| *pp++ = p; | ||
| } | ||
| *pp = NULL; | ||
| #else | ||
| /* pname and ptrs act as a little cache */ | ||
| PyInterpreterState *interp = _PyInterpreterState_GET(); | ||
| #define pname _Py_INTERP_CACHED_OBJECT(interp, type_slots_pname) | ||
| #define ptrs _Py_INTERP_CACHED_OBJECT(interp, type_slots_ptrs) | ||
| pytype_slotdef *p, **pp; | ||
| if (pname != name) { | ||
| /* Collect all slotdefs that match name into ptrs. */ | ||
| pname = name; | ||
| pp = ptrs; | ||
| for (p = slotdefs; p->name_strobj; p++) { | ||
| if (p->name_strobj == name) | ||
| *pp++ = p; | ||
| } | ||
| *pp = NULL; | ||
| } | ||
| #endif | ||
| /* Look in all slots of the type matching the name. If exactly one of these | ||
| has a filled-in slot, return a pointer to that slot. | ||
| Otherwise, return NULL. */ | ||
| void **res, **ptr; | ||
| res = NULL; | ||
| for (pp = ptrs; *pp; pp++) { | ||
| ptr = slotptr(type, (*pp)->offset); | ||
| if (ptr == NULL || *ptr == NULL) | ||
| continue; | ||
| if (res != NULL) | ||
| return NULL; | ||
| res = ptr; | ||
| } | ||
| #ifndef Py_GIL_DISABLED | ||
| #undef pname | ||
| #undef ptrs | ||
| #endif | ||
| return res; | ||
| } | ||
| // Return true if "name" corresponds to at least one slot definition. This is | ||
| // a more accurate but more expensive test compared to is_dunder_name(). | ||
| static bool | ||
| @@ -11645,7 +11595,15 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p, pytype_slotdef **next_p, | ||
| } | ||
| if (Py_IS_TYPE(descr, &PyWrapperDescr_Type) && | ||
| ((PyWrapperDescrObject *)descr)->d_base->name_strobj == p->name_strobj) { | ||
| void **tptr = resolve_slotdups(type, p->name_strobj); | ||
| void **tptr; | ||
| size_t index = (p - slotdefs) / sizeof(slotdefs[0]); | ||
| if (slotdefs_name_counts[index] == 1) { | ||
| tptr = slotptr(type, p->offset); | ||
| } | ||
| else { | ||
| tptr = NULL; | ||
| } | ||
| if (tptr == NULL || tptr == ptr) | ||
| generic = p->function; | ||
| d = (PyWrapperDescrObject *)descr; | ||
| @@ -11858,6 +11816,76 @@ update_all_slots(PyTypeObject* type) | ||
sergey-miryanov marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| #endif | ||
| int | ||
| _PyType_InitSlotDefs(PyInterpreterState *interp) | ||
| { | ||
| if (!_Py_IsMainInterpreter(interp)) { | ||
| return 0; | ||
| } | ||
| PyObject *bytearray = NULL; | ||
| PyObject *cache = PyDict_New(); | ||
| if (!cache) { | ||
| return -1; | ||
| } | ||
| pytype_slotdef *p; | ||
| Py_ssize_t idx = 0; | ||
| for (p = slotdefs; p->name_strobj; p++, idx++) { | ||
| assert(idx < 255); | ||
| if (PyDict_GetItemRef(cache, p->name_strobj, &bytearray) < 0) { | ||
| goto error; | ||
| } | ||
| if (!bytearray) { | ||
| Py_ssize_t size = sizeof(uint8_t) * (1 + MAX_EQUIV); | ||
| bytearray = PyByteArray_FromStringAndSize(NULL, size); | ||
| if (!bytearray) { | ||
| goto error; | ||
| } | ||
| uint8_t *data = (uint8_t *)PyByteArray_AS_STRING(bytearray); | ||
| data[0] = 0; | ||
| if (PyDict_SetItem(cache, p->name_strobj, bytearray) < 0) { | ||
| goto error; | ||
| } | ||
| } | ||
| assert(PyByteArray_CheckExact(bytearray)); | ||
| uint8_t *data = (uint8_t *)PyByteArray_AS_STRING(bytearray); | ||
| data[0] += 1; | ||
| assert(data[0] < MAX_EQUIV); | ||
| data[data[0]] = (uint8_t)idx; | ||
| Py_CLEAR(bytearray); | ||
| } | ||
| memset(slotdefs_name_counts, 0, sizeof(slotdefs_name_counts)); | ||
| Py_ssize_t pos = 0; | ||
| PyObject *key = NULL; | ||
| PyObject *value = NULL; | ||
| while (PyDict_Next(cache, &pos, &key, &value)) { | ||
| uint8_t *data = (uint8_t *)PyByteArray_AS_STRING(value); | ||
| uint8_t n = data[0]; | ||
| for (uint8_t i = 0; i < n; i++) { | ||
| uint8_t idx = data[i + 1]; | ||
| slotdefs_name_counts[idx] = n; | ||
| } | ||
| } | ||
| Py_DECREF(cache); | ||
| return 0; | ||
| error: | ||
| Py_XDECREF(bytearray); | ||
| Py_DECREF(cache); | ||
| return -1; | ||
| } | ||
| PyObject * | ||
| _PyType_GetSlotWrapperNames(void) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -344,6 +344,8 @@ Objects/obmalloc.c - obmalloc_state_main - | ||
| Objects/obmalloc.c - obmalloc_state_initialized - | ||
| Objects/typeobject.c - name_op - | ||
| Objects/typeobject.c - slotdefs - | ||
| # It initialized only once when main interpeter starts | ||
| Objects/typeobject.c - slotdefs_name_counts - | ||
sergey-miryanov marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| Objects/unicodeobject.c - stripfuncnames - | ||
| Objects/unicodeobject.c - utf7_category - | ||
| Objects/unicodeobject.c unicode_decode_call_errorhandler_wchar argparse - | ||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.