首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >redis + gevent -性能不佳-我哪里做错了?

redis + gevent -性能不佳-我哪里做错了?
EN

Stack Overflow用户
提问于 2012-05-19 01:13:26
回答 1查看 9.8K关注 0票数 21

我只是写了一段简单的代码来测试Redis + gevent,看看异步是如何帮助性能的,我惊讶地发现性能很差。这是我的代码。如果你去掉了前两行来修补这段代码,那么你会看到“正常执行”的时间。

在Ubuntu 12.04 LTS虚拟机上,我看到一个计时

无猴贴- 54秒,有猴贴- 61秒

我的代码/方法有问题吗?这里有性能问题吗?

代码语言:javascript
复制
#!/usr/bin/python

from gevent import monkey

monkey.patch_all()

import timeit
import redis
from redis.connection import UnixDomainSocketConnection

def UxDomainSocket():
    pool = redis.ConnectionPool(connection_class=UnixDomainSocketConnection, path =    '/var/redis/redis.sock')
    r = redis.Redis(connection_pool = pool)
    r.set("testsocket", 1)
    for i in range(100):
            r.incr('testsocket', 10)
    r.get('testsocket')
    r.delete('testsocket')


print timeit.Timer(stmt='UxDomainSocket()',
 setup='from __main__ import UxDomainSocket').timeit(number=1000)
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-05-19 16:08:21

这是意料之中的。

您在系统调用成本高于物理硬件的VM上运行此基准测试。当gevent被激活时,它倾向于生成更多的系统调用(以处理epoll设备),因此最终性能较低。

您可以通过在脚本上使用strace轻松地检查这一点。

如果没有gevent,内部循环将生成:

代码语言:javascript
复制
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,您将会出现以下情况:

代码语言:javascript
复制
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来说也是最糟糕的情况,因为:

  • 它生成到服务器的多次往返,它系统地连接/断开连接(1000次),因为池是在UxDomainSocket
  • 中声明的。

实际上,您的基准测试不会测试gevent、redis或redis-py:它会测试VM在两个进程之间支持乒乓球游戏的能力。

如果你想提高性能,你需要:

  • 使用流水线来减少整个benchmark

中池的持久化数量

例如,考虑使用以下脚本:

代码语言:javascript
复制
#!/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几乎没有任何开销。

票数 52
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10656953

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档