Redis + Gevent - плохой производительность - что я делаю не так?

StackOverflow https://stackoverflow.com//questions/10656953

Вопрос

Я только что написал простой кусок кода для Perf Test Redis + Gevent, чтобы увидеть, как Async помогает перфорамс, и я был удивлен, чтобы найти плохую производительность.Вот мой код.Если вы избавитесь от первых двух линий к Bonkey Patch этот код, вы увидите время «нормальное исполнение».

на 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 активирован, он имеет тенденцию генерировать больше системных вызовов (для обработки устройства EPOLL), поэтому вы заканчиваете менее производительностью.

Вы можете легко проверить эту точку, используя stroace на скрипте.

без 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
.

Когда вызов RECVROM блокирует (eaegain), Gevent возвращается к контуру события, чтобы дождаться дополнительные вызовы, чтобы дождаться события дескрипторов файлов (Epoll_Wait).

Обратите внимание, что этот вид ориентира является худшим случаем для любой системы цикла событий, поскольку у вас есть только один файловый дескриптор, поэтому операции ожидания не могут быть факторизованы на нескольких дескрипторах. Кроме того, Async I / OS не может больше улучшить здесь, так как все синхронно.

Это также худший случай для Redis, потому что:

    .
  • Это генерирует много круглых игр к серверу

  • Это систематически соединяет / отключает (1000 раз), потому что пул объявлен в функции Uxdomainsocket.

    На самом деле ваш ориентир не тестирует Gevent, Redis или Redis-Py: он осуществляет возможность VM для поддержания игры Ping-Pong между 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)
      
      .

      с этим скриптом, я получаю около 3x лучшую производительность и практически не накладки с Gevent.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top