Redis + GEVENT - 가난한 성능 - 내가 뭘 잘못하고 있니?
-
11-12-2019 - |
문제
방금 비대네크가 어떻게 구배를 돕는지와 나쁜 성능을 찾기 위해 놀랐을지를 볼 수있는 PERF 테스트 REDIS + GEVENT에 대한 간단한 코드를 썼습니다.여기 내 코드가 있습니다.원숭이 패치에 처음 두 줄을 제거하면이 코드가 "정상 실행"타이밍이 표시됩니다.
우분투 12.04 LTS VM, 나는 의 타이밍을보고있다.
원숭이 패치가없는
54 초 원숭이 패치와 함께 - 61 초
코드 / 접근 방식에 문제가 있습니까?여기에 PEF 문제가 있습니까?
#!/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가 활성화되면 더 많은 시스템 호출을 생성하는 경향이 있으므로 덜 성능을 덜받습니다.
스크립트의 스트레이스를 사용 하여이 시점을 쉽게 확인할 수 있습니다.
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 호출이 차단되면 (eazain) GEVent는 이벤트 루프로 돌아갑니다. 따라서 파일 설명자 이벤트 (EPOLL_WAIT)를 대기하기 위해 추가 호출이 수행됩니다.
이런 종류의 벤치 마크는 하나의 파일 설명 자만 있기 때문에 모든 이벤트 루프 시스템에 대한 최악의 경우이므로 대기 작업은 여러 설명자에서 인식 할 수 없습니다. 또한 모든 것이 동기식이기 때문에 비동기 I / O가 여기서 아무 것도 개선 할 수 없습니다.
Redis의 최악의 경우 :
-
서버에 많은 왕복을 생성합니다
-
풀이 UXDomastSocket 함수로 선언되기 때문에 저장 / 연결 해제를 체계적으로 연결 / 연결 해제합니다.
실제로 벤치 마크는 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)
이 스크립트를 사용하면 성능이 약 3 배 늘어나고 거의 오버 헤드가 없습니다.
-