Ini diharapkan.
Anda menjalankan tolok ukur ini pada VM, yang biaya panggilan sistemnya lebih tinggi daripada perangkat keras fisik. Saat gevent diaktifkan, ia cenderung menghasilkan lebih banyak panggilan sistem (untuk menangani perangkat epoll), sehingga performa Anda akan berkurang.
Anda dapat dengan mudah memeriksa poin ini dengan menggunakan strace pada skrip.
Tanpa gevent, loop dalam menghasilkan:
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
Dengan gevent, Anda akan mengalami kejadian:
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
Saat panggilan recvfrom memblokir (EAGAIN), gevent kembali ke loop peristiwa, jadi panggilan tambahan dilakukan untuk menunggu peristiwa deskriptor file (epoll_wait).
Harap dicatat benchmark semacam ini adalah kasus terburuk untuk sistem loop peristiwa apa pun, karena Anda hanya memiliki satu deskriptor file, sehingga operasi menunggu tidak dapat difaktorkan pada beberapa deskriptor. Selanjutnya, I/O async tidak dapat meningkatkan apa pun di sini karena semuanya sinkron.
Ini juga merupakan kasus terburuk bagi Redis karena:
-
itu menghasilkan banyak perjalanan pulang pergi ke server
-
itu secara sistematis menghubungkan/memutuskan (1000 kali) karena kumpulan dideklarasikan dalam fungsi UxDomainSocket.
Sebenarnya tolok ukur Anda tidak menguji gevent, redis, atau redis-py:tolok ukur ini melatih kemampuan VM untuk mempertahankan permainan ping-pong di antara 2 proses.
Jika Anda ingin meningkatkan kinerja, Anda perlu:
-
gunakan pipelining untuk mengurangi jumlah perjalanan pulang pergi
-
membuat kumpulan tetap ada di seluruh tolok ukur
Misalnya, pertimbangkan dengan skrip berikut:
#!/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)
Dengan skrip ini, saya mendapatkan kinerja sekitar 3x lebih baik dan hampir tidak ada overhead dengan gevent.