Reputation: 11
# _*_ coding: utf-8 _*_
import asyncio
from pprint import pprint
import time
async def add(a, b, c): return a+b+c
async def concat(a, b): return a+b
async def even(l): return [x for x in l if not x%2]
async def split(s): return s.split()
async def int_list(s): return [int(c) for c in s]
d = { add:(3,4,5),
concat:('Lill', 'Klas'),
even:[[1,2,3,4,5,6,7,8,9]],
split:['ett två tre fyra'],
int_list:['12345']
}
async def run(dic):
return {k.__name__:await asyncio.gather(k(*v,)) for k, v in dic.items()}
start=time.perf_counter()
res = asyncio.run(run(d))
end=time.perf_counter()
pprint(res)
print(end-start)
The code is running three times slower than without the async. And I can't figure out what I am doing wrong. I am running python 3.10.5.
no async: 0.00033 async 0.00098
Upvotes: 1
Views: 2247
Reputation: 155363
async
functions involve setting up a separate call stack for each of the tasks (similar to generator functions). The functions you've written are extremely lightweight, so the overhead of making them async is extreme.
It's unclear why you're even using asyncio
here; asyncio.gather
won't "parallelize" anything that doesn't ultimately devolve to low level I/O of some kind (it's not multithreading, so all it can do is schedule I/O and do other stuff while waiting for it; the best it can do for parallelism is wrap parallel task dispatch with run_in_executor
, which has its own overhead). Add-on the work to launch the event loop, create and queue the tasks, extract their results, and in a program with so little actual work to be done, the asyncio
overhead exceeds that of the real work. Practically speaking, what you've written is 100% synchronous, you just added a bunch of rigmarole to handle it as if it were asynchronous, without actually using any of those features.
In short: Don't use asyncio
when your code is not in any way asynchronous. Don't time execution of trivial amounts of code wrapped in expensive overhead that real programs never pay (e.g. repeatedly creating, running, tearing down, and destroying an event loop, which is what asyncio.run
does for you, or calling asyncio.gather
once per call, when gather
exists solely to simultaneously await
multiple tasks).
Upvotes: 2