Porównanie wydajności sterownika sieciowego w wersjach na 10 języków programowania

Grupa badawcza z niemieckich uniwersytetów opublikowała wyniki eksperymentu, w ramach którego na różnych językach programowania opracowano 10 wariantów typowego sterownika dla 10-gigabitowych kart sieciowych Intel Ixgbe (X5xx). Sterownik działa w przestrzeni użytkownika i został zaimplementowany w językach C, Rust, Go, C#, Java, OCaml, Haskell, Swift, JavaScript i Python. Podczas pisania kodu główny nacisk kładziono na osiągnięcie maksymalnej wydajności z uwzględnieniem specyfiki każdego języka. Pod względem funkcjonalności wszystkie warianty są identyczne i składają się z około 1000 linii kodu. Osiągnięcia projektu są rozpowszechniane na licencji BSD.

Wariant sterownika w języku Rust okazał się bardzo zbliżony pod względem wydajności do wzorcowego sterownika w języku C. Przy obciążeniu, które polegało na jednoczesnym wysyłaniu bloków składających się z 32 pakietów, sterownik Rust nieco ustępował, jednak w testach z większą liczbą niż 32 pakiety w bloku pod względem prędkości praktycznie nie różnił się od sterownika w C i wykazywał wydajność na poziomie przetwarzania 28 mln pakietów na sekundę na serwerze z procesorem CPU Xeon E3-1230 v2 3.3 GHz.

Porównanie wydajności sterownika sieciowego w wersjach na 10 języków programowania

Następne miejsce pod względem wydajności zajęły sterowniki w językach Go i C#, które wykazały dość zbliżone wyniki (sterownik w Go wygrywał w testach z blokami składającymi się z maksymalnie 16 pakietów, a nieco przegrywał w testach z większą liczbą niż 16 pakietów w bloku). Przy 256 pakietach w bloku maksymalna wydajność sterownika w C# wyniosła około 28 mln pakietów na sekundę, a sterownika w Go około 25 mln pakietów na sekundę.

Dalej, ze stosunkowo zbliżonymi wynikami, znajdowały się sterowniki w
Java, OCaml i Haskell, które już wyraźnie ustępowały wcześniej omawianym wariantom i nie zdołały przekroczyć poziomu 12 mln pakietów na sekundę. Jeszcze większe opóźnienia wykazały sterowniki w Swift i JavaScript, które mogły przetwarzać strumienie na poziomie 5 mln pakietów na sekundę.

Na końcu rankingu znalazł się sterownik w języku Python, który zdołał przetworzyć zaledwie 0,14 mln pakietów na sekundę. Implementacja w Pythonie została użyta do oceny prędkości działania interpreterów bez JIT i bez specyficznych optymalizacji (kod był wykonywany z użyciem CPython 3.7 i nie był zgodny z PyPy, lecz należy zauważyć, że optymalizacja struktur przechowywania danych mogłaby zwiększyć wydajność około 10-krotnie).

Dodatkowo przeprowadzono testy opóźnienia, które pokazały skuteczność buforowania i wpływ zbieracza śmieci. Podczas testowania mierzono opóźnienie po przekierowaniu każdego pakietu przez sterownik w porównaniu do dokładnie znanego czasu wysyłania. Liderami pozostali sterowniki w C i Rust, których wyniki były praktycznie nieodróżnialne dla strumienia 1 miliona pakietów na sekundę (około 20 µs). Dobrze spisał się sterownik w Go, który tylko nieznacznie odstawał od liderów i również utrzymywał się na poziomie 20 µs. Sterownik w C# wykazał opóźnienia rzędu 50 µs.
Największe opóźnienia wykazały sterowniki w JavaScript i Java (opóźnienia przekraczające 300 µs).

Porównanie wydajności sterownika sieciowego w wersjach na 10 języków programowania

Badanie przeprowadzono w celu oceny możliwości opracowania sterowników i komponentów systemu operacyjnego w językach wyższego poziomu niż C. Obecnie 39 na 40 problemów związanych z pamięcią w Linux dotyczy sterowników, dlatego kwestie zastosowania bardziej bezpiecznego języka i przeniesienia sterowników z jądra do przestrzeni użytkownika pozostają aktualne a producenci już aktywnie eksperymentują w tym kierunku (na przykład Google opracował stos TCP dla systemu Fuchsia w języku Go, firma CloudFlare stworzyła implementację protokołu QUIC w języku Rust, firma Apple przeniosła stos TCP na urządzenia mobilne do przestrzeni użytkownika).

W trakcie przeprowadzonej pracy dokonano wniosku, że język Rust jest najlepszym kandydatem do rozwoju sterowników. Możliwości oferowane przez Rust pozwalają na wyeliminowanie problemów wynikających z niskopoziomowego zarządzania pamięcią, kosztem spadku wydajności rzędu około 2%-10% w porównaniu do sterowników w języku C. Języki Go i C# również uznane zostały za odpowiednie do tworzenia komponentów systemowych, w sytuacjach, gdy akceptowane są opóźnienia na poziomie ułamków milisekund, spowodowane zastosowaniem zbieracza śmieci.

Źródło: opennet.ru

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster