Antworten:
Boost.Asio ist eine C ++ - Bibliothek, die mit dem Schwerpunkt Netzwerk begann. Die asynchronen E / A-Funktionen wurden jedoch auf andere Ressourcen erweitert. Da Boost.Asio Teil der Boost-Bibliotheken ist, wird der Umfang geringfügig eingeschränkt, um Doppelungen mit anderen Boost-Bibliotheken zu vermeiden. Beispielsweise stellt Boost.Asio keine Thread-Abstraktion bereit , da Boost.Thread bereits eine bereitstellt.
Andererseits ist libuv eine C-Bibliothek, die als Plattformschicht für Node.js konzipiert wurde . Es bietet eine Abstraktion für IOCP unter Windows, kqueue unter macOS und epoll unter Linux. Darüber hinaus sieht es so aus, als ob der Umfang geringfügig zugenommen hat, um Abstraktionen und Funktionen wie Threads, Threadpools und Kommunikation zwischen Threads einzuschließen.
Im Kern bietet jede Bibliothek eine Ereignisschleife und asynchrone E / A-Funktionen. Sie überschneiden sich mit einigen grundlegenden Funktionen wie Timern, Sockets und asynchronen Vorgängen. libuv hat einen breiteren Anwendungsbereich und bietet zusätzliche Funktionen wie Thread- und Synchronisationsabstraktionen, synchrone und asynchrone Dateisystemoperationen, Prozessmanagement usw. Im Gegensatz dazu bietet Boost.Asios ursprünglicher Netzwerkfokus Oberflächen, da es eine größere Anzahl netzwerkbezogener Funktionen bietet Funktionen wie ICMP, SSL, synchrone blockierende und nicht blockierende Vorgänge sowie übergeordnete Vorgänge für allgemeine Aufgaben, einschließlich Lesen aus einem Stream bis zum Empfang einer neuen Zeile.
Hier ist der kurze Vergleich einiger wichtiger Funktionen nebeneinander. Da Entwickler, die Boost.Asio verwenden, häufig andere Boost-Bibliotheken zur Verfügung haben, habe ich mich dafür entschieden, zusätzliche Boost-Bibliotheken in Betracht zu ziehen, wenn diese entweder direkt bereitgestellt oder einfach zu implementieren sind.
libuv Boost Ereignisschleife: ja Asio Threadpool: ja Asio + Threads Einfädeln: Themen: ja Themen Synchronisation: ja Threads Dateisystemoperationen: Synchron: ja Dateisystem Asynchron: ja Asio + Dateisystem Timer: ja Asio Scatter / Gather I / O [1] : kein Asio Vernetzung: ICMP: kein Asio DNS-Auflösung: Nur asynchrones Asio SSL: kein Asio TCP: Nur asynchrones Asio UDP: Nur asynchrones Asio Signal: Handhabung: ja Asio Senden: ja nein IPC: UNIX Domain Sockets: ja Asio Windows Named Pipe: Ja, Asio Prozessmanagement: Abnehmen: ja Prozess E / A-Rohr: ja Prozess Laichen: ja Prozess Systemabfragen: CPU: ja nein Netzwerkschnittstelle: ja nein Serielle Schnittstellen: nein ja TTY: ja nein Laden der gemeinsam genutzten Bibliothek: ja Erweiterung [2]
2. Boost.Extension wurde Boost nie zur Überprüfung vorgelegt. Wie hier erwähnt , hält der Autor es für vollständig.
Während sowohl libuv als auch Boost.Asio Ereignisschleifen bereitstellen, gibt es einige subtile Unterschiede zwischen den beiden:
uv_default_loop()
) vorsichtig vorgegangen werden , anstatt eine neue Schleife ( uv_loop_new()
) zu erstellen , da möglicherweise eine andere Komponente die Standardschleife ausführt.io_service
sind ihre eigenen Schleifen, mit denen mehrere Threads ausgeführt werden können. Um dies zu unterstützen, führt Boost.Asio eine interne Sperrung auf Kosten einer gewissen Leistung durch . Boost.Asio die Revisionsgeschichte zeigt , dass es wurden Verbesserungen mehrere Leistung die Verriegelung zu minimieren.uv_queue_work
. Die Threadpoolgröße kann über die Umgebungsvariable konfiguriert werden UV_THREADPOOL_SIZE
. Die Arbeit wird außerhalb der Ereignisschleife und innerhalb des Threadpools ausgeführt. Sobald die Arbeit abgeschlossen ist, wird der Abschlusshandler in die Warteschlange gestellt, damit er innerhalb der Ereignisschleife ausgeführt werden kann.io_service
kann dieser problemlos als einer fungieren, da io_service
mehrere Threads aufgerufen werden können run
. Dies überträgt dem Benutzer die Verantwortung für die Thread-Verwaltung und das Thread-Verhalten, wie in diesem Beispiel zu sehen ist .EAGAIN
oder überprüfen EWOULDBLOCK
.kill
und Signalverarbeitung mit uv_signal_t
Typ und uv_signal_*
Operationen.kill
, signal_set
bietet aber Signalverarbeitung.uv_pipe_t
Typ.local::stream_protocol::socket
oder local::datagram_protocol::socket
, und windows::stream_handle
.Während sich die APIs nur aufgrund der Sprache unterscheiden, gibt es hier einige wesentliche Unterschiede:
In Boost.Asio gibt es eine Eins-zu-Eins-Zuordnung zwischen einer Operation und einem Handler. Beispielsweise ruft jede async_write
Operation den WriteHandler einmal auf. Dies gilt für viele libuv-Operationen und -Handler. Libuvs uv_async_send
unterstützt jedoch eine Viele-zu-Eins-Zuordnung. Mehrere uv_async_send
Aufrufe können dazu führen, dass uv_async_cb einmal aufgerufen wird.
Bei Aufgaben wie dem Lesen aus einem Stream / UDP, dem Verarbeiten von Signalen oder dem Warten auf Timer sind die asynchronen Aufrufketten von Boost.Asio etwas expliziter. Mit libuv wird ein Beobachter erstellt, um Interessen an einem bestimmten Ereignis zu bestimmen. Anschließend wird eine Schleife für den Beobachter gestartet, in der ein Rückruf bereitgestellt wird. Nach Erhalt des Interessenereignisses wird der Rückruf aufgerufen. Auf der anderen Seite erfordert Boost.Asio, dass jedes Mal eine Operation ausgegeben wird, wenn die Anwendung an der Behandlung des Ereignisses interessiert ist.
Um diesen Unterschied zu veranschaulichen, gibt es hier eine asynchrone Leseschleife mit Boost.Asio, in der der async_receive
Aufruf mehrmals ausgegeben wird:
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 ); --'
}
Und hier ist das gleiche Beispiel mit libuv, bei dem handle_read
jedes Mal aufgerufen wird, wenn der Beobachter feststellt, dass der Socket Daten enthält:
uv_read_start( socket, alloc_buffer, handle_read ); --.
|
.-------------------------------------------------'
|
V
void handle_read( ... )
{
fprintf( stdout, "got data\n" );
}
Aufgrund der asynchronen Aufrufketten in Boost.Asio und der Beobachter in libuv erfolgt die Speicherzuweisung häufig zu unterschiedlichen Zeiten. Bei Beobachtern verschiebt libuv die Zuordnung, bis ein Ereignis empfangen wird, für dessen Verarbeitung Speicher erforderlich ist. Die Zuweisung erfolgt über einen Benutzerrückruf, der intern in libuv aufgerufen wird, und verschiebt die Freigabeverantwortung der Anwendung. Andererseits erfordern viele der Boost.Asio-Operationen, dass der Speicher zugewiesen wird, bevor die asynchrone Operation ausgegeben wird, wie im Fall des buffer
for async_read
. Boost.Asio bietet die Möglichkeit null_buffers
, auf ein Ereignis zu warten , sodass Anwendungen die Speicherzuweisung verschieben können, bis Speicher benötigt wird, obwohl dies veraltet ist.
Dieser Speicherzuordnungsunterschied zeigt sich auch innerhalb der bind->listen->accept
Schleife. Erstellt mit libuv uv_listen
eine Ereignisschleife, die den Benutzerrückruf aufruft, wenn eine Verbindung zur Annahme bereit ist. Dadurch kann die Anwendung die Zuordnung des Clients verschieben, bis eine Verbindung versucht wird. Auf der anderen Seite listen
ändert Boost.Asio nur den Status des acceptor
. Das async_accept
wartet auf das Verbindungsereignis und erfordert, dass der Peer zugewiesen wird, bevor er aufgerufen wird.
Leider habe ich keine konkreten Benchmark-Zahlen, um libuv und Boost.Asio zu vergleichen. Ich habe jedoch eine ähnliche Leistung bei Verwendung der Bibliotheken in Echtzeit- und Echtzeitanwendungen beobachtet. Wenn harte Zahlen gewünscht werden, kann der Benchmark-Test von libuv als Ausgangspunkt dienen.
Während die Profilerstellung durchgeführt werden sollte, um tatsächliche Engpässe zu identifizieren, sollten Sie außerdem die Speicherzuweisungen berücksichtigen. Für libuv ist die Speicherzuweisungsstrategie hauptsächlich auf den Allokator-Rückruf beschränkt. Auf der anderen Seite erlaubt die Boost.Asio-API keinen Allokator-Rückruf und überträgt stattdessen die Allokationsstrategie an die Anwendung. Die Handler / Rückrufe in Boost.Asio können jedoch kopiert, zugewiesen und freigegeben werden. Mit Boost.Asio können Anwendungen benutzerdefinierte Speicherzuweisungsfunktionen bereitstellen , um eine Speicherzuweisungsstrategie für Handler zu implementieren.
Die Entwicklung von Asio geht mindestens auf das OKT-2004 zurück und wurde am 22. März 2006 nach einer 20-tägigen Peer-Review in Boost 1.35 aufgenommen. Es diente auch als Referenzimplementierung und API für den Vorschlag für eine Netzwerkbibliothek für TR2 . Boost.Asio verfügt über eine beträchtliche Menge an Dokumentation , obwohl seine Nützlichkeit von Benutzer zu Benutzer unterschiedlich ist.
Die API hat auch ein ziemlich konsistentes Gefühl. Darüber hinaus sind die asynchronen Operationen im Namen der Operation explizit. Zum Beispiel accept
ist synchrones Blockieren und async_accept
ist asynchron. Die API bietet kostenlose Funktionen für allgemeine E / A-Aufgaben, z. B. Lesen aus einem Stream bis zum Lesen von a \r\n
. Es wurde auch darauf geachtet, einige netzwerkspezifische Details zu verbergen, z. B. die ip::address_v4::any()
Darstellung der Adresse "Alle Schnittstellen" von 0.0.0.0
.
Schließlich bietet Boost 1.47+ Handler-Tracking , das sich beim Debuggen als nützlich erweisen kann, sowie C ++ 11-Unterstützung.
Basierend auf ihren Github-Diagrammen geht die Entwicklung von Node.js auf mindestens FEB-2009 und die Entwicklung von libuv auf MAR-2011 zurück . Das uvbook ist ein großartiger Ort für eine libuv-Einführung. Die API-Dokumentation finden Sie hier .
Insgesamt ist die API ziemlich konsistent und einfach zu bedienen. Eine Anomalie, die Verwirrung stiften kann, besteht darin, dass uv_tcp_listen
eine Überwachungsschleife erstellt wird. Dies unterscheidet sich von anderen Beobachtern, die im Allgemeinen ein uv_*_start
und uv_*_stop
zwei Funktionen zur Steuerung der Lebensdauer der Überwachungsschleife haben. Außerdem haben einige der uv_fs_*
Operationen eine anständige Anzahl von Argumenten (bis zu 7). Wenn das synchrone und asynchrone Verhalten bei Vorhandensein eines Rückrufs (dem letzten Argument) bestimmt wird, kann die Sichtbarkeit des synchronen Verhaltens verringert werden.
Ein kurzer Blick auf den libuv- Commit-Verlauf zeigt schließlich, dass die Entwickler sehr aktiv sind.
uv_async_send
Anrufe akkumulieren und alle mit einem einzigen Rückruf bearbeiten. Es ist hier dokumentiert . Vielen Dank auch an alle.
OK. Ich habe einige Erfahrung in der Verwendung beider Bibliotheken und kann einige Dinge klären.
Erstens sind diese Bibliotheken konzeptionell sehr unterschiedlich im Design. Sie haben unterschiedliche Architekturen, weil sie unterschiedlich groß sind. Boost.Asio ist eine große Netzwerkbibliothek zur Verwendung mit TCP / UDP / ICMP-Protokollen, POSIX, SSL usw. Libuv ist nur eine Ebene für die plattformübergreifende Abstraktion von IOCP für Node.js. Libuv ist also funktional eine Teilmenge von Boost.Asio (gemeinsame Funktionen nur TCP / UDP-Sockets-Threads, Timer). In diesem Fall können wir diese Bibliotheken anhand weniger Kriterien vergleichen:
Integration mit neuen C ++ - Funktionen: Asio ist besser (Asio 1.51 verwendet ausgiebig asynchrones C ++ 11-Modell, Verschiebungssemantik, variable Vorlagen). In Bezug auf die Reife ist Asio ein stabileres und ausgereifteres Projekt mit guter Dokumentation (im Vergleich zu libuv Header-Beschreibung), viele Informationen im Internet (Videogespräche, Blogs: http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg = 1 usw.) Und sogar Bücher (nicht für Profis, aber dennoch: http://en.highscore.de/cpp/boost/index.html ). Libuv hat nur ein Online-Buch (aber auch gut) http://nikhilm.github.com/uvbook/index.htmlund mehrere Videogespräche, so dass es schwierig sein wird, alle Geheimnisse zu kennen (diese Bibliothek hat viele davon). Für eine genauere Diskussion der Funktionen siehe meine Kommentare unten.
Abschließend sollte ich sagen, dass alles von Ihren Zwecken, Ihrem Projekt und dem abhängt, was Sie konkret vorhaben.
Ein großer Unterschied besteht darin, dass der Autor von Asio (Christopher Kohlhoff) seine Bibliothek auf Aufnahme in die C ++ - Standardbibliothek vorbereitet (siehe http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2175) .pdf und http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4370.html
Hinzufügen des Portabilitätsstatus: Zum Zeitpunkt der Veröffentlichung dieser Antwort und nach meinen eigenen Versuchen: