Te dwie biblioteki mają wspólną filozofię i podobne decyzje projektowe. Ale ten popularny test WSGI mówi eventlet
jest znacznie wolniejsze niż gevent
. Co sprawia, że ich występ jest tak inny?
Jak znam kluczowe różnice między nimi to:
gevent
celowo zależy od i jest sprzężony z libev
(libevent
, wcześniej) podczas eventlet
definiuje niezależny interfejs reaktora i implementuje poszczególne adaptery wykorzystujące select
, epoll
i zakręcony reaktor za nim. Czy dodatkowy interfejs reaktora generuje krytyczne wyniki?
gevent
jest najczęściej napisany w Cython, podczas gdy eventlet
jest napisany w czystym Pythonie. Czy natywnie kompilowany Cython jest szybszy od czystego Pythona, z powodu nie tak dużo programów obliczeniowych, ale związanych z IO?
Prymitywne gevent
emulować standardowe interfejsy bibliotek podczas eventlet
Prymitywy różnią się od standardowych i zapewnia dodatkową warstwę do emulacji. Czy tworzy dodatkową warstwę emulacji eventlet
wolniej?
Czy wdrożenie eventlet.wsgi
tylko gorzej niż gevent.pywsgi
?
Naprawdę się zastanawiam, ponieważ oni ogólnie wyglądają dla mnie tak podobnie.
Cóż, gevent nie jest "w większości" napisany w języku Cython, chociaż niektóre krytyczne sekcje są.
Cython robi ogromną różnicę. Optymalizacje procesora działają znacznie lepiej dzięki skompilowanemu kodowi. Prognozowanie rozgałęzień, na przykład, rozpada się w systemach opartych na VM, ponieważ niedostrzeganie rozgałęzień na poziomie wykonania maszyny wirtualnej jest dla niej nieprzejrzyste. Ślad pamięci podręcznej jest mocniejszy. Skompilowany kod robi tutaj ogromną różnicę, a IO może być bardzo wrażliwy na opóźnienie.
W podobnym tonie, libev jest bardzo szybki. Te same powody.
Wygląda na to, że eventlet nie powinien był używać selektora select (zazwyczaj Python 2.6 jest domyślnie ustawiony na epoll). Jeśli jednak utknął na selekcji, to by to uczyniło naprawdę slow (ponieważ Python musi przekonwertować select fd_set w tę iz powrotem na listę Pythona, więc robi się brzydko, gdy jest w środku pętli).
Nie zrobiłem żadnego profilowania, ale chciałbym się założyć, że libev / libevent plus Cython robi dużą różnicę. Warto zauważyć, że niektóre z pierwotnych wątków są w Cython w gevent. To wielka sprawa, ponieważ wiele kodu dotyka ich pośrednio poprzez IO, a nawet standardową bibliotekę w niektórych miejscach.
Jeśli chodzi o dodatkową warstwę emulacji zdarzenia, wydaje się, że jest o wiele więcej bounciness. W gevent, ścieżka kodu wydaje się budować wywołania zwrotne i pozwolić hubowi je wywoływać. Wydaje się, że zdarzenie wykonało więcej operacji księgowych wykonywanych przez węzeł w gevent. Znowu jednak nie sprofilowałem tego. Jeśli chodzi o sam małpowanie, wyglądają dość podobnie.
Serwer WSGI jest kolejnym trudnym. Warto zauważyć, że analiza nagłówka w gevent jest odłożona do biblioteki standardowej, podczas gdy implementują ją same w zdarzeniu. Nie jestem pewien, czy to duży wpływ, czy nie, ale nie byłoby zaskoczeniem, gdyby coś tam się czaiło. Najbardziej wymowne jest to, że serwer zdarzeń jest oparty na niedopasowanej wersji standardowej biblioteki BaseHTTPServer. Nie mogę sobie wyobrazić, że jest to bardzo optymalne. Gevent implementuje serwer, który jest świadomy emulacji.
Przepraszam za spóźnioną odpowiedź.
Istnieją dwa główne powody dużej różnicy w wydajności w tym benchmarku:
- jak wspomniano wcześniej, krytyczne ścieżki gevent są mocno zoptymalizowane
- ten benchmark przeprowadza testy warunków skrajnych. Nie jest już związany z IO, ponieważ stara się, aby maszyna uruchamiała jak najwięcej zgłoszeń. I tu właśnie świeci kod Cythonized.
"W prawdziwym świecie", który zdarza się tylko podczas "slashdot" wybuchów ruchu. Co jest ważne i trzeba być gotowym, ale kiedy to się dzieje, reagujesz, dodając więcej serwerów lub wyłączając funkcje obciążające zasoby. Nie widziałem benchmarku, który faktycznie dodaje więcej serwerów, gdy zwiększa się obciążenie.
Z drugiej strony, benchmark symulowałby ładunek "normalny dzień" (który byłby różny w zależności od witryny), ale generalnie mógłby być przybliżony do żądania, losowa pauza, powtórzenie. Im mniej ta przerwa, tym większy ruch symulujemy. Również strona benchmarku po stronie klienta musiałaby symulować opóźnienie. W Linuksie można to zrobić za pomocą niesamowitego netema [1], w przeciwnym razie, wprowadzając małe opóźnienia przed recv / send calls (co byłoby bardzo trudne, ponieważ testy porównawcze zwykle korzystają z bibliotek wyższych poziomów).
Jeśli te warunki zostaną spełnione, będziemy faktycznie porównywać problemy związane z IO. Ale wyniki nie byłyby zbyt niesamowite: wszyscy kandydaci z powodzeniem obsłużyli 10, 50, a nawet 200 qps ładunków. Nudne, prawda? Moglibyśmy zatem zmierzyć rozkład opóźnień, czas na obsługę żądań 99%, itp. Gevent nadal wykazywałby lepsze wyniki. Ale różnica nie byłaby imponująca.
[1] Symuluj opóźnione i upuszczone pakiety w systemie Linux