@@ -186,6 +186,16 @@ typedef struct {
186186 /* Counter for autogenerated Task names */
187187 uint64_t task_name_counter ;
188188
189+ /* Internal benchmarking counters for asyncio task hot paths. */
190+ uint64_t bench_call_soon_calls ;
191+ uint64_t bench_task_init_calls ;
192+ uint64_t bench_task_call_step_soon_calls ;
193+ uint64_t bench_task_call_step_soon_with_arg_calls ;
194+ uint64_t bench_register_task_calls ;
195+ uint64_t bench_future_schedule_callbacks_calls ;
196+ uint64_t bench_future_schedule_callback0_calls ;
197+ uint64_t bench_future_schedule_callback_list_items ;
198+
189199 /* Pointer to the asyncio debug offset to avoid it to be optimized away
190200 by the compiler */
191201 void * debug_offsets ;
@@ -359,6 +369,7 @@ static int
359369call_soon (asyncio_state * state , PyObject * loop , PyObject * func , PyObject * arg ,
360370 PyObject * ctx )
361371{
372+ state -> bench_call_soon_calls ++ ;
362373 PyObject * handle ;
363374
364375 if (ctx == NULL ) {
@@ -423,6 +434,7 @@ static int
423434future_schedule_callbacks (asyncio_state * state , FutureObj * fut )
424435{
425436 _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (fut );
437+ state -> bench_future_schedule_callbacks_calls ++ ;
426438
427439 assert (fut -> fut_state != STATE_PENDING );
428440
@@ -434,6 +446,7 @@ future_schedule_callbacks(asyncio_state *state, FutureObj *fut)
434446 }
435447
436448 if (fut -> fut_callback0 != NULL ) {
449+ state -> bench_future_schedule_callback0_calls ++ ;
437450 /* There's a 1st callback */
438451
439452 // Beware: An evil call_soon could alter fut_callback0 or fut_context0.
@@ -472,6 +485,7 @@ future_schedule_callbacks(asyncio_state *state, FutureObj *fut)
472485 PyObject * callbacks = fut -> fut_callbacks ;
473486 fut -> fut_callbacks = NULL ;
474487 Py_ssize_t n = PyList_GET_SIZE (callbacks );
488+ state -> bench_future_schedule_callback_list_items += (uint64_t )n ;
475489 for (Py_ssize_t i = 0 ; i < n ; i ++ ) {
476490 assert (PyList_GET_SIZE (callbacks ) == n );
477491 PyObject * cb_tup = PyList_GET_ITEM (callbacks , i );
@@ -2183,6 +2197,8 @@ register_task(_PyThreadStateImpl *ts, TaskObj *task)
21832197 return ;
21842198 }
21852199 struct llist_node * head = & ts -> asyncio_tasks_head ;
2200+ asyncio_state * state = get_asyncio_state_by_def ((PyObject * )task );
2201+ state -> bench_register_task_calls ++ ;
21862202 llist_insert_tail (head , & task -> task_node );
21872203}
21882204
@@ -2308,6 +2324,7 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop,
23082324 self -> task_is_task = 1 ;
23092325
23102326 asyncio_state * state = get_asyncio_state_by_def ((PyObject * )self );
2327+ state -> bench_task_init_calls ++ ;
23112328 int is_coro = is_coroutine (state , coro );
23122329 if (is_coro == -1 ) {
23132330 return -1 ;
@@ -2983,6 +3000,10 @@ TaskObj_dealloc(PyObject *self)
29833000static int
29843001task_call_step_soon (asyncio_state * state , TaskObj * task , PyObject * arg )
29853002{
3003+ state -> bench_task_call_step_soon_calls ++ ;
3004+ if (arg != NULL ) {
3005+ state -> bench_task_call_step_soon_with_arg_calls ++ ;
3006+ }
29863007 PyObject * cb = TaskStepMethWrapper_new (task , arg );
29873008 if (cb == NULL ) {
29883009 return -1 ;
@@ -4327,6 +4348,58 @@ module_init(asyncio_state *state)
43274348
43284349PyDoc_STRVAR (module_doc , "Accelerator module for asyncio" );
43294350
4351+ static PyObject *
4352+ _asyncio__get_benchmark_counters (PyObject * module , PyObject * Py_UNUSED (ignored ))
4353+ {
4354+ asyncio_state * state = get_asyncio_state (module );
4355+ PyObject * counters = PyDict_New ();
4356+ if (counters == NULL ) {
4357+ return NULL ;
4358+ }
4359+
4360+ #define SET_COUNTER (name ) \
4361+ do { \
4362+ PyObject *value = PyLong_FromUnsignedLongLong(state->name); \
4363+ if (value == NULL) { \
4364+ Py_DECREF(counters); \
4365+ return NULL; \
4366+ } \
4367+ if (PyDict_SetItemString(counters, #name, value) < 0) { \
4368+ Py_DECREF(value); \
4369+ Py_DECREF(counters); \
4370+ return NULL; \
4371+ } \
4372+ Py_DECREF(value); \
4373+ } while (0)
4374+
4375+ SET_COUNTER (bench_call_soon_calls );
4376+ SET_COUNTER (bench_task_init_calls );
4377+ SET_COUNTER (bench_task_call_step_soon_calls );
4378+ SET_COUNTER (bench_task_call_step_soon_with_arg_calls );
4379+ SET_COUNTER (bench_register_task_calls );
4380+ SET_COUNTER (bench_future_schedule_callbacks_calls );
4381+ SET_COUNTER (bench_future_schedule_callback0_calls );
4382+ SET_COUNTER (bench_future_schedule_callback_list_items );
4383+
4384+ #undef SET_COUNTER
4385+ return counters ;
4386+ }
4387+
4388+ static PyObject *
4389+ _asyncio__reset_benchmark_counters (PyObject * module , PyObject * Py_UNUSED (ignored ))
4390+ {
4391+ asyncio_state * state = get_asyncio_state (module );
4392+ state -> bench_call_soon_calls = 0 ;
4393+ state -> bench_task_init_calls = 0 ;
4394+ state -> bench_task_call_step_soon_calls = 0 ;
4395+ state -> bench_task_call_step_soon_with_arg_calls = 0 ;
4396+ state -> bench_register_task_calls = 0 ;
4397+ state -> bench_future_schedule_callbacks_calls = 0 ;
4398+ state -> bench_future_schedule_callback0_calls = 0 ;
4399+ state -> bench_future_schedule_callback_list_items = 0 ;
4400+ Py_RETURN_NONE ;
4401+ }
4402+
43304403static PyMethodDef asyncio_methods [] = {
43314404 _ASYNCIO_CURRENT_TASK_METHODDEF
43324405 _ASYNCIO_GET_EVENT_LOOP_METHODDEF
@@ -4343,6 +4416,8 @@ static PyMethodDef asyncio_methods[] = {
43434416 _ASYNCIO_ALL_TASKS_METHODDEF
43444417 _ASYNCIO_FUTURE_ADD_TO_AWAITED_BY_METHODDEF
43454418 _ASYNCIO_FUTURE_DISCARD_FROM_AWAITED_BY_METHODDEF
4419+ {"_get_benchmark_counters" , _asyncio__get_benchmark_counters , METH_NOARGS , NULL },
4420+ {"_reset_benchmark_counters" , _asyncio__reset_benchmark_counters , METH_NOARGS , NULL },
43464421 {NULL , NULL }
43474422};
43484423
0 commit comments