当我将一个生产系统迁移到异步时,我发现同步版本比异步版本快了20倍。我能够创建一个非常简单的示例以可重复的方式演示这一点;
异步版本
import asyncio, time
data = {}
async def process_usage(key):
data[key] = key
async def main():
await asyncio.gather(*(process_usage(key) for key in range(0,1000000)))
s = time.perf_counter()
results = asyncio.run(main())
elapsed = time.perf_counter() - s
print(f"Took {elapsed:0.2f} seconds.")
这需要19秒钟。代码通过1M个键循环并构建一个字典,data
,其具有相同的键和值。
$ python3.7 async_test.py
Took 19.08 seconds.
同步版本
import time
data = {}
def process_usage(key):
data[key] = key
def main():
for key in range(0,1000000):
process_usage(key)
s = time.perf_counter()
results = main()
elapsed = time.perf_counter() - s
print(f"Took {elapsed:0.2f} seconds.")
这需要0.17秒!并且与上面的做法完全相同。
$ python3.7 test.py
Took 0.17 seconds.
使用 create_task
实现异步版本
import asyncio, time
data = {}
async def process_usage(key):
data[key] = key
async def main():
for key in range(0,1000000):
asyncio.create_task(process_usage(key))
s = time.perf_counter()
results = asyncio.run(main())
elapsed = time.perf_counter() - s
print(f"Took {elapsed:0.2f} seconds.")
此版本将其降至11秒。
$ python3.7 async_test2.py
Took 11.91 seconds.
为什么会发生这种情况?
在我的生产代码中,我将在process_usage
函数中进行一个阻塞式调用,将键的值保存到Redis数据库中。
range()
迭代器。 - Kyle Willmoncreate_task
的示例,将其降至11秒。目前这是最佳选择。但是我的脚本大量使用redis,我想在process_usage
中使用aioredis,但如果它不是异步的,我就无法这样做。 - Jonathan