Skip to content

Commit ee48c7d

Browse files
authored
bpo-43452: Micro-optimizations to PyType_Lookup (GH-24804)
The common case going through _PyType_Lookup is to have a cache hit. There are some small tweaks that can make this a little cheaper: * The name field identity is used for a cache hit and is kept alive by the cache. So there's no need to read the hash code o the name - instead, the address can be used as the hash. * There's no need to check if the name is cachable on the lookup either, it probably is, and if it is, it'll be in the cache. * If we clear the version tag when invalidating a type then we don't actually need to check for a valid version tag bit.
1 parent 2fd16ef commit ee48c7d

File tree

2 files changed

+24
-28
lines changed

2 files changed

+24
-28
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added micro-optimizations to ``_PyType_Lookup()`` to improve cache lookup performance in the common case of cache hits.

Objects/typeobject.c

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,7 @@ class object "PyObject *" "&PyBaseObject_Type"
3232
& ((1 << MCACHE_SIZE_EXP) - 1))
3333

3434
#define MCACHE_HASH_METHOD(type, name) \
35-
MCACHE_HASH((type)->tp_version_tag, \
36-
((PyASCIIObject *)(name))->hash)
35+
MCACHE_HASH((type)->tp_version_tag, ((Py_ssize_t)(name)) >> 3)
3736
#define MCACHE_CACHEABLE_NAME(name) \
3837
PyUnicode_CheckExact(name) && \
3938
PyUnicode_IS_READY(name) && \
@@ -338,6 +337,7 @@ PyType_Modified(PyTypeObject *type)
338337
}
339338
}
340339
type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
340+
type->tp_version_tag = 0; /* 0 is not a valid version tag */
341341
}
342342

343343
static void
@@ -396,6 +396,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
396396
Py_XDECREF(type_mro_meth);
397397
type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG|
398398
Py_TPFLAGS_VALID_VERSION_TAG);
399+
type->tp_version_tag = 0; /* 0 is not a valid version tag */
399400
}
400401

401402
static int
@@ -3351,18 +3352,15 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
33513352
PyObject *res;
33523353
int error;
33533354

3354-
if (MCACHE_CACHEABLE_NAME(name) &&
3355-
_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
3356-
/* fast path */
3357-
unsigned int h = MCACHE_HASH_METHOD(type, name);
3358-
struct type_cache *cache = get_type_cache();
3359-
struct type_cache_entry *entry = &cache->hashtable[h];
3360-
if (entry->version == type->tp_version_tag && entry->name == name) {
3355+
unsigned int h = MCACHE_HASH_METHOD(type, name);
3356+
struct type_cache *cache = get_type_cache();
3357+
struct type_cache_entry *entry = &cache->hashtable[h];
3358+
if (entry->version == type->tp_version_tag &&
3359+
entry->name == name) {
33613360
#if MCACHE_STATS
3362-
cache->hits++;
3361+
cache->hits++;
33633362
#endif
3364-
return entry->value;
3365-
}
3363+
return entry->value;
33663364
}
33673365

33683366
/* We may end up clearing live exceptions below, so make sure it's ours. */
@@ -3385,24 +3383,21 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
33853383
return NULL;
33863384
}
33873385

3388-
if (MCACHE_CACHEABLE_NAME(name)) {
3389-
struct type_cache *cache = get_type_cache();
3390-
if (assign_version_tag(cache, type)) {
3391-
unsigned int h = MCACHE_HASH_METHOD(type, name);
3392-
struct type_cache_entry *entry = &cache->hashtable[h];
3393-
entry->version = type->tp_version_tag;
3394-
entry->value = res; /* borrowed */
3395-
assert(((PyASCIIObject *)(name))->hash != -1);
3386+
if (MCACHE_CACHEABLE_NAME(name) && assign_version_tag(cache, type)) {
3387+
h = MCACHE_HASH_METHOD(type, name);
3388+
struct type_cache_entry *entry = &cache->hashtable[h];
3389+
entry->version = type->tp_version_tag;
3390+
entry->value = res; /* borrowed */
3391+
assert(((PyASCIIObject *)(name))->hash != -1);
33963392
#if MCACHE_STATS
3397-
if (entry->name != Py_None && entry->name != name) {
3398-
cache->collisions++;
3399-
}
3400-
else {
3401-
cache->misses++;
3402-
}
3403-
#endif
3404-
Py_SETREF(entry->name, Py_NewRef(name));
3393+
if (entry->name != Py_None && entry->name != name) {
3394+
cache->collisions++;
34053395
}
3396+
else {
3397+
cache->misses++;
3398+
}
3399+
#endif
3400+
Py_SETREF(entry->name, Py_NewRef(name));
34063401
}
34073402
return res;
34083403
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy