2009-08-05 11 views
24

Opracowałem mini serwer HTTP w C++, używając instrukcji boost :: asio, a teraz testuję go na wielu klientach i nie byłem w stanie zbliżyć się do nasycania procesor. Testuję na instancji Amazon EC2 i uzyskuję około 50% wykorzystania jednego procesora, 20% innego, a pozostałe dwa są bezczynne (zgodnie z htopem).Serwer Socket C++ - Nie można nasycić procesora

Szczegóły:

  • Lampa serwer się jednego wątku na rdzeń
  • Wnioski są odbierane, analizowane, przetwarzane i odpowiedzi są wypisywane
  • wnioski są dla danych, które są odczytywane z pamięć (tylko do odczytu dla tego testu)
  • Jestem "ładowanie" serwera za pomocą dwóch maszyn, z których każda działa aplikacja java, działa 25 wątków, wysyłanie żądań
  • Widzę około 230 żądań na sekundę ughput (to aplikacyjne wnioski, które składają się z wielu żądań HTTP)

Więc co mam patrzeć, aby poprawić ten wynik? Biorąc pod uwagę, że procesor jest w większości bezczynny, chciałbym wykorzystać tę dodatkową pojemność, aby uzyskać wyższą przepustowość, powiedzmy 800 żądań/s lub cokolwiek innego.

Pomysły miałem:

  • Wnioski są bardzo małe, a często spełnione w ciągu kilku ms, mogę zmodyfikować klienta do wysyłania/skomponować większe żądania (być może za pomocą dozownika)
  • I czy można zmodyfikować serwer HTTP tak, aby używał wzoru Wybierz, czy jest to odpowiednie tutaj?
  • mogłem zrobić kilka profili, aby spróbować zrozumieć jakie są wąskim gardłem użytkownika/jest
+0

Czy można założyć, że masz port 1Gbps na serwerze? Jakie są twoje żądania i rozmiary odpowiedzi (na drucie)? – nik

+2

Jakie jest wykorzystanie przepustowości na porcie sieciowym serwera (ten, który zakładam jako 1Gbps)? – nik

+1

Test działa na EC2, który jak sądzę wykorzystuje gigabit. Bmon zgłasza około 3 MB (megabity, jak sądzę) stawki TX i stawki 2,5Mib RX. Wiele rozmiarów żądań/odpowiedzi jest niewielkich (zaledwie 100 bajtów), ale niektóre odpowiedzi wynoszą do 1mb, żądania prawdopodobnie wynoszą do .25mb. –

Odpowiedz

40

boost :: asio nie jest thread-przyjazny, jak można mieć nadzieję - jest duży zamek wokół kodu epoll na impuls /asio/detail/epoll_reactor.hpp, co oznacza, że ​​tylko jeden wątek może wywoływać w systemie epolowym jądro systemu naraz. A w przypadku bardzo małych próśb robi to ogromną różnicę (oznacza to, że widzisz tylko z grubsza jednowątkową wydajność).

Należy zauważyć, że jest to ograniczenie sposobu, w jaki program boost :: asio korzysta z zasobów jądra systemu Linux, niekoniecznie z samego jądra systemu Linux. Funkcja epoll syscall obsługuje wiele wątków podczas korzystania z zdarzeń wyzwalanych zboczem, ale prawidłowe wykonanie (bez nadmiernego blokowania) może być dość trudne.

BTW, robiłem trochę pracy w tym obszarze (łączenie wielowątkowej epilowej pętli zdarzeń epoll z zaplanowanymi wątkami/włóknami) i udostępniłem jakiś kod pod projektem nginetd.

+0

Dzięki za informacje, ciekawe rzeczy. –

+1

(+1) cmeer Mam nieodebrane stanowisko dotyczące wydajności boost :: asio ogólnie w systemach Windows i Linux. Jeśli przeczytałeś dużą część asio, przyjdź i odpowiedz na mój post: P –

+3

Naprawdę martwiłem się tym globalnym zamkiem. To nie jest tak duży problem, jak mogłoby się wydawać. Szyjka butelki może występować tylko w scenariuszach z wysoką wydajnością. Jednakże, gdy asio działa w trybie epoll (linux), próbuje prewencyjnie pisać lub czytać po wydaniu komendy 'async_ *'. W scenariuszu z wysokim poziomem wejściowym gniazdo będzie zwykle gotowe do czytania, pozwalając całkowicie pominąć epokę "async_read". Nie można prosić o lepszą wydajność sieci. –

2

Z twoich komentarzy na temat wykorzystania sieci,
Wygląda na to, że nie masz dużego ruchu sieciowego.

3 + 2.5 MiB/sec znajduje się w pobliżu stadionu 50Mbps (w porównaniu do portu 1Gbps).

Powiedziałbym, masz jedną z następujących dwóch problemów,

  1. Niedostateczne obciążenie pracą (low-rate żądania od klientów)
    • Blokowanie na serwerze (ingerować generowanie odpowiedzi)

Patrząc na cmeerw „s notatki a wykorzystanie procesora figury
(na biegu jałowym w 50% + 20% + 0% + 0%)
wydaje się najprawdopodobniej ograniczeniem w implementacji serwera.
Po drugie odpowiedź cmeerw (+1).

+1

Przeprowadza testy w klastrze Cloud Computing Cluster EC2. Trudno wykluczyć możliwą słabą wydajność EC2. – unixman83

3

230 żądań/s wydaje się bardzo niskie dla takich prostych żądań asynchronicznych. Jako takie, używanie wielu wątków jest prawdopodobnie przedwczesną optymalizacją - spraw, aby działało poprawnie i dostroiło się w jednym wątku, i sprawdź, czy nadal ich potrzebujesz. Pozbycie się niepotrzebnego blokowania może przyspieszyć działanie.

This article ma kilka szczegółów i dyskusji na temat strategii wejścia/wyjścia dla wydajności serwera WWW około 2003. Czy ktoś ma coś bardziej świeżego?

+0

Należy pamiętać, że 230 żądań/s to "żądania aplikacji", które składają się z wielu faktycznie żądań HTTP. –

+0

Nie ma zbyt wiele blokowania, aby się go pozbyć, żadnego w moim kodzie, ale jak cmeerw wskazuje na zwiększenie :: asio robi pewne wewnętrzne blokowanie. Serwer HTTP wykonuje pracę wyłącznie z procesorem, więc nie używanie dodatkowych rdzeni byłoby kosztownym marnotrawstwem. –

+2

Jeśli celem jest nasycenie procesora, wykonaj pracę w jednym wątku, a pozostałe trzy obliczyć PI lub coś podobnego. Posiadanie wielu wątków na poziomie użytkownika nie ułatwia lub nie pozwala sprzętowi i urządzeniom IO na odczytywanie i zapisywanie pakietów sieciowych. Wątki i rdzenie są przeznaczone do pracy obliczeniowej, jeśli ich nie robisz, nie mogą niczego zyskać i ryzykować rywalizacją z innymi systemami. – soru

12

Ponieważ używasz EC2, wszystkie zakłady są wyłączone.

Wypróbuj za pomocą prawdziwego sprzętu, a następnie możesz zobaczyć, co się dzieje. Próba przeprowadzenia testów wydajności w maszynach wirtualnych jest zasadniczo niemożliwa.

Nie wiem jeszcze, do czego EC2 jest przydatny, jeśli ktoś się dowie, proszę dać mi znać.

+0

System ten zostanie wdrożony w EC2, więc testowanie wydajności systemu na prawdziwym sprzęcie nie byłoby pomocne, nie sądzę. –

+6

Punkt oznaczenia jest poprawny: Do profilowania należy używać prawdziwej maszyny lub co najmniej bardziej kontrolowanego środowiska. Wprowadź do EC2 wszystko, co ci się podoba, ale zrozum, że pracujesz na obrazie VM, a to oznacza, że ​​twój "bezczynny" procesor może być po prostu dlatego, że jakiś inny lokator na tym komputerze ma na chwilę cały procesor. A to utrudnia profilowanie. – janm

+2

(+1) nienawidzę źle poinformowanych głosów w dół –

0

ASIO nadaje się do małych i średnich zadań, ale nie jest zbyt dobry w wykorzystywaniu mocy systemu bazowego. Nie są to połączenia z użyciem gniazd raw, ani nawet IOCP w systemie Windows, ale jeśli masz doświadczenie, zawsze będziesz lepszy niż ASIO. Tak czy inaczej, wiele z tych metod wiąże się z obciążeniem, a jeszcze bardziej z ASIO.

Za to, co warto. używanie wywołań w trybie raw na moim niestandardowym HTTP może obsłużyć dynamiczne żądania 800K na sekundę z 4 rdzeniem I7. Obsługuje z pamięci RAM, czyli tam, gdzie musisz być na tym poziomie wydajności. Na tym poziomie wydajności sterownik sieci i system operacyjny zużywają około 40% procesora. Korzystając z ASIO, mogę uzyskać około 50 do 100 000 zapytań na sekundę, a jego wydajność jest dość zmienna i w większości związana w mojej aplikacji. Post @cmeerw wyjaśnia głównie dlaczego.

Jednym ze sposobów poprawy wydajności jest wdrożenie proxy UDP. Przechwytywanie żądań HTTP, a następnie przekierowywanie ich przez UDP do serwera UDP-HTTP backendu pozwala ominąć wiele obciążeń TCP w stosach systemu operacyjnego. Możesz także mieć przednie końce, które przebiegają przez UDP, co nie powinno być zbyt trudne do zrobienia. Zaletą serwera proxy HTTP-UDP jest to, że pozwala on na użycie dowolnego dobrego interfejsu bez modyfikacji, a użytkownik może je dowolnie wymieniać bez żadnego wpływu. Aby go zaimplementować, potrzebujesz jeszcze kilku serwerów. Ta modyfikacja na moim przykładzie obniżyła użycie procesora systemu operacyjnego do 10%, co zwiększyło liczbę żądań na sekundę do nieco ponad miliona na tym pojedynczym zapleczu. I FWIW Powinieneś zawsze mieć konfigurację frontend-backend dla każdej wydajnej strony, ponieważ frontendy mogą buforować dane bez spowalniania ważniejszych backendów zleceń dynamicznych.

Przyszłość wydaje się być pisaniem własnego sterownika, który implementuje własny stos sieciowy, dzięki czemu można uzyskać możliwie jak najbliżej żądań i zaimplementować tam swój własny protokół.Co prawdopodobnie nie jest tym, co większość programistów chce usłyszeć, ponieważ jest bardziej skomplikowana. W moim przypadku byłbym w stanie wykorzystać o 40% więcej procesora i przejść do ponad miliona dynamicznych żądań na sekundę. Metoda proxy UDP może zbliżyć się do optymalnej wydajności bez potrzeby wykonywania tej czynności, jednak będziesz potrzebować więcej serwerów - ale jeśli wykonujesz tę liczbę żądań na sekundę, zwykle potrzebujesz wielu kart sieciowych i wielu frontendów, aby obsłużyć przepustowość, dzięki czemu kilka lekkich proxy UDP nie ma tak wielkiej umowy.

Mam nadzieję, że niektóre z nich mogą ci się przydać.

+1

Czy chcesz pokazać przykład lub działający projekt? Bez tego jest to równie pomocne, jak nieistotna rozmowa. Nie próbuję cię poniżać, ale potrzebny jest tutaj konkretny kod. –

0

Ile masz instancji io_service? Zwiększ asio ma example, który tworzy io_service na procesor i używać ich w sposób RoundRobin.

Nadal można utworzyć cztery wątki i przypisać po jednym na procesor, ale każdy wątek może sondować we własnym urządzeniu io_service.