summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/whatsnew/3.12.rst3
-rw-r--r--Lib/test/test_asyncio/test_tasks.py12
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2023-04-24-14-38-16.gh-issue-103793.kqoH6Q.rst3
-rw-r--r--Modules/_asynciomodule.c13
4 files changed, 29 insertions, 2 deletions
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index 908cf3b..f4ee30b 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -610,6 +610,9 @@ Optimizations
replacement strings containing group references by 2--3 times.
(Contributed by Serhiy Storchaka in :gh:`91524`.)
+* Speed up :class:`asyncio.Task` creation by deferring expensive string formatting.
+ (Contributed by Itamar O in :gh:`103793`.)
+
CPython bytecode changes
========================
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py
index 31622c9..6e8a51c 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -399,6 +399,18 @@ class BaseTaskTests:
self.loop.run_until_complete(t1)
self.loop.run_until_complete(t2)
+ def test_task_set_name_pylong(self):
+ # test that setting the task name to a PyLong explicitly doesn't
+ # incorrectly trigger the deferred name formatting logic
+ async def notmuch():
+ return 123
+
+ t = self.new_task(self.loop, notmuch(), name=987654321)
+ self.assertEqual(t.get_name(), '987654321')
+ t.set_name(123456789)
+ self.assertEqual(t.get_name(), '123456789')
+ self.loop.run_until_complete(t)
+
def test_task_repr_name_not_str(self):
async def notmuch():
return 123
diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-04-24-14-38-16.gh-issue-103793.kqoH6Q.rst b/Misc/NEWS.d/next/Core and Builtins/2023-04-24-14-38-16.gh-issue-103793.kqoH6Q.rst
new file mode 100644
index 0000000..c483487
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2023-04-24-14-38-16.gh-issue-103793.kqoH6Q.rst
@@ -0,0 +1,3 @@
+Optimized asyncio Task creation by deferring expensive string formatting
+(task name generation) from Task creation to the first time ``get_name`` is
+called. This makes asyncio benchmarks up to 5% faster.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 2476dca..82dbc08 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -2069,8 +2069,10 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop,
Py_XSETREF(self->task_coro, coro);
if (name == Py_None) {
- name = PyUnicode_FromFormat("Task-%" PRIu64,
- ++state->task_name_counter);
+ // optimization: defer task name formatting
+ // store the task counter as PyLong in the name
+ // for deferred formatting in get_name
+ name = PyLong_FromUnsignedLongLong(++state->task_name_counter);
} else if (!PyUnicode_CheckExact(name)) {
name = PyObject_Str(name);
} else {
@@ -2449,6 +2451,13 @@ _asyncio_Task_get_name_impl(TaskObj *self)
/*[clinic end generated code: output=0ecf1570c3b37a8f input=a4a6595d12f4f0f8]*/
{
if (self->task_name) {
+ if (PyLong_CheckExact(self->task_name)) {
+ PyObject *name = PyUnicode_FromFormat("Task-%S", self->task_name);
+ if (name == NULL) {
+ return NULL;
+ }
+ Py_SETREF(self->task_name, name);
+ }
return Py_NewRef(self->task_name);
}