mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
GH-113710: Fix updating of dict version tag and add watched dict stats (GH-115221)
This commit is contained in:
parent
93ac78ac3e
commit
8144661017
6 changed files with 22 additions and 23 deletions
|
@ -133,6 +133,9 @@ typedef struct _rare_event_stats {
|
||||||
uint64_t builtin_dict;
|
uint64_t builtin_dict;
|
||||||
/* Modifying a function, e.g. func.__defaults__ = ..., etc. */
|
/* Modifying a function, e.g. func.__defaults__ = ..., etc. */
|
||||||
uint64_t func_modification;
|
uint64_t func_modification;
|
||||||
|
/* Modifying a dict that is being watched */
|
||||||
|
uint64_t watched_dict_modification;
|
||||||
|
uint64_t watched_globals_modification;
|
||||||
} RareEventStats;
|
} RareEventStats;
|
||||||
|
|
||||||
typedef struct _stats {
|
typedef struct _stats {
|
||||||
|
|
|
@ -209,6 +209,7 @@ static inline PyDictUnicodeEntry* DK_UNICODE_ENTRIES(PyDictKeysObject *dk) {
|
||||||
|
|
||||||
#define DICT_VERSION_INCREMENT (1 << (DICT_MAX_WATCHERS + DICT_WATCHED_MUTATION_BITS))
|
#define DICT_VERSION_INCREMENT (1 << (DICT_MAX_WATCHERS + DICT_WATCHED_MUTATION_BITS))
|
||||||
#define DICT_WATCHER_MASK ((1 << DICT_MAX_WATCHERS) - 1)
|
#define DICT_WATCHER_MASK ((1 << DICT_MAX_WATCHERS) - 1)
|
||||||
|
#define DICT_WATCHER_AND_MODIFICATION_MASK ((1 << (DICT_MAX_WATCHERS + DICT_WATCHED_MUTATION_BITS)) - 1)
|
||||||
|
|
||||||
#ifdef Py_GIL_DISABLED
|
#ifdef Py_GIL_DISABLED
|
||||||
#define DICT_NEXT_VERSION(INTERP) \
|
#define DICT_NEXT_VERSION(INTERP) \
|
||||||
|
@ -236,10 +237,10 @@ _PyDict_NotifyEvent(PyInterpreterState *interp,
|
||||||
assert(Py_REFCNT((PyObject*)mp) > 0);
|
assert(Py_REFCNT((PyObject*)mp) > 0);
|
||||||
int watcher_bits = mp->ma_version_tag & DICT_WATCHER_MASK;
|
int watcher_bits = mp->ma_version_tag & DICT_WATCHER_MASK;
|
||||||
if (watcher_bits) {
|
if (watcher_bits) {
|
||||||
|
RARE_EVENT_STAT_INC(watched_dict_modification);
|
||||||
_PyDict_SendEvent(watcher_bits, event, mp, key, value);
|
_PyDict_SendEvent(watcher_bits, event, mp, key, value);
|
||||||
return DICT_NEXT_VERSION(interp) | watcher_bits;
|
|
||||||
}
|
}
|
||||||
return DICT_NEXT_VERSION(interp);
|
return DICT_NEXT_VERSION(interp) | (mp->ma_version_tag & DICT_WATCHER_AND_MODIFICATION_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern PyObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values);
|
extern PyObject *_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values);
|
||||||
|
|
|
@ -28,25 +28,23 @@ increment_mutations(PyObject* dict) {
|
||||||
d->ma_version_tag += (1 << DICT_MAX_WATCHERS);
|
d->ma_version_tag += (1 << DICT_MAX_WATCHERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The first two dict watcher IDs are reserved for CPython,
|
||||||
|
* so we don't need to check that they haven't been used */
|
||||||
|
#define BUILTINS_WATCHER_ID 0
|
||||||
|
#define GLOBALS_WATCHER_ID 1
|
||||||
|
|
||||||
static int
|
static int
|
||||||
globals_watcher_callback(PyDict_WatchEvent event, PyObject* dict,
|
globals_watcher_callback(PyDict_WatchEvent event, PyObject* dict,
|
||||||
PyObject* key, PyObject* new_value)
|
PyObject* key, PyObject* new_value)
|
||||||
{
|
{
|
||||||
if (event == PyDict_EVENT_CLONED) {
|
RARE_EVENT_STAT_INC(watched_globals_modification);
|
||||||
return 0;
|
assert(get_mutations(dict) < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS);
|
||||||
}
|
|
||||||
uint64_t watched_mutations = get_mutations(dict);
|
|
||||||
if (watched_mutations < _Py_MAX_ALLOWED_GLOBALS_MODIFICATIONS) {
|
|
||||||
_Py_Executors_InvalidateDependency(_PyInterpreterState_GET(), dict);
|
_Py_Executors_InvalidateDependency(_PyInterpreterState_GET(), dict);
|
||||||
increment_mutations(dict);
|
increment_mutations(dict);
|
||||||
}
|
PyDict_Unwatch(GLOBALS_WATCHER_ID, dict);
|
||||||
else {
|
|
||||||
PyDict_Unwatch(1, dict);
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
global_to_const(_PyUOpInstruction *inst, PyObject *obj)
|
global_to_const(_PyUOpInstruction *inst, PyObject *obj)
|
||||||
{
|
{
|
||||||
|
@ -82,11 +80,6 @@ incorrect_keys(_PyUOpInstruction *inst, PyObject *obj)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The first two dict watcher IDs are reserved for CPython,
|
|
||||||
* so we don't need to check that they haven't been used */
|
|
||||||
#define BUILTINS_WATCHER_ID 0
|
|
||||||
#define GLOBALS_WATCHER_ID 1
|
|
||||||
|
|
||||||
/* Returns 1 if successfully optimized
|
/* Returns 1 if successfully optimized
|
||||||
* 0 if the trace is not suitable for optimization (yet)
|
* 0 if the trace is not suitable for optimization (yet)
|
||||||
* -1 if there was an error. */
|
* -1 if there was an error. */
|
||||||
|
@ -117,8 +110,8 @@ remove_globals(_PyInterpreterFrame *frame, _PyUOpInstruction *buffer,
|
||||||
uint32_t builtins_watched = 0;
|
uint32_t builtins_watched = 0;
|
||||||
uint32_t globals_checked = 0;
|
uint32_t globals_checked = 0;
|
||||||
uint32_t globals_watched = 0;
|
uint32_t globals_watched = 0;
|
||||||
if (interp->dict_state.watchers[1] == NULL) {
|
if (interp->dict_state.watchers[GLOBALS_WATCHER_ID] == NULL) {
|
||||||
interp->dict_state.watchers[1] = globals_watcher_callback;
|
interp->dict_state.watchers[GLOBALS_WATCHER_ID] = globals_watcher_callback;
|
||||||
}
|
}
|
||||||
for (int pc = 0; pc < buffer_size; pc++) {
|
for (int pc = 0; pc < buffer_size; pc++) {
|
||||||
_PyUOpInstruction *inst = &buffer[pc];
|
_PyUOpInstruction *inst = &buffer[pc];
|
||||||
|
|
|
@ -611,7 +611,7 @@ static int
|
||||||
builtins_dict_watcher(PyDict_WatchEvent event, PyObject *dict, PyObject *key, PyObject *new_value)
|
builtins_dict_watcher(PyDict_WatchEvent event, PyObject *dict, PyObject *key, PyObject *new_value)
|
||||||
{
|
{
|
||||||
PyInterpreterState *interp = _PyInterpreterState_GET();
|
PyInterpreterState *interp = _PyInterpreterState_GET();
|
||||||
if (event != PyDict_EVENT_CLONED && interp->rare_events.builtin_dict < _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) {
|
if (interp->rare_events.builtin_dict < _Py_MAX_ALLOWED_BUILTINS_MODIFICATIONS) {
|
||||||
_Py_Executors_InvalidateAll(interp);
|
_Py_Executors_InvalidateAll(interp);
|
||||||
}
|
}
|
||||||
RARE_EVENT_INTERP_INC(interp, builtin_dict);
|
RARE_EVENT_INTERP_INC(interp, builtin_dict);
|
||||||
|
|
|
@ -275,6 +275,8 @@ print_rare_event_stats(FILE *out, RareEventStats *stats)
|
||||||
fprintf(out, "Rare event (set_eval_frame_func): %" PRIu64 "\n", stats->set_eval_frame_func);
|
fprintf(out, "Rare event (set_eval_frame_func): %" PRIu64 "\n", stats->set_eval_frame_func);
|
||||||
fprintf(out, "Rare event (builtin_dict): %" PRIu64 "\n", stats->builtin_dict);
|
fprintf(out, "Rare event (builtin_dict): %" PRIu64 "\n", stats->builtin_dict);
|
||||||
fprintf(out, "Rare event (func_modification): %" PRIu64 "\n", stats->func_modification);
|
fprintf(out, "Rare event (func_modification): %" PRIu64 "\n", stats->func_modification);
|
||||||
|
fprintf(out, "Rare event (watched_dict_modification): %" PRIu64 "\n", stats->watched_dict_modification);
|
||||||
|
fprintf(out, "Rare event (watched_globals_modification): %" PRIu64 "\n", stats->watched_globals_modification);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -415,7 +415,7 @@ class Stats:
|
||||||
def get_rare_events(self) -> list[tuple[str, int]]:
|
def get_rare_events(self) -> list[tuple[str, int]]:
|
||||||
prefix = "Rare event "
|
prefix = "Rare event "
|
||||||
return [
|
return [
|
||||||
(key[len(prefix) + 1:-1], val)
|
(key[len(prefix) + 1:-1].replace("_", " "), val)
|
||||||
for key, val in self._data.items()
|
for key, val in self._data.items()
|
||||||
if key.startswith(prefix)
|
if key.startswith(prefix)
|
||||||
]
|
]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue