Je serais intéressé par des aspects comme:
Boost.Asio est une bibliothèque C++ qui a commencé par se concentrer sur la mise en réseau, mais ses fonctionnalités d'E/S asynchrones ont été étendues à d'autres ressources. De plus, Boost.Asio faisant partie des bibliothèques Boost, sa portée est légèrement réduite pour éviter les doubles emplois avec d'autres bibliothèques Boost. Par exemple, Boost.Asio ne fournira pas une abstraction de thread, car Boost.Thread en fournit déjà une.
D'autre part, libuv est une bibliothèque C conçue pour être la couche de plate-forme de Node.js . Il fournit une abstraction pour IOCP sous Windows, kqueue sous macOS et epoll sous Linux. En outre, il semble que sa portée ait légèrement augmenté pour inclure des abstractions et des fonctionnalités, telles que les threads, les pools de threads et la communication inter-thread.
À la base, chaque bibliothèque fournit une boucle d'événement et des fonctionnalités d'E/S asynchrones. Elles se chevauchent pour certaines des fonctionnalités de base, telles que les temporisations, les sockets et les opérations asynchrones. libuv a une portée plus large et fournit des fonctionnalités supplémentaires, telles que des abstractions de threads et de synchronisation, des opérations de système de fichiers synchrones et asynchrones, la gestion des processus, etc. En revanche, le focus original de Boost.Asio sur les réseaux, car il fournit un ensemble plus riche de des fonctionnalités telles que ICMP, SSL, des opérations de blocage synchrone et des opérations sans blocage et des opérations de niveau supérieur pour des tâches courantes, notamment la lecture d'un flux jusqu'à la réception d'une nouvelle ligne.
Voici la brève comparaison côte à côte sur certaines des principales caractéristiques. Les développeurs utilisant Boost.Asio ayant souvent d'autres bibliothèques Boost disponibles, j'ai choisi d'envisager des bibliothèques Boost supplémentaires si elles sont fournies directement ou si leur mise en œuvre est triviale.
libuv Boost Boucle d'événement: oui Asio Threadpool: oui Asio + Threads Threading: Threads: oui Threads Synchronisation : oui Fils Opérations sur le système de fichiers: Synchrone: oui Système de fichiers Asynchrone: oui Asio + Système de fichiers Temporisateurs: oui Asio Dispersion/Collecte I/O[1]: no Asio Réseau: ICMP: no Asio Résolution DNS: uniquement asynchrone Asio SSL: non Asio TCP: uniquement asynchrone Asio UDP: Async seulement Asio Signal: Traitement: oui Asio Envoi: oui non IPC: UNIX Domain Sockets: yes Asio Windows Nipe Pipe: yes Asio Gestion des processus: Détachement: yes Process I/O Pipe: yes Process Génération: oui Processus Requêtes système: CPU: oui non Interface réseau: oui non Ports série: non oui TTY: oui non Chargement de la bibliothèque partagée: oui Extension[2]
1. Scatter/Gather I/O .
2. Boost.Extension n'a jamais été soumis pour examen à Boost. Comme indiqué ici , l'auteur considère qu'il est complet.
Bien que libuv et Boost.Asio fournissent des boucles d'événement, il existe quelques différences subtiles entre les deux:
uv_default_loop()
) plutôt que de créer une nouvelle boucle (uv_loop_new()
), car un autre composant exécute peut-être la boucle par défaut.io_service
sont leurs propres boucles permettant l'exécution de plusieurs threads. Pour supporter cela, Boost.Asio effectue verrouillage interne au prix de quelques performance . La révision de Boost.Asio historique indique que plusieurs améliorations des performances ont été apportées pour minimiser le verrouillage.uv_queue_work
. La taille du pool de threads est configurable via la variable d'environnement UV_THREADPOOL_SIZE
. Le travail sera exécuté en dehors de la boucle d'événements et dans le pool de threads. Une fois le travail terminé, le gestionnaire d'achèvement sera mis en file d'attente pour s'exécuter dans la boucle d'événements.io_service
Peut facilement fonctionner comme tel à la suite de io_service
Permettant à plusieurs threads d'appeler run
. Cela place la responsabilité de la gestion des threads et du comportement sur l'utilisateur, comme on peut le voir dans l'exemple this .EAGAIN
ou EWOULDBLOCK
.kill
et un traitement du signal avec ses opérations uv_signal_t
et uv_signal_*
.kill
, mais son signal_set
assure la gestion du signal.uv_pipe_t
.local::stream_protocol::socket
ou local::datagram_protocol::socket
, et windows::stream_handle
.Bien que les API diffèrent en fonction du langage, voici quelques différences essentielles:
Dans Boost.Asio, il existe un mappage univoque entre une opération et un gestionnaire. Par exemple, chaque opération async_write
invoquera une fois WriteHandler . Ceci est vrai pour beaucoup d'opérations et de gestionnaires de libuv. Cependant, uv_async_send
De libuv prend en charge un mappage plusieurs à un. Plusieurs appels uv_async_send
Peuvent entraîner la uv_async_cb être appelé une fois.
Lorsque vous traitez une tâche, telle que la lecture d'un flux/UDP, la gestion de signaux ou l'attente de minuteries, les chaînes d'appels asynchrones de Boost.Asio sont un peu plus explicites. Avec libuv, un observateur est créé pour désigner les intérêts d'un événement particulier. Une boucle est alors lancée pour l'observateur, où un rappel est fourni. À la réception de l'événement d'intérêt, le rappel sera appelé. D'autre part, Boost.Asio nécessite qu'une opération soit émise chaque fois que l'application est intéressée par le traitement de l'événement.
Pour illustrer cette différence, voici une boucle de lecture asynchrone avec Boost.Asio, dans laquelle l'appel async_receive
Sera émis à plusieurs reprises:
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 ); --'
}
Et voici le même exemple avec libuv, où handle_read
Est invoqué chaque fois que l'observateur constate que le socket contient des données:
uv_read_start( socket, alloc_buffer, handle_read ); --.
|
.-------------------------------------------------'
|
V
void handle_read( ... )
{
fprintf( stdout, "got data\n" );
}
En raison des chaînes d'appels asynchrones dans Boost.Asio et des observateurs dans libuv, l'allocation de mémoire se produit souvent à des moments différents. Avec les observateurs, libuv diffère l’allocation jusqu’à ce qu’il reçoive un événement dont la gestion nécessite de la mémoire. L'attribution est effectuée via un rappel de l'utilisateur, invoqué de manière interne à libuv, et reporte la responsabilité de désallocation de l'application. D'autre part, de nombreuses opérations Boost.Asio nécessitent l'allocation de mémoire avant d'émettre l'opération asynchrone, comme dans le cas de buffer
pour async_read
. Boost.Asio fournit null_buffers
, qui peut être utilisé pour écouter un événement, ce qui permet aux applications de différer l’allocation de mémoire jusqu’à ce que la mémoire soit nécessaire, bien que ce soit obsolète.
Cette différence d'allocation de mémoire se présente également dans la boucle bind->listen->accept
. Avec libuv, uv_listen
Crée une boucle d'événements qui appelle le rappel de l'utilisateur lorsqu'une connexion est prête à être acceptée. Cela permet à l'application de différer l'allocation du client jusqu'à la tentative de connexion. D'autre part, listen
de Boost.Asio ne modifie que l'état de acceptor
. Le async_accept
écoute l'événement de connexion et exige que l'homologue soit attribué avant d'être appelé.
Malheureusement, je n'ai pas de chiffres de référence concrets pour comparer libuv et Boost.Asio. Cependant, j'ai observé des performances similaires en utilisant les bibliothèques dans des applications temps réel et quasi temps réel. Si vous souhaitez obtenir des chiffres précis, le test test d'évaluation de libuv peut servir de point de départ.
En outre, même si le profilage doit être effectué pour identifier les goulots d'étranglement réels, tenez compte des allocations de mémoire. Pour libuv, la stratégie d'allocation de mémoire est principalement limitée au rappel de l'allocateur. D'autre part, l'API de Boost.Asio ne permet pas un rappel d'allocateur, mais pousse la stratégie d'allocation à l'application. Toutefois, les gestionnaires/rappels dans Boost.Asio peuvent être copiés, alloués et désalloués. Boost.Asio permet aux applications de fournir allocation de mémoire personnalisée des fonctions permettant de mettre en œuvre une stratégie d’allocation de mémoire pour les gestionnaires.
Le développement d'Asio remonte au moins à octobre 2004. Il a été accepté dans Boost 1.35 le 22 mars 2006 après avoir fait l'objet d'un examen par les pairs de 20 jours. Il a également servi d'implémentation de référence et d'API pour Proposition de bibliothèque de mise en réseau pour TR2 . Boost.Asio contient pas mal de documentation , bien que son utilité varie d’un utilisateur à l’autre.
Les API ont également une sensation assez cohérente. En outre, les opérations asynchrones sont explicites dans le nom de l'opération. Par exemple, accept
est un blocage synchrone et async_accept
Est asynchrone. L'API fournit des fonctions gratuites pour les tâches d'E/S courantes, par exemple, la lecture d'un flux jusqu'à la lecture d'un \r\n
. Nous avons également veillé à masquer certains détails spécifiques au réseau, tels que la ip::address_v4::any()
représentant l'adresse "toutes interfaces" de 0.0.0.0
.
Enfin, Boost 1.47+ fournit suivi du gestionnaire , ce qui peut s'avérer utile lors du débogage, ainsi que la prise en charge de C++ 11.
Basé sur leurs graphes github, le développement de Node.js remonte au moins à FEB-2009 , et celui de libuv à MAR-2011 . Le vbook est un endroit idéal pour une introduction à libuv. La documentation de l'API est ici .
Globalement, l’API est relativement cohérente et facile à utiliser. Une anomalie qui pourrait être source de confusion est que uv_tcp_listen
Crée une boucle d'observation. Cela diffère de celui des autres observateurs disposant généralement d'une paire de fonctions uv_*_start
Et uv_*_stop
Pour contrôler la durée de vie de la boucle d'observation. De plus, certaines opérations uv_fs_*
Ont une quantité d'arguments décente (jusqu'à 7). Le comportement synchrone et asynchrone étant déterminé par la présence d'un rappel (le dernier argument), la visibilité du comportement synchrone peut être diminuée.
Enfin, un rapide coup d'œil sur libuv historique des mises à jour montre que les développeurs sont très actifs.
D'accord. J'ai une certaine expérience dans l'utilisation des deux bibliothèques et je peux clarifier certaines choses.
Premièrement, d’un point de vue conceptuel, ces bibliothèques ont une conception très différente. Ils ont des architectures différentes, car ils sont d'échelle différente. Boost.Asio est une grande bibliothèque réseau destinée à être utilisée avec les protocoles TCP/UDP/ICMP, POSIX, SSL, etc. Libuv est juste une couche d'abstraction multi-plateforme de IOCP pour Node.js, principalement. Donc, libuv est fonctionnellement un sous-ensemble de Boost.Asio (fonctionnalités communes uniquement les threads Sockets TCP/UDP, les timers). Dans ce cas, nous pouvons comparer ces bibliothèques en utilisant seulement quelques critères:
Intégration avec les nouvelles fonctionnalités C++: Asio est meilleur (Asio 1.51 utilise abondamment le modèle asynchrone C++ 11, déplace la sémantique, modèles variadiques). En ce qui concerne la maturité, Asio est un projet plus stable et mature avec une bonne documentation (si on le compare à libuv description des en-têtes), beaucoup d’informations sur Internet (vidéoconférences, blogs: http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with -boostasio? pg = 1 , etc.) et même des livres (pas pour les professionnels mais quand même: http://en.highscore.de/cpp/boost/index.html ). Libuv n’a qu’un seul livre en ligne (mais aussi bon) http://nikhilm.github.com/uvbook/index.html et plusieurs vidéoconférences, il sera donc difficile de connaître tous les secrets la bibliothèque en a beaucoup). Pour plus de détails sur les fonctions, voir mes commentaires ci-dessous.
En conclusion, je dois dire que tout dépend de vos objectifs, de votre projet et de ce que vous avez l'intention de faire concrètement.
Une énorme différence est l'auteur d'Asio (Christopher Kohlhoff) prépare sa bibliothèque pour l'inclure dans la bibliothèque standard C++, voir http://www.open-std.org/jtc1/sc22/wg21/docs/papers /2007/n2175.pdf et http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4370.html