Skip to content

Commit 76cbbc6

Browse files
committed
gh-100344: Provide C implementation for asyncio.current_task
Implementing it in C makes it about 4x-6x faster Microbenchmark: ``` # bench.py import time import timeit import asyncio ITERS: int = 10**6 NANO: int = 10**9 NANO_PER_ITER: float = NANO / ITERS async def main(): # avoid load attr noise py_current_task = asyncio.tasks._py_current_task c_current_task = asyncio.tasks._c_current_task asyncio.current_task() # warmup py_current_task() # warmup c_current_task() # warmup print( "current_task: {}ns".format(timeit.timeit(py_current_task, number=ITERS, timer=time.process_time) * NANO_PER_ITER) ) print( "current_task: {}ns".format(timeit.timeit(c_current_task, number=ITERS, timer=time.process_time) * NANO_PER_ITER) ) asyncio.run(main()) ``` a few runs on MacBook Pro 2.4 GHz 8-Core Intel Core i9 64 GB 2667 MHz DDR4: debug build: ``` ~/work/pyexe/main-dbg ⌚ 9:57:34 $ ./python.exe bench.py [py] current_task: 606.234ns [c] current_task: 104.47699999999993ns ~/work/pyexe/main-dbg ⌚ 9:57:59 $ ./python.exe bench.py [py] current_task: 631.856ns [c] current_task: 110.22500000000002ns ~/work/pyexe/main-dbg ⌚ 9:58:08 $ ./python.exe bench.py [py] current_task: 637.746ns [c] current_task: 105.03899999999999ns ~/work/pyexe/main-dbg ⌚ 9:58:16 $ ./python.exe bench.py [py] current_task: 621.3169999999999ns [c] current_task: 103.01300000000002ns ``` opt build: ``` ~/work/pyexe/main-opt ⌚ 10:33:17 $ ./python.exe bench.py [py] current_task: 128.743ns [c] current_task: 31.997999999999998ns ~/work/pyexe/main-opt ⌚ 10:33:24 $ ./python.exe bench.py [py] current_task: 126.388ns [c] current_task: 32.64599999999998ns ~/work/pyexe/main-opt ⌚ 10:33:26 $ ./python.exe bench.py [py] current_task: 137.053ns [c] current_task: 32.066999999999986ns ~/work/pyexe/main-opt ⌚ 10:33:28 $ ./python.exe bench.py [py] current_task: 131.17800000000003ns [c] current_task: 32.06600000000001ns ```
1 parent 702a5bc commit 76cbbc6

File tree

5 files changed

+109
-2
lines changed

5 files changed

+109
-2
lines changed

Doc/whatsnew/3.12.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ asyncio
225225
a custom event loop factory.
226226
(Contributed by Kumar Aditya in :gh:`99388`.)
227227

228+
* Add C implementation of :func:`asyncio.current_task` for 4x-6x speedup.
229+
(Contributed by Itamar Ostricher and Pranav Thulasiram Bhat in :gh:`100344`.)
230+
228231
inspect
229232
-------
230233

Lib/asyncio/tasks.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,7 @@ def _unregister_task(task):
964964
_all_tasks.discard(task)
965965

966966

967+
_py_current_task = current_task
967968
_py_register_task = _register_task
968969
_py_unregister_task = _unregister_task
969970
_py_enter_task = _enter_task
@@ -973,10 +974,12 @@ def _unregister_task(task):
973974
try:
974975
from _asyncio import (_register_task, _unregister_task,
975976
_enter_task, _leave_task,
976-
_all_tasks, _current_tasks)
977+
_all_tasks, _current_tasks,
978+
current_task)
977979
except ImportError:
978980
pass
979981
else:
982+
_c_current_task = current_task
980983
_c_register_task = _register_task
981984
_c_unregister_task = _unregister_task
982985
_c_enter_task = _enter_task
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Provide C implementation for :func:`asyncio.current_task` for a 4x-6x
2+
speedup.

Modules/_asynciomodule.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3344,6 +3344,44 @@ _asyncio__leave_task_impl(PyObject *module, PyObject *loop, PyObject *task)
33443344
}
33453345

33463346

3347+
/*[clinic input]
3348+
_asyncio.current_task
3349+
3350+
loop: object = None
3351+
3352+
Return a currently executed task.
3353+
3354+
[clinic start generated code]*/
3355+
3356+
static PyObject *
3357+
_asyncio_current_task_impl(PyObject *module, PyObject *loop)
3358+
/*[clinic end generated code: output=fe15ac331a7f981a input=58910f61a5627112]*/
3359+
{
3360+
PyObject *ret;
3361+
asyncio_state *state = get_asyncio_state(module);
3362+
3363+
if (loop == Py_None) {
3364+
loop = _asyncio_get_running_loop_impl(module);
3365+
}
3366+
3367+
if (loop == NULL) {
3368+
return NULL;
3369+
}
3370+
3371+
ret = PyDict_GetItemWithError(state->current_tasks, loop);
3372+
if (ret == NULL && PyErr_Occurred()) {
3373+
return NULL;
3374+
}
3375+
else if (ret == NULL) {
3376+
Py_RETURN_NONE;
3377+
}
3378+
else {
3379+
Py_INCREF(ret);
3380+
return ret;
3381+
}
3382+
}
3383+
3384+
33473385
/*********************** PyRunningLoopHolder ********************/
33483386

33493387

@@ -3599,6 +3637,7 @@ module_init(asyncio_state *state)
35993637
PyDoc_STRVAR(module_doc, "Accelerator module for asyncio");
36003638

36013639
static PyMethodDef asyncio_methods[] = {
3640+
_ASYNCIO_CURRENT_TASK_METHODDEF
36023641
_ASYNCIO_GET_EVENT_LOOP_METHODDEF
36033642
_ASYNCIO_GET_RUNNING_LOOP_METHODDEF
36043643
_ASYNCIO__GET_RUNNING_LOOP_METHODDEF

Modules/clinic/_asynciomodule.c.h

Lines changed: 61 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)