每个人都知道python中的asyncio
模块在一个线程中计划所有coroutines。这意味着它可以帮助您更轻松地编码,并且无法从中获得任何性能。
但是Python的asyncio
模块的性能是什么?与传统的gevent
和本地epoll
相比,它可以运行多快?
要求
pip3 install hiredis gevent
如果在Linux上工作,也可以安装一个可选的uvloop:
pip3 install uvloop
资源
以下代码同时使用asyncio
和gevent
模拟端口5000、5001和5002上的redis服务器。
可以用redis-benchmark
进行测试。
echo_bench_gevent.py
的内容:
import sys
import gevent
import gevent.monkey
import hiredis
from gevent.server import StreamServer
gevent.monkey.patch_all()
d = {}
def process(req):
# only support get/set
cmd = req[0].lower()
if cmd == b'set':
d[req[1]] = req[2]
return b"+OK\r\n"
elif cmd == b'get':
v = d.get(req[1])
if v is None:
return b'$-1\r\n'
else:
return b'$1\r\n1\r\n'
else:
print(cmd)
raise NotImplementedError()
return b''
def handle(sock, addr):
reader = hiredis.Reader()
while True:
buf = sock.recv(4096)
if not buf:
return
reader.feed(buf)
while True:
req = reader.gets()
if not req:
break
sock.sendall(process(req))
return 0
print('serving on 0.0.0.0:5000')
server = StreamServer(('0.0.0.0', 5000), handle)
server.serve_forever()
echo_bench_asyncio.py
的内容:
import asyncio
import hiredis
d = {}
def process(req):
cmd = req[0].lower()
if cmd == b'set':
d[req[1]] = req[2]
return b"+OK\r\n"
elif cmd == b'get':
v = d.get(req[1])
if v is None:
return b'$-1\r\n'
else:
return b'$1\r\n1\r\n'
elif cmd == b'config':
return b'-ERROR\r\n'
else:
return b'-ERROR\r\n'
return b''
async def echo_server(reader, writer):
hireader = hiredis.Reader()
while True:
s = await reader.read(4096)
if not s:
break
hireader.feed(s)
while True:
req = hireader.gets()
if not req:
break
res = process(req)
writer.write(res)
await writer.drain()
return 0
async def main():
server = await asyncio.start_server(echo_server, '0.0.0.0', 5001)
print('serving on {}'.format(server.sockets[0].getsockname()))
await server.serve_forever()
return 0
asyncio.run(main())
echo_bench_asyncio_uvloop.py
的内容:
import asyncio
import hiredis
d = {}
def process(req):
cmd = req[0].lower()
if cmd == b'set':
d[req[1]] = req[2]
return b"+OK\r\n"
elif cmd == b'get':
v = d.get(req[1])
if v is None:
return b'$-1\r\n'
else:
return b'$1\r\n1\r\n'
elif cmd == b'config':
return b'-ERROR\r\n'
else:
return b'-ERROR\r\n'
return b''
async def echo_server(reader, writer):
hireader = hiredis.Reader()
while True:
s = await reader.read(4096)
if not s:
break
hireader.feed(s)
while True:
req = hireader.gets()
if not req:
break
res = process(req)
writer.write(res)
await writer.drain()
return 0
async def main():
server = await asyncio.start_server(echo_server, '0.0.0.0', 5002)
print('serving on {}'.format(server.sockets[0].getsockname()))
await server.serve_forever()
return 0
try:
import uvloop
uvloop.install()
print('uvloop is enabled')
except ImportError:
print('uvloop is not available')
asyncio.run(main())
启动服务器
python3 echo_bench_gevent.py # will listen on port 5000
python3 echo_bench_asyncio.py # will listen on port 5001
python3 echo_bench_asyncio_uvloop.py # will listen on port 5002
测试
redis-benchmark -p 5000 -t get -n 100000 -r 100000000
redis-benchmark -p 5001 -t get -n 100000 -r 100000000
redis-benchmark -p 5002 -t get -n 100000 -r 100000000
结果
模式 | python 3.9 | python 3.11 |
---|---|---|
gevent | 34281.80请求 /秒< / td> | 32258.07请求 /秒< / td> |
asyncio | 40144.52请求 /秒< / td> | 51652.89请求 /第二个< / td> |
asyncio + uvloop | 64102.57请求 /第二个< / td> | 66577.90请求 /秒< / td> |
本地epoll
redis是用C中的epoll
实现的,我们可以直接测试redis:
redis-benchmark -p 6379 -t get -n 100000 -r 100000000
输出:
75244.55 requests per second
结论
-
asyncio
比python 3.11 中的 -
asyncio
可以使用uvloop
的gevent
速度两倍。 -
asyncio
最高可达本机Epoll程序的68%。 -
asyncio
可以使用uvloop
的本机Epoll程序的88%。
gevent
快50%