Skip to content

Commit

Permalink
Add a bit vector to optimize watcher dispatch
Browse files Browse the repository at this point in the history
A bit is set in the bit vector iff there is a watcher set at the
corresponding offset in the watcher array. Only notify watchers
if at least one bit is set.
  • Loading branch information
mpage committed Nov 22, 2022
1 parent dd41468 commit 812dd5f
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 2 deletions.
2 changes: 2 additions & 0 deletions Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ struct _is {

PyDict_WatchCallback dict_watchers[DICT_MAX_WATCHERS];
PyFunction_WatchCallback func_watchers[FUNC_MAX_WATCHERS];
// One bit is set for each non-NULL entry in func_watchers
uint8_t active_func_watchers;

Py_ssize_t co_extra_user_count;
freefunc co_extra_freefuncs[MAX_CO_EXTRA_USERS];
Expand Down
16 changes: 14 additions & 2 deletions Objects/funcobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
#include "structmember.h" // PyMemberDef

static void
handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func, PyObject *new_value)
notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
PyFunctionObject *func, PyObject *new_value)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
for (int i = 0; i < FUNC_MAX_WATCHERS; i++) {
PyFunction_WatchCallback cb = interp->func_watchers[i];
if ((cb != NULL) && (cb(event, func, new_value) < 0)) {
Expand All @@ -20,6 +20,16 @@ handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func, PyObject
}
}

static inline void
handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func,
PyObject *new_value)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
if (interp->active_func_watchers) {
notify_func_watchers(interp, event, func, new_value);
}
}

int
PyFunction_AddWatcher(PyFunction_WatchCallback callback)
{
Expand All @@ -28,6 +38,7 @@ PyFunction_AddWatcher(PyFunction_WatchCallback callback)
for (int i = 0; i < FUNC_MAX_WATCHERS; i++) {
if (interp->func_watchers[i] == NULL) {
interp->func_watchers[i] = callback;
interp->active_func_watchers |= (1 << i);
return i;
}
}
Expand All @@ -50,6 +61,7 @@ PyFunction_ClearWatcher(int watcher_id)
return -1;
}
interp->func_watchers[watcher_id] = NULL;
interp->active_func_watchers &= ~(1 << watcher_id);
return 0;
}

Expand Down
1 change: 1 addition & 0 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ interpreter_clear(PyInterpreterState *interp, PyThreadState *tstate)
for (int i=0; i < FUNC_MAX_WATCHERS; i++) {
interp->func_watchers[i] = NULL;
}
interp->active_func_watchers = 0;

// XXX Once we have one allocator per interpreter (i.e.
// per-interpreter GC) we must ensure that all of the interpreter's
Expand Down

0 comments on commit 812dd5f

Please sign in to comment.