Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2a40e36

Browse files
committed
Issue #22847: Improve method cache efficiency.
1 parent 59f0682 commit 2a40e36

2 files changed

Lines changed: 38 additions & 4 deletions

File tree

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Release date: TBA
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #22847: Improve method cache efficiency.
14+
1315
- Issue #22335: Fix crash when trying to enlarge a bytearray to 0x7fffffff
1416
bytes on a 32-bit platform.
1517

Objects/typeobject.c

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
MCACHE_MAX_ATTR_SIZE, since it might be a problem if very large
1515
strings are used as attribute names. */
1616
#define MCACHE_MAX_ATTR_SIZE 100
17-
#define MCACHE_SIZE_EXP 9
17+
#define MCACHE_SIZE_EXP 12
1818
#define MCACHE_HASH(version, name_hash) \
19-
(((unsigned int)(version) * (unsigned int)(name_hash)) \
20-
>> (8*sizeof(unsigned int) - MCACHE_SIZE_EXP))
19+
(((unsigned int)(version) ^ (unsigned int)(name_hash)) \
20+
& ((1 << MCACHE_SIZE_EXP) - 1))
21+
2122
#define MCACHE_HASH_METHOD(type, name) \
2223
MCACHE_HASH((type)->tp_version_tag, \
2324
((PyASCIIObject *)(name))->hash)
@@ -35,6 +36,14 @@ struct method_cache_entry {
3536
static struct method_cache_entry method_cache[1 << MCACHE_SIZE_EXP];
3637
static unsigned int next_version_tag = 0;
3738

39+
#define MCACHE_STATS 0
40+
41+
#if MCACHE_STATS
42+
static size_t method_cache_hits = 0;
43+
static size_t method_cache_misses = 0;
44+
static size_t method_cache_collisions = 0;
45+
#endif
46+
3847
/* alphabetical order */
3948
_Py_IDENTIFIER(__abstractmethods__);
4049
_Py_IDENTIFIER(__class__);
@@ -165,6 +174,18 @@ PyType_ClearCache(void)
165174
Py_ssize_t i;
166175
unsigned int cur_version_tag = next_version_tag - 1;
167176

177+
#if MCACHE_STATS
178+
size_t total = method_cache_hits + method_cache_collisions + method_cache_misses;
179+
fprintf(stderr, "-- Method cache hits = %zd (%d%%)\n",
180+
method_cache_hits, (int) (100.0 * method_cache_hits / total));
181+
fprintf(stderr, "-- Method cache true misses = %zd (%d%%)\n",
182+
method_cache_misses, (int) (100.0 * method_cache_misses / total));
183+
fprintf(stderr, "-- Method cache collisions = %zd (%d%%)\n",
184+
method_cache_collisions, (int) (100.0 * method_cache_collisions / total));
185+
fprintf(stderr, "-- Method cache size = %zd KB\n",
186+
sizeof(method_cache) / 1024);
187+
#endif
188+
168189
for (i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
169190
method_cache[i].version = 0;
170191
Py_CLEAR(method_cache[i].name);
@@ -2708,8 +2729,12 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
27082729
/* fast path */
27092730
h = MCACHE_HASH_METHOD(type, name);
27102731
if (method_cache[h].version == type->tp_version_tag &&
2711-
method_cache[h].name == name)
2732+
method_cache[h].name == name) {
2733+
#if MCACHE_STATS
2734+
method_cache_hits++;
2735+
#endif
27122736
return method_cache[h].value;
2737+
}
27132738
}
27142739

27152740
/* Look in tp_dict of types in MRO */
@@ -2743,6 +2768,13 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
27432768
method_cache[h].version = type->tp_version_tag;
27442769
method_cache[h].value = res; /* borrowed */
27452770
Py_INCREF(name);
2771+
assert(((PyASCIIObject *)(name))->hash != -1);
2772+
#if MCACHE_STATS
2773+
if (method_cache[h].name != Py_None && method_cache[h].name != name)
2774+
method_cache_collisions++;
2775+
else
2776+
method_cache_misses++;
2777+
#endif
27462778
Py_DECREF(method_cache[h].name);
27472779
method_cache[h].name = name;
27482780
}

0 commit comments

Comments
 (0)