Odpowiedzi:
Boost.Asio to biblioteka C ++, która zaczęła się od sieci, ale jej asynchroniczne funkcje we / wy zostały rozszerzone na inne zasoby. Dodatkowo, ponieważ Boost.Asio jest częścią bibliotek Boost, jego zakres jest nieco zawężony, aby zapobiec duplikacji z innymi bibliotekami Boost. Na przykład Boost.Asio nie zapewni abstrakcji nici, ponieważ Boost.Thread już ją udostępnia.
Z drugiej strony, libuv jest biblioteką C zaprojektowany jako warstwa platforma node.js . Zapewnia abstrakcję dla IOCP w systemie Windows, kqueue na macOS i epoll w systemie Linux. Dodatkowo wygląda na to, że jego zakres nieznacznie się zwiększył, włączając abstrakcje i funkcje, takie jak wątki, pule wątków i komunikacja między wątkami.
U podstaw każdej biblioteki znajduje się pętla zdarzeń i asynchroniczne funkcje we / wy. Nakładają się na siebie niektóre podstawowe funkcje, takie jak liczniki czasu, gniazda i operacje asynchroniczne. libuv ma szerszy zakres i zapewnia dodatkowe funkcje, takie jak abstrakcje wątków i synchronizacji, synchroniczne i asynchroniczne operacje na systemie plików, zarządzanie procesami itp. W przeciwieństwie do tego, pierwotne ukierunkowanie sieciowe Boost.Asio, ponieważ zapewnia bogatszy zestaw powiązanych z siecią możliwości, takie jak ICMP, SSL, synchroniczne operacje blokowania i nieblokowania oraz operacje wyższego poziomu do typowych zadań, w tym odczytywanie ze strumienia do momentu otrzymania nowej linii.
Oto krótkie porównanie niektórych głównych funkcji. Ponieważ programiści używający Boost.Asio często dysponują innymi bibliotekami Boost, zdecydowałem się rozważyć dodatkowe biblioteki Boost, jeśli są one dostarczane bezpośrednio lub są trywialne do wdrożenia.
libuv Boost Pętla zdarzeń: tak Asio Pula wątków: tak Asio + wątki Gwintowanie: Wątki: tak Wątki Synchronizacja: tak Wątki Operacje systemu plików: Synchroniczny: tak FileSystem Asynchroniczny: tak System plików Asio + Timery: tak Asio Scatter / Gather I / O [1] : no Asio Sieć: ICMP: brak Asio Rozdzielczość DNS: Asio tylko Async SSL: bez Asio TCP: Asio-only asio UDP: Asio-only asio Sygnał: Obsługa: tak Asio Wysyłanie: tak nie IPC: Gniazda domen UNIX: tak Asio Nazwana rura systemu Windows: tak Asio Zarządzanie procesem: Odłączanie: tak Proces Rura I / O: tak Proces Tarło: tak Proces Zapytania systemowe: CPU: tak nie Interfejs sieciowy: tak nie Porty szeregowe: nie tak TTY: tak nie Ładowanie biblioteki współdzielonej: tak Rozszerzenie [2]
1. rozproszony / Zebrać I / O .
2. Zwiększenie . Rozszerzenie nigdy nie zostało przesłane do oceny w ramach wzmocnienia. Jak wspomniano tutaj , autor uważa to za kompletne.
Chociaż zarówno libuv, jak i Boost.Asio zapewniają pętle zdarzeń, istnieją między nimi pewne subtelne różnice:
uv_default_loop()
), zamiast tworzyć nową pętlę ( uv_loop_new()
), ponieważ inny komponent może uruchamiać domyślną pętlę.io_service
są własnymi pętlami, które pozwalają na uruchomienie wielu wątków. Aby wesprzeć ten Boost.Asio wykonuje wewnętrzne blokowanie kosztem pewnej wydajności . Historia wersji Boost.Asio wskazuje, że wprowadzono kilka ulepszeń wydajności w celu zminimalizowania blokowania.uv_queue_work
. Rozmiar puli wątków można konfigurować za pomocą zmiennej środowiskowej UV_THREADPOOL_SIZE
. Praca zostanie wykonana poza pętlą zdarzeń i w puli wątków. Po zakończeniu pracy moduł obsługi zakończenia będzie w kolejce do uruchomienia w pętli zdarzeń.io_service
może z łatwością działać jako jeden, ponieważ io_service
pozwala na wywołanie wielu wątków run
. To nakłada na użytkownika odpowiedzialność za zarządzanie wątkami i ich zachowanie, co można zobaczyć w tym przykładzie.EAGAIN
lub EWOULDBLOCK
.kill
i obsługę sygnałów z ich uv_signal_t
rodzajem i uv_signal_*
działaniem.kill
, ale signal_set
zapewnia obsługę sygnału.uv_pipe_t
typu.local::stream_protocol::socket
lub local::datagram_protocol::socket
, i windows::stream_handle
.Chociaż interfejsy API różnią się w zależności od samego języka, oto kilka kluczowych różnic:
W ramach Boost.Asio istnieje odwzorowanie jeden na jednego między operacją a modułem obsługi. Na przykład każda async_write
operacja wywoła WriteHandler jeden raz. Dotyczy to wielu operacji i programów obsługi libuv. Jednak libuv uv_async_send
obsługuje mapowanie wiele do jednego. Wiele uv_async_send
wywołań może spowodować, że uv_async_cb zostanie wywołany jeden raz.
W przypadku zadań, takich jak odczyt ze strumienia / UDP, obsługa sygnałów lub oczekiwanie na liczniki czasu, asynchroniczne łańcuchy połączeń Boost.Asio są nieco bardziej wyraźne. W libuv tworzony jest obserwator, który określa zainteresowania w konkretnym wydarzeniu. Następnie uruchamiana jest pętla dla obserwatora, w której zapewnione jest wywołanie zwrotne. Po otrzymaniu zdarzenia zainteresowania zostanie wywołane oddzwonienie. Z drugiej strony Boost.Asio wymaga wykonania operacji za każdym razem, gdy aplikacja jest zainteresowana obsługą zdarzenia.
Aby zilustrować tę różnicę, oto asynchroniczna pętla odczytu z funkcją Boost.Asio, w której async_receive
wywołanie zostanie wykonane wielokrotnie:
void start()
{
socket.async_receive( buffer, handle_read ); ----.
} |
.----------------------------------------------'
| .---------------------------------------.
V V |
void handle_read( ... ) |
{ |
std::cout << "got data" << std::endl; |
socket.async_receive( buffer, handle_read ); --'
}
A oto ten sam przykład z libuv, gdzie handle_read
jest wywoływany za każdym razem, gdy obserwator zauważy, że gniazdo ma dane:
uv_read_start( socket, alloc_buffer, handle_read ); --.
|
.-------------------------------------------------'
|
V
void handle_read( ... )
{
fprintf( stdout, "got data\n" );
}
W wyniku asynchronicznych łańcuchów wywołań w Boost.Asio i obserwatorów w libuv alokacja pamięci często występuje w różnych momentach. W przypadku obserwatorów libuv odkłada alokację, dopóki nie otrzyma zdarzenia wymagającego pamięci do obsługi. Alokacja odbywa się poprzez wywołanie zwrotne użytkownika, wywoływane wewnętrznie w libuv i odracza odpowiedzialność aplikacji za dezalokację. Z drugiej strony wiele operacji Boost.Asio wymaga przydzielenia pamięci przed wykonaniem operacji asynchronicznej, na przykład w przypadku buffer
for async_read
. Boost.Asio zapewnia null_buffers
, że można go użyć do nasłuchiwania zdarzenia, umożliwiając aplikacjom odraczanie alokacji pamięci do momentu, gdy pamięć będzie potrzebna, chociaż jest to przestarzałe.
Ta różnica alokacji pamięci pojawia się również w bind->listen->accept
pętli. W libuv uv_listen
tworzy pętlę zdarzeń, która wywoła oddzwonienie użytkownika, gdy połączenie będzie gotowe do zaakceptowania. Umożliwia to aplikacji odroczenie alokacji klienta do momentu próby nawiązania połączenia. Z drugiej strony Boost.Asio listen
zmienia tylko stan acceptor
. W async_accept
nasłuchuje przypadku połączenia, a wymaga peer to być przydzielane przed wywoływany.
Niestety nie mam konkretnych liczb porównawczych do porównania libuv i Boost.Asio. Jednak zaobserwowałem podobną wydajność przy użyciu bibliotek w aplikacjach w czasie rzeczywistym i prawie w czasie rzeczywistym. Jeśli pożądane są liczby twarde, punktem odniesienia może być test porównawczy libuv .
Ponadto, mimo że należy przeprowadzić profilowanie w celu zidentyfikowania faktycznych wąskich gardeł, należy pamiętać o przydziałach pamięci. W przypadku libuv strategia alokacji pamięci ogranicza się przede wszystkim do wywołania zwrotnego alokatora. Z drugiej strony, API Boost.Asio nie pozwala na wywołanie zwrotne alokatora, a zamiast tego wypycha strategię alokacji do aplikacji. Jednak procedury obsługi / wywołania zwrotne w Boost.Asio mogą być kopiowane, przydzielane i zwalniane. Boost.Asio pozwala aplikacjom na zapewnienie niestandardowych funkcji alokacji pamięci w celu zaimplementowania strategii alokacji pamięci dla programów obsługi.
Rozwój Asio sięga co najmniej OCT-2004 i został przyjęty do Boost 1.35 22-MAR-2006 po przejściu 20-dniowej wzajemnej oceny. Służył również jako implementacja referencyjna i API dla Networking Library Propission for TR2 . Boost.Asio ma sporo dokumentacji , chociaż jego użyteczność różni się w zależności od użytkownika.
Interfejs API ma również dość spójne działanie. Ponadto operacje asynchroniczne są jawne w nazwie operacji. Na przykład accept
blokuje synchronicznie i async_accept
jest asynchroniczny. Interfejs API zapewnia bezpłatne funkcje do typowych zadań we / wy, na przykład czytanie ze strumienia, aż do \r\n
odczytu. Zwrócono również uwagę na ukrycie niektórych szczegółów dotyczących sieci, takich jak ip::address_v4::any()
reprezentacja adresu „wszystkich interfejsów” 0.0.0.0
.
Wreszcie, Boost 1.47+ zapewnia śledzenie procedur obsługi , które mogą okazać się przydatne podczas debugowania, a także obsługę C ++ 11.
Na podstawie ich wykresów github rozwój Node.js sięga co najmniej FEB-2009 , a rozwój libuv - MAR-2011 . Książka uvbook to świetne miejsce na wprowadzenie libuv. Dokumentacja API jest tutaj .
Ogólnie rzecz biorąc, interfejs API jest dość spójny i łatwy w użyciu. Jedną z anomalii, które mogą być źródłem nieporozumień, jest uv_tcp_listen
tworzenie pętli obserwatora. Jest to inaczej niż w innych obserwatorów, które mają zwykle uv_*_start
i uv_*_stop
parę funkcji kontrolowania trwałości pętli obserwatora. Ponadto niektóre uv_fs_*
operacje mają przyzwoitą liczbę argumentów (do 7). Dzięki określeniu zachowania synchronicznego i asynchronicznego na podstawie wywołania zwrotnego (ostatniego argumentu) widoczność zachowania synchronicznego może zostać zmniejszona.
Wreszcie, szybkie spojrzenie na historię zatwierdzania libuv pokazuje, że programiści są bardzo aktywni.
uv_async_send
połączeń i obsługiwać je wszystkie za pomocą jednego połączenia zwrotnego. Jest to udokumentowane tutaj . Dziękuję również wszystkim.
Dobrze. Mam pewne doświadczenie w korzystaniu z obu bibliotek i potrafię wyjaśnić niektóre rzeczy.
Po pierwsze, z koncepcyjnego punktu widzenia biblioteki te mają zupełnie inną budowę. Mają różne architektury, ponieważ mają różną skalę. Boost.Asio to duża biblioteka sieciowa przeznaczona do użycia z protokołami TCP / UDP / ICMP, POSIX, SSL i tak dalej. Libuv to przede wszystkim warstwa do wieloplatformowej abstrakcji IOCP dla Node.js. Tak więc libuv jest funkcjonalnie podzbiorem Boost.Asio (wspólne funkcje tylko wątki TCP / UDP Sockets, timery). W takim przypadku możemy porównać te biblioteki przy użyciu tylko kilku kryteriów:
Integracja z nowymi funkcjami C ++: Asio jest lepsze (Asio 1.51 intensywnie korzysta z modelu asynchronicznego C ++ 11, semantyki przenoszenia, szablonów variadic). Pod względem dojrzałości Asio jest bardziej stabilnym i dojrzałym projektem z dobrą dokumentacją (jeśli porównać go z libuv opis nagłówków), wiele informacji w Internecie (rozmowy wideo, blogi: http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg = 1 itd.), A nawet książki (nie dla profesjonalistów, ale mimo to: http://en.highscore.de/cpp/boost/index.html ). Libuv ma tylko jedną książkę online (ale także dobrą) http://nikhilm.github.com/uvbook/index.htmli kilka rozmów wideo, więc trudno będzie poznać wszystkie sekrety (ta biblioteka ma ich wiele). Aby uzyskać bardziej szczegółowe omówienie funkcji, zobacz moje komentarze poniżej.
Podsumowując, powinienem powiedzieć, że wszystko zależy od twoich celów, twojego projektu i tego, co konkretnie zamierzasz zrobić.
Ogromną różnicą jest to, że autor Asio (Christopher Kohlhoff) przygotowuje swoją bibliotekę do włączenia do standardowej biblioteki C ++, patrz http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2175 .pdf i http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4370.html
Dodanie statusu przenośności: od opublikowania tej odpowiedzi i zgodnie z moimi próbami: