Skip to content

Commit 48e352a

Browse files
authoredDec 15, 2022
Move stats for the method cache into the Py_STAT machinery (GH-100255)
1 parent bdd8674 commit 48e352a

File tree

4 files changed

+35
-47
lines changed

4 files changed

+35
-47
lines changed
 

‎Include/internal/pycore_typeobject.h

-6
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,9 @@ struct type_cache_entry {
3636
};
3737

3838
#define MCACHE_SIZE_EXP 12
39-
#define MCACHE_STATS 0
4039

4140
struct type_cache {
4241
struct type_cache_entry hashtable[1 << MCACHE_SIZE_EXP];
43-
#if MCACHE_STATS
44-
size_t hits;
45-
size_t misses;
46-
size_t collisions;
47-
#endif
4842
};
4943

5044
/* For now we hard-code this to a value for which we are confident

‎Include/pystats.h

+7
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,15 @@ typedef struct _object_stats {
6565
uint64_t dict_materialized_new_key;
6666
uint64_t dict_materialized_too_big;
6767
uint64_t dict_materialized_str_subclass;
68+
uint64_t type_cache_hits;
69+
uint64_t type_cache_misses;
70+
uint64_t type_cache_dunder_hits;
71+
uint64_t type_cache_dunder_misses;
72+
uint64_t type_cache_collisions;
6873
} ObjectStats;
6974

75+
#
76+
7077
typedef struct _stats {
7178
OpcodeStats opcode_stats[256];
7279
CallStats call_stats;

‎Objects/typeobject.c

+23-41
Original file line numberDiff line numberDiff line change
@@ -327,18 +327,6 @@ static unsigned int
327327
_PyType_ClearCache(PyInterpreterState *interp)
328328
{
329329
struct type_cache *cache = &interp->types.type_cache;
330-
#if MCACHE_STATS
331-
size_t total = cache->hits + cache->collisions + cache->misses;
332-
fprintf(stderr, "-- Method cache hits = %zd (%d%%)\n",
333-
cache->hits, (int) (100.0 * cache->hits / total));
334-
fprintf(stderr, "-- Method cache true misses = %zd (%d%%)\n",
335-
cache->misses, (int) (100.0 * cache->misses / total));
336-
fprintf(stderr, "-- Method cache collisions = %zd (%d%%)\n",
337-
cache->collisions, (int) (100.0 * cache->collisions / total));
338-
fprintf(stderr, "-- Method cache size = %zd KiB\n",
339-
sizeof(cache->hashtable) / 1024);
340-
#endif
341-
342330
// Set to None, rather than NULL, so _PyType_Lookup() can
343331
// use Py_SETREF() rather than using slower Py_XSETREF().
344332
type_cache_clear(cache, Py_None);
@@ -4148,6 +4136,24 @@ find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
41484136
return res;
41494137
}
41504138

4139+
/* Check if the "readied" PyUnicode name
4140+
is a double-underscore special name. */
4141+
static int
4142+
is_dunder_name(PyObject *name)
4143+
{
4144+
Py_ssize_t length = PyUnicode_GET_LENGTH(name);
4145+
int kind = PyUnicode_KIND(name);
4146+
/* Special names contain at least "__x__" and are always ASCII. */
4147+
if (length > 4 && kind == PyUnicode_1BYTE_KIND) {
4148+
const Py_UCS1 *characters = PyUnicode_1BYTE_DATA(name);
4149+
return (
4150+
((characters[length-2] == '_') && (characters[length-1] == '_')) &&
4151+
((characters[0] == '_') && (characters[1] == '_'))
4152+
);
4153+
}
4154+
return 0;
4155+
}
4156+
41514157
/* Internal API to look for a name through the MRO.
41524158
This returns a borrowed reference, and doesn't set an exception! */
41534159
PyObject *
@@ -4161,12 +4167,13 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
41614167
struct type_cache_entry *entry = &cache->hashtable[h];
41624168
if (entry->version == type->tp_version_tag &&
41634169
entry->name == name) {
4164-
#if MCACHE_STATS
4165-
cache->hits++;
4166-
#endif
41674170
assert(_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG));
4171+
OBJECT_STAT_INC_COND(type_cache_hits, !is_dunder_name(name));
4172+
OBJECT_STAT_INC_COND(type_cache_dunder_hits, is_dunder_name(name));
41684173
return entry->value;
41694174
}
4175+
OBJECT_STAT_INC_COND(type_cache_misses, !is_dunder_name(name));
4176+
OBJECT_STAT_INC_COND(type_cache_dunder_misses, is_dunder_name(name));
41704177

41714178
/* We may end up clearing live exceptions below, so make sure it's ours. */
41724179
assert(!PyErr_Occurred());
@@ -4194,14 +4201,7 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
41944201
entry->version = type->tp_version_tag;
41954202
entry->value = res; /* borrowed */
41964203
assert(_PyASCIIObject_CAST(name)->hash != -1);
4197-
#if MCACHE_STATS
4198-
if (entry->name != Py_None && entry->name != name) {
4199-
cache->collisions++;
4200-
}
4201-
else {
4202-
cache->misses++;
4203-
}
4204-
#endif
4204+
OBJECT_STAT_INC_COND(type_cache_collisions, entry->name != Py_None && entry->name != name);
42054205
assert(_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG));
42064206
Py_SETREF(entry->name, Py_NewRef(name));
42074207
}
@@ -4218,24 +4218,6 @@ _PyType_LookupId(PyTypeObject *type, _Py_Identifier *name)
42184218
return _PyType_Lookup(type, oname);
42194219
}
42204220

4221-
/* Check if the "readied" PyUnicode name
4222-
is a double-underscore special name. */
4223-
static int
4224-
is_dunder_name(PyObject *name)
4225-
{
4226-
Py_ssize_t length = PyUnicode_GET_LENGTH(name);
4227-
int kind = PyUnicode_KIND(name);
4228-
/* Special names contain at least "__x__" and are always ASCII. */
4229-
if (length > 4 && kind == PyUnicode_1BYTE_KIND) {
4230-
const Py_UCS1 *characters = PyUnicode_1BYTE_DATA(name);
4231-
return (
4232-
((characters[length-2] == '_') && (characters[length-1] == '_')) &&
4233-
((characters[0] == '_') && (characters[1] == '_'))
4234-
);
4235-
}
4236-
return 0;
4237-
}
4238-
42394221
/* This is similar to PyObject_GenericGetAttr(),
42404222
but uses _PyType_Lookup() instead of just looking in type->tp_dict. */
42414223
static PyObject *

‎Python/specialize.c

+5
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,11 @@ print_object_stats(FILE *out, ObjectStats *stats)
187187
fprintf(out, "Object materialize dict (new key): %" PRIu64 "\n", stats->dict_materialized_new_key);
188188
fprintf(out, "Object materialize dict (too big): %" PRIu64 "\n", stats->dict_materialized_too_big);
189189
fprintf(out, "Object materialize dict (str subclass): %" PRIu64 "\n", stats->dict_materialized_str_subclass);
190+
fprintf(out, "Object method cache hits: %" PRIu64 "\n", stats->type_cache_hits);
191+
fprintf(out, "Object method cache misses: %" PRIu64 "\n", stats->type_cache_misses);
192+
fprintf(out, "Object method cache collisions: %" PRIu64 "\n", stats->type_cache_collisions);
193+
fprintf(out, "Object method cache dunder hits: %" PRIu64 "\n", stats->type_cache_dunder_hits);
194+
fprintf(out, "Object method cache dunder misses: %" PRIu64 "\n", stats->type_cache_dunder_misses);
190195
}
191196

192197
static void

0 commit comments

Comments
 (0)
Please sign in to comment.