Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python redis+;gevent-性能差-我做错了什么?_Python_Performance_Redis_Gevent - Fatal编程技术网

Python redis+;gevent-性能差-我做错了什么?

Python redis+;gevent-性能差-我做错了什么?,python,performance,redis,gevent,Python,Performance,Redis,Gevent,我只是写了一段简单的代码来测试Redis+gevent,看看async如何帮助提高性能,我惊讶地发现性能很差。这是我的密码。如果您去掉前两行代码来修补这段代码,那么您将看到“正常执行”时间 在Ubuntu12.04 LTS虚拟机上,我看到了 没有猴子补丁-54秒 猴子补丁-61秒 我的代码/方法是否有问题?这里有性能问题吗 #!/usr/bin/python from gevent import monkey monkey.patch_all() import timeit import

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

在Ubuntu12.04 LTS虚拟机上,我看到了

没有猴子补丁-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)
这是意料之中的

您可以在虚拟机上运行此基准测试,在虚拟机上系统调用的成本高于物理硬件。当激活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最糟糕的情况,因为:

  • 它会生成许多到服务器的往返

  • 它系统地连接/断开连接(1000次),因为池是在UxDomainSocket函数中声明的

实际上,您的基准并不测试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几乎没有开销。

感谢您的详细回复。如果我理解更深层次的问题,基本上我所做的是,只有一个“对象”可以等待-例如,如果我有一个Redis连接池,我使用gevent,那么它将给我更好的性能(假设Redis可以跟上)。顺便说一句,VM(和Ux套接字)仅用于测试。生产将是不同的实例等,如果使用管道,那么如何使用“redis锁”