Reduce redundant object creation while calling callback function from gc
| for (Py_ssize_t i=0; i<PyList_GET_SIZE(gcstate->callbacks); i++) { | |
| PyObject *r, *cb = PyList_GET_ITEM(gcstate->callbacks, i); | |
| Py_INCREF(cb); /* make sure cb doesn't go away */ | |
| r = PyObject_CallFunction(cb, "sO", phase, info); | |
| if (r == NULL) { | |
| PyErr_WriteUnraisable(cb); | |
| } | |
| else { | |
| Py_DECREF(r); | |
| } | |
| Py_DECREF(cb); | |
| } |
The phase object can be created earlier with one-time creation and it can be replaced with vectorcall either.
From my microbenchmark, there is 4-5% performance improvement by doing this.
microbenchmark
import pyperf import gc def benchamark_collection(loops): def callback_foo(phase, info): pass for _ in range(100): gc.callbacks.append(callback_foo) total_time = 0 for _ in range(loops): t0 = pyperf.perf_counter() collected = gc.collect() total_time += pyperf.perf_counter() - t0 return total_time if __name__ == "__main__": runner = pyperf.Runner() runner.metadata["description"] = "GC callback benchmark" runner.bench_time_func("create_gc_cycles", benchamark_collection)