Skip to content

Commit

Permalink
gh-100344: Provide C implementation for asyncio.current_task
Browse files Browse the repository at this point in the history
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
```
  • Loading branch information
itamaro committed Dec 19, 2022
1 parent 702a5bc commit 76cbbc6
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 2 deletions.
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.12.rst
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ asyncio
a custom event loop factory.
(Contributed by Kumar Aditya in :gh:`99388`.)

* Add C implementation of :func:`asyncio.current_task` for 4x-6x speedup.
(Contributed by Itamar Ostricher and Pranav Thulasiram Bhat in :gh:`100344`.)

inspect
-------

Expand Down
5 changes: 4 additions & 1 deletion Lib/asyncio/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,7 @@ def _unregister_task(task):
_all_tasks.discard(task)


_py_current_task = current_task
_py_register_task = _register_task
_py_unregister_task = _unregister_task
_py_enter_task = _enter_task
Expand All @@ -973,10 +974,12 @@ def _unregister_task(task):
try:
from _asyncio import (_register_task, _unregister_task,
_enter_task, _leave_task,
_all_tasks, _current_tasks)
_all_tasks, _current_tasks,
current_task)
except ImportError:
pass
else:
_c_current_task = current_task
_c_register_task = _register_task
_c_unregister_task = _unregister_task
_c_enter_task = _enter_task
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Provide C implementation for :func:`asyncio.current_task` for a 4x-6x
speedup.
39 changes: 39 additions & 0 deletions Modules/_asynciomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3344,6 +3344,44 @@ _asyncio__leave_task_impl(PyObject *module, PyObject *loop, PyObject *task)
}


/*[clinic input]
_asyncio.current_task
loop: object = None
Return a currently executed task.
[clinic start generated code]*/

static PyObject *
_asyncio_current_task_impl(PyObject *module, PyObject *loop)
/*[clinic end generated code: output=fe15ac331a7f981a input=58910f61a5627112]*/
{
PyObject *ret;
asyncio_state *state = get_asyncio_state(module);

if (loop == Py_None) {
loop = _asyncio_get_running_loop_impl(module);
}

if (loop == NULL) {
return NULL;
}

ret = PyDict_GetItemWithError(state->current_tasks, loop);
if (ret == NULL && PyErr_Occurred()) {
return NULL;
}
else if (ret == NULL) {
Py_RETURN_NONE;
}
else {
Py_INCREF(ret);
return ret;
}
}


/*********************** PyRunningLoopHolder ********************/


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

static PyMethodDef asyncio_methods[] = {
_ASYNCIO_CURRENT_TASK_METHODDEF
_ASYNCIO_GET_EVENT_LOOP_METHODDEF
_ASYNCIO_GET_RUNNING_LOOP_METHODDEF
_ASYNCIO__GET_RUNNING_LOOP_METHODDEF
Expand Down
62 changes: 61 additions & 1 deletion Modules/clinic/_asynciomodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 76cbbc6

Please sign in to comment.