我刚写了一条简单的代码来处理redis + gevent + gevent,看看异步如何有助于穿孔,我惊讶地发现不良表现。这是我的代码。如果您将前两条线路摆脱猴子补丁此代码,那么您将看到“正常执行”时序。 在Ubuntu 12.04 LTS VM上,我看到了的时间

没有猴子补丁 - 54秒 猴子补丁 - 61秒

我的代码/方法有什么问题吗?这里有一个完善的问题吗?

#!/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)
.

有帮助吗?

解决方案

这是预期的。

您在VM上运行该基准测试,系统调用的成本高于物理硬件。当GEVENT激活时,它倾向于生成更多系统调用(以处理ePOLS设备),因此您最终最终表现较少。

您可以通过在脚本上使用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也是最糟糕的情况因为:

  • 它为服务器生成了许多往返

  • 它系统地连接/断开连接(1000次),因为池在uxdomainsocket函数中声明。 实际上,您的基准测试不会测试GEVent,Redis或Redis-Py:它练习VM的能力,以维持2个进程之间的平板游戏。

    如果要提高性能,则需要:

    • 使用流水线来减少往返的圆形数量

    • 使池在整个基准中持久地持续

      例如,请考虑以下脚本:

      #!/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)
      
      . 使用此脚本,我达到了更好的性能,几乎没有带有gvent的开销。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top