Это ожидаемо.
Вы запускаете этот тест на виртуальной машине, на которой стоимость системных вызовов выше, чем на физическом оборудовании. Когда 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).
Обратите внимание, что этот вид тестов является наихудшим случаем для любой системы циклов обработки событий, потому что у вас есть только один файловый дескриптор, поэтому операции ожидания нельзя разложить на несколько дескрипторов. Кроме того, асинхронный ввод-вывод ничего здесь не улучшит, поскольку все синхронно.
Это также худший случай для Redis, потому что:
он генерирует много обращений к серверу
он систематически подключается/отключается (1000 раз), потому что пул объявлен в функции UxDomainSocket.
На самом деле ваш тест не тестирует gevent, redis или redis-py: он проверяет способность виртуальной машины поддерживать игру в пинг-понг между двумя процессами.
Если вы хотите повысить производительность, вам необходимо:
используйте конвейерную обработку, чтобы уменьшить количество циклов обмена данными
сделать пул постоянным на протяжении всего теста
Например, рассмотрим следующий скрипт:
#!/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.
person
Didier Spezia
schedule
19.05.2012