mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +00:00 
			
		
		
		
	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.
This commit is contained in:
		
							parent
							
								
									2fd16ef406
								
							
						
					
					
						commit
						ee48c7d541
					
				
					 2 changed files with 24 additions and 28 deletions
				
			
		| 
						 | 
					@ -0,0 +1 @@
 | 
				
			||||||
 | 
					Added micro-optimizations to ``_PyType_Lookup()`` to improve cache lookup performance in the common case of cache hits.
 | 
				
			||||||
| 
						 | 
					@ -32,8 +32,7 @@ class object "PyObject *" "&PyBaseObject_Type"
 | 
				
			||||||
         & ((1 << MCACHE_SIZE_EXP) - 1))
 | 
					         & ((1 << MCACHE_SIZE_EXP) - 1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MCACHE_HASH_METHOD(type, name)                                  \
 | 
					#define MCACHE_HASH_METHOD(type, name)                                  \
 | 
				
			||||||
        MCACHE_HASH((type)->tp_version_tag,                     \
 | 
					    MCACHE_HASH((type)->tp_version_tag, ((Py_ssize_t)(name)) >> 3)
 | 
				
			||||||
                    ((PyASCIIObject *)(name))->hash)
 | 
					 | 
				
			||||||
#define MCACHE_CACHEABLE_NAME(name)                             \
 | 
					#define MCACHE_CACHEABLE_NAME(name)                             \
 | 
				
			||||||
        PyUnicode_CheckExact(name) &&                           \
 | 
					        PyUnicode_CheckExact(name) &&                           \
 | 
				
			||||||
        PyUnicode_IS_READY(name) &&                             \
 | 
					        PyUnicode_IS_READY(name) &&                             \
 | 
				
			||||||
| 
						 | 
					@ -338,6 +337,7 @@ PyType_Modified(PyTypeObject *type)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
 | 
					    type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
 | 
				
			||||||
 | 
					    type->tp_version_tag = 0; /* 0 is not a valid version tag */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					@ -396,6 +396,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
 | 
				
			||||||
    Py_XDECREF(type_mro_meth);
 | 
					    Py_XDECREF(type_mro_meth);
 | 
				
			||||||
    type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG|
 | 
					    type->tp_flags &= ~(Py_TPFLAGS_HAVE_VERSION_TAG|
 | 
				
			||||||
                        Py_TPFLAGS_VALID_VERSION_TAG);
 | 
					                        Py_TPFLAGS_VALID_VERSION_TAG);
 | 
				
			||||||
 | 
					    type->tp_version_tag = 0; /* 0 is not a valid version tag */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int
 | 
					static int
 | 
				
			||||||
| 
						 | 
					@ -3351,18 +3352,15 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
 | 
				
			||||||
    PyObject *res;
 | 
					    PyObject *res;
 | 
				
			||||||
    int error;
 | 
					    int error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (MCACHE_CACHEABLE_NAME(name) &&
 | 
					    unsigned int h = MCACHE_HASH_METHOD(type, name);
 | 
				
			||||||
        _PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
 | 
					    struct type_cache *cache = get_type_cache();
 | 
				
			||||||
        /* fast path */
 | 
					    struct type_cache_entry *entry = &cache->hashtable[h];
 | 
				
			||||||
        unsigned int h = MCACHE_HASH_METHOD(type, name);
 | 
					    if (entry->version == type->tp_version_tag &&
 | 
				
			||||||
        struct type_cache *cache = get_type_cache();
 | 
					        entry->name == name) {
 | 
				
			||||||
        struct type_cache_entry *entry = &cache->hashtable[h];
 | 
					 | 
				
			||||||
        if (entry->version == type->tp_version_tag && entry->name == name) {
 | 
					 | 
				
			||||||
#if MCACHE_STATS
 | 
					#if MCACHE_STATS
 | 
				
			||||||
            cache->hits++;
 | 
					        cache->hits++;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
            return entry->value;
 | 
					        return entry->value;
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* We may end up clearing live exceptions below, so make sure it's ours. */
 | 
					    /* We may end up clearing live exceptions below, so make sure it's ours. */
 | 
				
			||||||
| 
						 | 
					@ -3385,24 +3383,21 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
 | 
				
			||||||
        return NULL;
 | 
					        return NULL;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (MCACHE_CACHEABLE_NAME(name)) {
 | 
					    if (MCACHE_CACHEABLE_NAME(name) && assign_version_tag(cache, type)) {
 | 
				
			||||||
        struct type_cache *cache = get_type_cache();
 | 
					        h = MCACHE_HASH_METHOD(type, name);
 | 
				
			||||||
        if (assign_version_tag(cache, type)) {
 | 
					        struct type_cache_entry *entry = &cache->hashtable[h];
 | 
				
			||||||
            unsigned int h = MCACHE_HASH_METHOD(type, name);
 | 
					        entry->version = type->tp_version_tag;
 | 
				
			||||||
            struct type_cache_entry *entry = &cache->hashtable[h];
 | 
					        entry->value = res;  /* borrowed */
 | 
				
			||||||
            entry->version = type->tp_version_tag;
 | 
					        assert(((PyASCIIObject *)(name))->hash != -1);
 | 
				
			||||||
            entry->value = res;  /* borrowed */
 | 
					 | 
				
			||||||
            assert(((PyASCIIObject *)(name))->hash != -1);
 | 
					 | 
				
			||||||
#if MCACHE_STATS
 | 
					#if MCACHE_STATS
 | 
				
			||||||
            if (entry->name != Py_None && entry->name != name) {
 | 
					        if (entry->name != Py_None && entry->name != name) {
 | 
				
			||||||
                cache->collisions++;
 | 
					            cache->collisions++;
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else {
 | 
					 | 
				
			||||||
                cache->misses++;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
            Py_SETREF(entry->name, Py_NewRef(name));
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            cache->misses++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					        Py_SETREF(entry->name, Py_NewRef(name));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return res;
 | 
					    return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue