Python协程性能测试asyncio vs gevent(异步并发)

两者的区别

区别1解释

gevent是第三方库,通过greenlet实现协程,其基本思路是:

当一个greenlet遇到IO操作时,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。

asyncio是Python 3.4版本引入的标准库,直接内置了对异步IO的支持,不需要第三方的支持,

asyncio的编程模型就是一个消息循环。我们从asyncio模块中直接获取一个EventLoop的引用,然后把需要执行的协程扔到EventLoop中执行,就实现了异步IO。很多异步io操作这两个库都可以用,只是他们在不同场景下的效率和易用性可能有区别,当然这个得进行深入的测试和研究,单就现在普通的场景来说,区别并不大。

区别2解释

asycio 需要自己在代码中让出CPU,控制权在自己手上

gevent 用会替换标准库,你以为调用的是标准库的方法实际已经被替换成gevent自己的实现,遇到阻塞调用,gevent会自动让出CPU

性能测试

测试一下 python 的 asyncio 和 gevent 的性能,再和同等 C 程序对比一下,先安装依赖

pip3 install hiredis gevent

如果是 Linux 的话,可以选择安装 uvloop 的包,可以测试加速 asyncio 的效果。

测试程序: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          # listened on port 5000
python3 echo_bench_asyncio.py         # listened on port 5001
python3 echo_bench_asyncio_uvloop.py  # listened 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 requests / second 32258.07 requests / second
asyncio 40144.52 requests / second 51652.89 requests / second
asyncio + uvloop 64102.57 requests / second 66577.90 requests / second

原生对比:

redis-benchmark -p 6379 -t get -n 100000 -r 100000000

输出

75244.55 requests per second

测试结论

结论就是 3.11 下面,asyncio 比 gevent 快 50%,加上 uvloop 可以快一倍。纯用 asyncio 性能可以做到 redis 的 68%,而加上 uvloop 后可以做到 redis 的 88%,当然程序比较简单,没有复杂的数据处理,主要就是测评网络 I/O 性能

猜你喜欢

转载自blog.csdn.net/u012206617/article/details/128788444