258 votes

Comment la libuv se compare-t-elle à Boost/ASIO ?

Je serais intéressé par des aspects comme :

  • portée/caractéristiques
  • performance
  • échéance

24 votes

Posons cette question et obtenons de bonnes réponses !

0 votes

\o / J'espère que nous aurons des réponses perspicaces !

541voto

Tanner Sansbury Points 17166

Portée

Boost.Asio est une bibliothèque C++ qui a commencé par se concentrer sur les réseaux, mais ses capacités d'E/S asynchrones ont été étendues à d'autres ressources. De plus, Boost.Asio faisant partie des bibliothèques Boost, son champ d'application est légèrement réduit pour éviter les doublons avec d'autres bibliothèques Boost. Par exemple, Boost.Asio ne fournira pas d'abstraction de threads, comme le fait la bibliothèque Boost.Thread en fournit déjà un.

D'un autre côté, libuv est une bibliothèque C conçue pour être la couche de plateforme pour Node.js . Il fournit une abstraction pour IOCP sur Windows, kqueue sur macOS, et epoll sur Linux. De plus, il semble que son champ d'application se soit légèrement élargi pour inclure des abstractions et des fonctionnalités, telles que les threads, les pools de threads et la communication inter-threads.

Au fond, chaque bibliothèque fournit une boucle d'événements et des capacités d'E/S asynchrones. Elles se chevauchent pour certaines des fonctionnalités de base, comme les timers, les sockets et les opérations asynchrones. libuv a une portée plus large, et fournit des fonctionnalités supplémentaires, comme les abstractions de threads et de synchronisation, les opérations synchrones et asynchrones du système de fichiers, la gestion des processus, etc. En revanche, l'orientation réseau originale de Boost.Asio fait surface, car elle fournit un ensemble plus riche de capacités liées au réseau, telles que ICMP, SSL, des opérations synchrones bloquantes et non bloquantes, et des opérations de plus haut niveau pour les tâches courantes, y compris la lecture d'un flux jusqu'à la réception d'une nouvelle ligne.


Liste des fonctionnalités

Voici une brève comparaison côte à côte de quelques-unes des principales caractéristiques. Puisque les développeurs utilisant Boost.Asio ont souvent d'autres bibliothèques Boost disponibles, j'ai choisi de considérer les bibliothèques Boost supplémentaires si elles sont soit directement fournies, soit triviales à mettre en œuvre.

                         libuv          Boost
Event Loop:              yes            Asio
Threadpool:              yes            Asio + Threads
Threading:              
  Threads:               yes            Threads
  Synchronization:       yes            Threads
File System Operations:
  Synchronous:           yes            FileSystem
  Asynchronous:          yes            Asio + Filesystem
Timers:                  yes            Asio
Scatter/Gather I/O\[1\]:    no             Asio
Networking:
  ICMP:                  no             Asio
  DNS Resolution:        async-only     Asio
  SSL:                   no             Asio
  TCP:                   async-only     Asio
  UDP:                   async-only     Asio
Signal:
  Handling:              yes            Asio
  Sending:               yes            no
IPC:
  UNIX Domain Sockets:   yes            Asio
  Windows Named Pipe:    yes            Asio
Process Management:
  Detaching:             yes            Process
  I/O Pipe:              yes            Process
  Spawning:              yes            Process
System Queries:
  CPU:                   yes            no
  Network Interface:     yes            no
Serial Ports:            no             yes
TTY:                     yes            no
Shared Library Loading:  yes            Extension\[2\]

1. <a href="https://en.wikipedia.org/wiki/Vectored_I/O" rel="noreferrer">E/S de diffusion/récolte </a>.

2. <a href="https://web.archive.org/web/20150527052526im_/http://boost-extension.redshoelace.com:80/docs/boost/extension/index.html" rel="noreferrer">Boost.Extension </a>n'a jamais été soumis pour examen à Boost. Comme indiqué <a href="https://web.archive.org/web/20170318102850im_/http://blog.redshoelace.com/2010/01/explanation.html" rel="noreferrer">aquí </a>l'auteur considère qu'il est complet.

Boucle d'événement

Bien que la libuv et Boost.Asio fournissent toutes deux des boucles d'événements, il existe quelques différences subtiles entre les deux :

  • Bien que libuv supporte les boucles d'événements multiples, elle ne supporte pas l'exécution de la même boucle à partir de plusieurs threads. Pour cette raison, il faut faire attention lors de l'utilisation de la boucle par défaut ( uv_default_loop() ), plutôt que de créer une nouvelle boucle ( uv_loop_new() ), car un autre composant peut exécuter la boucle par défaut.
  • Boost.Asio n'a pas la notion de boucle par défaut ; toutes les io_service sont leurs propres boucles qui permettent l'exécution de plusieurs threads. Pour cela, Boost.Asio effectue verrouillage interne au prix d'une certaine performance . Révision de Boost.Asio histoire indique qu'il y a eu plusieurs améliorations des performances pour minimiser le verrouillage.

Threadpool

  • La libuv fournit un threadpool à travers 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énement et dans le pool de threads. Une fois le travail terminé, le gestionnaire d'achèvement sera mis en file d'attente pour être exécuté dans la boucle d'événements.
  • Bien que Boost.Asio ne fournisse pas de pool de threads, la fonction io_service peuvent facilement fonctionner comme une seule entité grâce à io_service permettant à plusieurs threads d'invoquer run . Cela confie la responsabilité de la gestion et du comportement des fils à l'utilisateur, comme on peut le voir dans l'exemple suivant este exemple.

Threading et synchronisation

  • libuv fournit une abstraction aux threads et aux types de synchronisation.
  • Boost.Thread fournit un thread et des types de synchronisation. La plupart de ces types suivent de près la norme C++11, mais fournissent également quelques extensions. Comme Boost.Asio permet à plusieurs threads d'exécuter une seule boucle d'événement, il fournit des types de synchronisation pour les threads. brins comme un moyen de créer une invocation séquentielle de gestionnaires d'événements sans utiliser de mécanismes de verrouillage explicites.

Opérations sur le système de fichiers

  • libuv fournit une abstraction pour de nombreuses opérations du système de fichiers. Il y a une fonction par opération, et chaque opération peut être soit synchrone bloquante, soit asynchrone. Si un callback est fourni, alors l'opération sera exécutée de manière asynchrone dans un threadpool interne. Si un callback n'est pas fourni, alors l'appel sera synchrone et bloquant.
  • Boost.Filesystem fournit des appels synchrones bloquants pour de nombreuses opérations du système de fichiers. Ceux-ci peuvent être combinés avec Boost.Asio et un threadpool pour créer des opérations asynchrones du système de fichiers.

Mise en réseau

  • libuv supporte les opérations asynchrones sur les sockets UDP et TCP, ainsi que la résolution DNS. Les développeurs d'applications doivent être conscients que les descripteurs de fichiers sous-jacents sont définis comme non bloquants. Par conséquent, les opérations synchrones natives devraient contrôler les valeurs de retour et errno pour EAGAIN o EWOULDBLOCK .
  • Boost.Asio est un peu plus riche dans son support réseau. En plus de nombreuses fonctionnalités de la mise en réseau de libuv, Boost.Asio supporte les sockets SSL et ICMP. De plus, Boost.Asio fournit des opérations synchrones bloquantes et synchrones non bloquantes, en plus de ses opérations asynchrones. Il y a de nombreuses fonctions indépendantes qui fournissent des opérations communes de plus haut niveau, telles que la lecture d'un nombre déterminé d'octets, ou jusqu'à ce qu'un caractère de délimitation spécifié soit lu.

Signal

  • libuv fournit une abstraction kill et le traitement des signaux avec son uv_signal_t et uv_signal_* opérations.
  • Boost.Asio ne fournit pas une abstraction pour kill mais son signal_set assure la gestion des signaux.

IPC


Différences d'API

Bien que les API soient différentes en fonction du seul langage, voici quelques différences essentielles :

Association des opérateurs et des manutentionnaires

Dans Boost.Asio, il existe une correspondance biunivoque entre une opération et un gestionnaire. Par exemple, chaque async_write invoquera l'opération WriteHandler une fois. Ceci est vrai pour la plupart des opérations et des gestionnaires de libuv. Cependant, la fonction uv_async_send prend en charge un mappage de plusieurs à un. Multiple uv_async_send Les appels peuvent entraîner le uv_async_cb être appelé une fois.

Chaînes d'appels et boucles d'observation

Lorsqu'il s'agit de tâches, comme la lecture d'un flux/UDP, la gestion de signaux ou l'attente de temporisations, les chaînes d'appel asynchrones de Boost.Asio sont un peu plus explicites. Avec libuv, un watcher est créé pour désigner les intérêts dans un événement particulier. Une boucle est alors lancée pour le watcher, où un callback est fourni. A la réception de l'événement d'intérêts, la callback sera invoquée. D'autre part, Boost.Asio exige 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, où le async_receive L'appel sera émis plusieurs fois :

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 le watcher observe que la socket a des données :

uv_read_start( socket, alloc_buffer, handle_read ); --.
                                                      |
    .-------------------------------------------------'
    |
    V
void handle_read( ... )
{
  fprintf( stdout, "got data\n" );
}

Allocation de mémoire

En raison des chaînes d'appel asynchrones de Boost.Asio et des observateurs de libuv, l'allocation de la mémoire se produit souvent à des moments différents. Avec les watchers, libuv reporte l'allocation jusqu'à ce qu'elle reçoive un événement qui nécessite de la mémoire à gérer. L'allocation se fait par le biais d'une callback utilisateur, invoquée en interne à libuv, et reporte la responsabilité de la désallocation sur l'application. D'un autre côté, de nombreuses opérations de Boost.Asio requièrent que la mémoire soit allouée avant d'émettre l'opération asynchrone, comme dans le cas de la fonction buffer pour async_read . Boost.Asio fournit null_buffers qui peut être utilisé pour écouter un événement, permettant aux applications de différer l'allocation de mémoire jusqu'à ce que la mémoire soit nécessaire, bien que cela soit déprécié.

Cette différence d'allocation de mémoire se présente également dans le cadre de la bind->listen->accept boucle. Avec libuv, uv_listen crée une boucle d'événements qui invoquera 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'à ce qu'une connexion soit tentée. D'un autre côté, la méthode de Boost.Asio listen ne change que l'état de la acceptor . Le site async_accept écoute l'événement de connexion, et exige que le pair soit alloué avant d'être invoqué.


Performance

Malheureusement, je ne dispose 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 en temps réel et en temps quasi réel. Si vous souhaitez obtenir des chiffres concrets, l'indice de performance de libuv test d'évaluation peut servir de point de départ.

De plus, alors que le profilage devrait être effectué pour identifier les goulots d'étranglement réels, soyez attentif aux allocations de mémoire. Pour libuv, la stratégie d'allocation de mémoire est principalement limitée au callback de l'allocateur. D'autre part, l'API de Boost.Asio ne permet pas de callback d'allocateur, et pousse plutôt la stratégie d'allocation à l'application. Cependant, les handlers/callbacks de 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 afin de mettre en œuvre une stratégie d'allocation de mémoire pour les gestionnaires.


Maturité

Boost.Asio

Le développement d'Asio remonte à au moins OCT-2004, et il a été accepté dans Boost 1.35 le 22-MAR-2006 après avoir subi une évaluation par les pairs de 20 jours. Il a également servi d'implémentation de référence et d'API pour les projets suivants Proposition de bibliothèque en réseau pour TR2 . Boost.Asio a une bonne dose de documentation bien que son utilité varie d'un utilisateur à l'autre.

L'API a également une sensation assez cohérente. De plus, 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'à ce qu'un message d'erreur soit envoyé. \r\n est lu. Une attention particulière a également été accordée à la dissimulation de certains détails spécifiques au réseau, tels que le nom de l'utilisateur et le numéro de téléphone. ip::address_v4::any() représentant l'adresse "toutes les interfaces" de 0.0.0.0 .

Enfin, Boost 1.47+ fournit suivi des manipulateurs qui peut s'avérer utile lors du débogage, ainsi que la prise en charge de C++11.

libuv

Sur la base de leurs graphiques github, le développement de Node.js remonte à au moins FEB-2009 et le développement de la Libuv remonte à MAR-2011 . Le site uvbook est un endroit idéal pour une introduction à la libuv. La documentation de l'API est aquí .

Dans l'ensemble, l'API est assez cohérente et facile à utiliser. Une anomalie qui peut être une source de confusion est la suivante uv_tcp_listen crée une boucle d'observation. Ceci est différent des autres observateurs qui ont généralement une uv_*_start y uv_*_stop paire de fonctions pour contrôler la vie de la boucle watcher. De plus, certaines des uv_fs_* Les opérations ont une quantité décente d'arguments (jusqu'à 7). Le comportement synchrone et asynchrone étant déterminé par la présence d'un callback (le dernier argument), la visibilité du comportement synchrone peut être diminuée.

Enfin, un coup d'œil rapide sur la libuv historique des commandes montre que les développeurs sont très actifs.

49voto

Ok. J'ai une certaine expérience dans l'utilisation des deux bibliothèques et je peux clarifier certaines choses.

Tout d'abord, d'un point de vue conceptuel, ces bibliothèques sont de conception très différente. Elles ont des architectures différentes, car elles sont de taille 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 n'est qu'une couche d'abstraction multiplateforme pour les protocoles TCP/UDP/ICMP. IOCP pour Node.js, de manière prédominante. Ainsi, libuv est fonctionnellement un sous-ensemble de Boost.Asio (caractéristiques communes uniquement TCP/UDP Sockets threads, timers). Dans ces conditions, nous pouvons comparer ces bibliothèques en utilisant seulement quelques critères :

  1. Intégration avec Node.js - Libuv est considérablement meilleur parce qu'il est destiné à cela (nous pouvons l'intégrer complètement et l'utiliser dans tous les aspects, par exemple, le cloud, par exemple Windows azure). Mais Asio met également en œuvre presque la même fonctionnalité que dans l'environnement piloté par file d'attente d'événements de Node.js.
  2. Performances d'IOCP - Je ne vois pas de grandes différences, car ces deux bibliothèques font abstraction de l'API du système d'exploitation sous-jacent. Mais elles le font d'une manière différente : Asio utilise fortement les fonctionnalités C++ comme les templates et parfois TMP. Libuv est une bibliothèque C native. Néanmoins, la réalisation d'IOCP par Asio est très efficace. Les sockets UDP dans Asio ne sont pas assez bons, il est préférable d'utiliser libuv pour eux.

    Intégration avec les nouvelles fonctionnalités du C++ : Asio est meilleur (Asio 1.51 utilise largement le modèle asynchrone C++11, la sémantique des déplacements, les templates variadiques). En ce qui concerne la maturité, Asio est un projet plus stable et plus mature avec une bonne documentation (si on la compare à la description des en-têtes de libuv), beaucoup d'informations sur Internet (discussions vidéo, 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 néanmoins : http://en.highscore.de/cpp/boost/index.html ). Libuv n'a qu'un seul livre en ligne (mais également bon) http://nikhilm.github.com/uvbook/index.html et plusieurs conférences vidéo, il sera donc difficile de connaître tous les secrets (cette bibliothèque en a beaucoup). Pour une discussion plus spécifique sur les fonctions, voir mes commentaires ci-dessous.

Pour conclure, je dirais que tout dépend de vos objectifs, de votre projet et de ce que vous comptez faire concrètement.

11 votes

Ce qui compte, c'est votre compétence technique et votre expérience. Salutations cordiales d'un Cubain.

2 votes

Je suis d'accord avec tous vos points sauf la documentation d'Asio. La documentation officielle ne rend pas justice à cette merveilleuse bibliothèque. Il y a un tas d'autres documents et une conférence de l'auteur que j'ai trouvée très utile. Et je ne suis pas tombé sur un livre pour Asio. Pouvez-vous le lier dans votre réponse ? Ce serait très utile.

0 votes

Pour ce qui est des livres, j'ai édité ma réponse mais je pense que vous l'avez déjà vue (malheureusement, il n'y a pas de livre entièrement consacré à Boost - seulement des informations éparses).

24voto

Vinnie Falco Points 829

L'une des grandes différences est que l'auteur d'Asio (Christopher Kohlhoff) prépare sa bibliothèque pour qu'elle soit incluse dans la bibliothèque standard C++. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2175.pdf y http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4370.html

2voto

kakyo Points 948

Ajout du statut de portabilité : Au moment de poster cette réponse et selon mes propres essais :

  • Boost.ASIO n'a pas de support officiel pour iOS et Android, par exemple, son système de construction ne fonctionne pas pour iOS.
  • libuv se construit facilement pour iOS et Android, avec une prise en charge officielle d'Android dès le début. leurs docs . Ma propre construction générique iOS script pour les projets basés sur Autotools fonctionne sans problème.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X