这是意料之中的。
您在 VM 上运行此基准测试,系统调用的成本高于物理硬件。当 gevent 被激活时,它往往会产生更多的系统调用(以处理 epoll 设备),因此最终性能会降低。
您可以通过在脚本上使用 strace 轻松检查这一点。
如果没有 gevent,内部循环会生成:
recvfrom(3, ":931\r\n", 4096, 0, NULL, NULL) = 6
sendto(3, "*3\r\n$6\r\nINCRBY\r\n$10\r\ntestsocket\r"..., 41, 0, NULL, 0) = 41
recvfrom(3, ":941\r\n", 4096, 0, NULL, NULL) = 6
sendto(3, "*3\r\n$6\r\nINCRBY\r\n$10\r\ntestsocket\r"..., 41, 0, NULL, 0) = 41
使用 gevent,您将遇到以下情况:
recvfrom(3, ":221\r\n", 4096, 0, NULL, NULL) = 6
sendto(3, "*3\r\n$6\r\nINCRBY\r\n$10\r\ntestsocket\r"..., 41, 0, NULL, 0) = 41
recvfrom(3, 0x7b0f04, 4096, 0, 0, 0) = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(5, EPOLL_CTL_ADD, 3, {EPOLLIN, {u32=3, u64=3}}) = 0
epoll_wait(5, {{EPOLLIN, {u32=3, u64=3}}}, 32, 4294967295) = 1
clock_gettime(CLOCK_MONOTONIC, {2469, 779710323}) = 0
epoll_ctl(5, EPOLL_CTL_DEL, 3, {EPOLLIN, {u32=3, u64=3}}) = 0
recvfrom(3, ":231\r\n", 4096, 0, NULL, NULL) = 6
sendto(3, "*3\r\n$6\r\nINCRBY\r\n$10\r\ntestsocket\r"..., 41, 0, NULL, 0) = 41
当 recvfrom 调用阻塞 (EAGAIN) 时,gevent 会返回事件循环,因此会执行额外的调用来等待文件描述符事件 (epoll_wait)。
请注意,这种基准测试对于任何事件循环系统来说都是最坏的情况,因为您只有一个文件描述符,因此无法将等待操作分解为多个描述符。此外,异步 I/O 在这里无法改进任何东西,因为一切都是同步的。
对于 Redis 来说,这也是最坏的情况,因为:
实际上,您的基准测试并未测试 gevent、redis 或 redis-py:它锻炼了 VM 的能力,以维持两个进程之间的乒乓球比赛。
如果你想提高性能,你需要:
使用流水线减少往返次数
使池在整个基准测试中保持不变
例如,考虑以下脚本:
#!/usr/bin/python
from gevent import monkey
monkey.patch_all()
import timeit
import redis
from redis.connection import UnixDomainSocketConnection
pool = redis.ConnectionPool(connection_class=UnixDomainSocketConnection, path = '/tmp/redis.sock')
def UxDomainSocket():
r = redis.Redis(connection_pool = pool)
p = r.pipeline(transaction=False)
p.set("testsocket", 1)
for i in range(100):
p.incr('testsocket', 10)
p.get('testsocket')
p.delete('testsocket')
p.execute()
print timeit.Timer(stmt='UxDomainSocket()', setup='from __main__ import UxDomainSocket').timeit(number=1000)
使用这个脚本,我获得了大约 3 倍的性能提升,并且 gevent 几乎没有开销。