Comment libuv se compare-t-il à Boost / ASIO?

239

Je serais intéressé par des aspects comme:

  • portée / caractéristiques
  • performance
  • maturité
oberstet
la source
20
Revenons à cette question et obtenons de bonnes réponses!
Viet
\ o / .. j'espère que nous aurons des réponses perspicaces!
oberstet

Réponses:

493

Portée

Boost.Asio est une bibliothèque C ++ qui a commencé par se concentrer sur la mise en réseau, mais ses capacité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 la duplication avec d'autres bibliothèques Boost. Par exemple, Boost.Asio ne fournira pas d'abstraction de thread, comme 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 pour 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 les abstractions et les fonctionnalités, telles que les threads, les pools de threads et la communication entre les threads.

Au fond, chaque bibliothèque fournit une boucle d'événement et des capacités d'E / S asynchrones. Ils se chevauchent pour certaines des fonctionnalités de base, telles que les minuteries, les sockets et les opérations asynchrones. libuv a une portée plus large et fournit des fonctionnalités supplémentaires, telles que les abstractions de thread et de synchronisation, les opérations de système de fichiers synchrones et asynchrones, la gestion des processus, etc. des capacités telles que ICMP, SSL, des opérations de blocage et de non-blocage synchrones et des opérations de niveau supérieur 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 certaines des principales fonctionnalités. Étant donné que les développeurs utilisant Boost.Asio ont souvent d'autres bibliothèques Boost disponibles, j'ai choisi d'envisager des bibliothèques Boost supplémentaires si elles sont directement fournies ou triviales à implémenter.

                         libuv Boost
Boucle d'événement: oui Asio
Threadpool: oui Asio + Threads
Filetage:              
  Fils: oui Fils
  Synchronisation: oui Threads
Opérations du système de fichiers:
  Synchrone: oui FileSystem
  Asynchrone: oui Asio + Filesystem
Minuteurs: oui Asio
E / S Scatter / Gather [1] : pas d'Asio
La mise en réseau:
  ICMP: pas d'Asio
  Résolution DNS: Asio uniquement asynchrone
  SSL: pas d'Asio
  TCP: Asio uniquement asynchrone
  UDP: Asio uniquement asynchrone
Signal:
  Manipulation: oui Asio
  Envoi: oui non
IPC:
  Sockets de domaine UNIX: oui Asio
  Canal nommé Windows: oui Asio
La gestion des processus:
  Détachement: oui Processus
  Tuyau d'E / S: oui Processus
  Apparition: oui Processus
Requêtes système:
  CPU: oui non
  Interface réseau: oui non
Ports série: non oui
ATS: oui non
Chargement de la bibliothèque partagée: oui Extension [2]

1. / regroupement d' E / S .

2. Boost.Extension n'a jamais été soumis pour examen à Boost. Comme indiqué ici , l'auteur considère qu'il est complet.

Boucle d'événement

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

  • Bien que libuv prenne en charge plusieurs boucles d'événements, il ne prend pas en charge 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; tous io_servicesont leurs propres boucles qui permettent à plusieurs threads de s'exécuter. Pour prendre en charge ce Boost.Asio effectue un verrouillage interne au prix de certaines performances . Révision de Boost.Asio l'histoire indique qu'il ya eu plusieurs améliorations de performances pour minimiser le verrouillage.

Threadpool

  • libuv's 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é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.
  • Bien que Boost.Asio ne fournisse pas de pool de threads, le io_servicepeut facilement fonctionner comme un en raison de l' io_serviceautorisation de plusieurs threads à invoquer run. Cela place la responsabilité de la gestion et du comportement des threads pour l'utilisateur, comme on peut le voir dans cet exemple.

Filetage et synchronisation

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

Opérations du système de fichiers

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

La mise en réseau

  • libuv prend en charge les opérations asynchrones sur les sockets UDP et TCP, ainsi que la résolution DNS. Les développeurs d'applications doivent savoir que les descripteurs de fichiers sous-jacents sont définis sur non bloquants. Par conséquent, les opérations synchrones natives doivent vérifier les valeurs de retour et errno pour EAGAINou EWOULDBLOCK.
  • Boost.Asio est un peu plus riche en support réseau. De plus, la plupart des fonctionnalités de mise en réseau de libuv sont disponibles, Boost.Asio prenant en charge les sockets SSL et ICMP. De plus, Boost.Asio propose des opérations de blocage synchrone et non bloquantes synchrones, en plus de ses opérations asynchrones. Il existe de nombreuses fonctions autonomes qui fournissent des opérations courantes de niveau supérieur, telles que la lecture d'une quantité définie d'octets, ou jusqu'à ce qu'un caractère de délimiteur spécifié soit lu.

Signal

  • libuv fournit une abstraction killet une gestion du signal avec son uv_signal_ttype et ses uv_signal_*opérations.
  • Boost.Asio ne fournit pas une abstraction kill, mais signal_setfournit une gestion du signal.

IPC


Différences API

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

Association d'exploitation et de manutention

Dans Boost.Asio, il existe un mappage un à un entre une opération et un gestionnaire. Par exemple, chaque async_writeopération invoquera le WriteHandler une fois. Cela est vrai pour de nombreuses opérations et gestionnaires libuv. Cependant, libuv's uv_async_sendprend en charge un mappage plusieurs-à-un. Plusieurs uv_async_sendappels peuvent entraîner l' appel de uv_async_cb une fois.

Chaînes d'appel vs boucles d'observation

Lorsque vous traitez une tâche, comme la lecture d'un flux / UDP, la gestion des signaux ou l'attente de temporisateurs, 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 démarrée pour l'observateur, où un rappel est fourni. Dès réception de l'événement d'intérêt, le rappel sera invoqué. En revanche, Boost.Asio requiert qu'une opération soit émise chaque fois que l'application souhaite gérer l'événement.

Pour illustrer cette différence, voici une boucle de lecture asynchrone avec Boost.Asio, où l' async_receiveappel 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_readest invoqué chaque fois que l'observateur observe que le socket contient 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'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 qui nécessite de la mémoire pour être géré. L'allocation se fait via un rappel utilisateur, invoqué en interne dans libuv, et reporte la responsabilité de désallocation de l'application. D'un autre côté, de nombreuses opérations Boost.Asio nécessitent que la mémoire soit allouée avant d'émettre l'opération asynchrone, comme dans le cas du bufferfor 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 obsolète.

Cette différence d'allocation de mémoire se présente également dans la bind->listen->acceptboucle. Avec libuv, uv_listencré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. En revanche, Boost.Asio listenne change que l'état du acceptor. L' async_acceptécoute de l'événement de connexion et requiert que l'homologue soit alloué avant d'être appelé.


Performance

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 en temps réel et en temps quasi réel. Si des chiffres précis sont souhaités, le test de référence de libuv peut servir de point de départ.

De plus, alors que le profilage doit être effectué pour identifier les goulots d'étranglement réels, soyez conscient des allocations de mémoire. Pour libuv, la stratégie d'allocation de mémoire est principalement limitée au rappel d'allocateur. D'un autre côté, l'API de Boost.Asio ne permet pas de rappel d'allocateur et pousse plutôt la stratégie d'allocation à l'application. Cependant, les gestionnaires / rappels dans Boost.Asio peuvent être copiés, alloués et désalloués. Boost.Asio permet aux applications de fournir des fonctions d' allocation de mémoire personnalisées 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 un examen par les pairs de 20 jours. Il a également servi d'implémentation de référence et d'API pour la proposition de bibliothèque de mise en réseau pour TR2 . Boost.Asio a une bonne quantité de documentation , bien que son utilité varie d'un utilisateur à l'autre.

L'API a également une sensation assez cohérente. En outre, les opérations asynchrones sont explicites dans le nom de l'opération. Par exemple, acceptest un blocage synchrone et async_acceptest 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. Une attention a également été accordée à masquer certains détails spécifiques au réseau, tels que la ip::address_v4::any()représentation de l'adresse "toutes les interfaces" de 0.0.0.0.

Enfin, Boost 1.47+ fournit un suivi des gestionnaires , 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 libuv à MAR-2011 . L' uvbook est un excellent endroit pour une introduction libuv. La documentation de l'API est ici .

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

Enfin, un rapide coup d'œil à l'historique des validations de libuv montre que les développeurs sont très actifs.

Tanner Sansbury
la source
2
Merci mec! Très bonne réponse! Je ne peux pas penser à quelque chose de plus complet :)
Viet
1
Très satisfait de la réponse, je vous accorde la prime :) Laissez le SO décider de la meilleure réponse pour lui-même.
Viet
28
Réponse incroyable. Cela couvre à la fois l'image de haut niveau, ainsi que des différences de détail spécifiques importantes (comme le filetage / boucle d'événements). Merci beaucoup!
oberstet
1
@oberstet: Non. J'ai mis à jour la réponse pour mentionner que la plupart des opérations de libuv sont individuelles. Cependant, libuv peut accumuler plusieurs uv_async_sendappels et les gérer tous avec un seul rappel. Il est documenté ici . Merci aussi à tous.
Tanner Sansbury
2
Le verrouillage interne de la boucle d'événements sur Boost.Asio semble effrayant du point de vue des performances. Comment peut-il avoir des performances similaires à libuv sans verrouillage? Peut-être que l'ajout d'un avertissement dans la section des performances peut être utile.
zeodtr
46

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

D'abord, d'un point de vue conceptuel, ces bibliothèques sont très différentes dans leur conception. Ils ont des architectures différentes, car ils sont d'échelle différente. Boost.Asio est une grande bibliothèque de mise en réseau destinée à être utilisée avec les protocoles TCP / UDP / ICMP, POSIX, SSL, etc. Libuv est juste une couche pour l'abstraction multiplateforme d' IOCP pour Node.js, principalement. Libuv est donc fonctionnellement un sous-ensemble de Boost.Asio (fonctionnalités communes uniquement aux threads Sockets TCP / UDP, minuteries). Cela étant, nous pouvons comparer ces bibliothèques en utilisant seulement quelques critères:

  1. Intégration avec Node.js - Libuv est considérablement meilleur car il est destiné à cela (nous pouvons l'intégrer complètement et l'utiliser dans tous les aspects, par exemple, le cloud, par exemple, les fenêtres azur). Mais Asio implémente également presque les mêmes fonctionnalités que dans l'environnement piloté par la file d'attente d'événements Node.js.
  2. Performances IOCP - Je ne pouvais pas voir de grandes différences, car ces deux bibliothèques résument l'API OS sous-jacente. Mais ils le font d'une manière différente: Asio utilise fortement les fonctionnalités C ++ telles que les modèles et parfois TMP. Libuv est une bibliothèque C native. Mais néanmoins la réalisation Asio d'IOCP 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 de nouvelles fonctionnalités C ++: Asio est meilleur (Asio 1.51 utilise largement le modèle asynchrone C ++ 11, déplace la sémantique, les 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 (conférences 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 aussi bien) http://nikhilm.github.com/uvbook/index.htmlet plusieurs discussions vidéo, il sera donc difficile de connaître tous les secrets (cette bibliothèque en a beaucoup). Pour une discussion plus spécifique des 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 comptez concrètement faire.

Oleksandr Karaberov
la source
11
Ce qui compte, c'est votre compétence technique et votre expérience. Salutations aimables d'un Cubain.
dsign
2
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 un discours de boost de l'auteur que j'ai trouvé très utile. Et je n'ai pas trouvé de livre pour Asio. Pouvez-vous lier cela dans votre réponse? Ce sera très utile.
Vikas
@vikas Oui je suis d'accord que la documentation est pauvre et parfois contradictoire mais comparée à libuv c'est agréable pour commencer.Comme pour les livres je modifie ma réponse mais je pense que vous l'avez déjà vue (malheureusement il n'y a pas de livre entièrement dédié à Boost - seulement dispersé informations)
Oleksandr Karaberov
Que voulez-vous dire par "Donc libuv est fonctionnellement un sous-ensemble de Boost.Asio (TCP / UDP / Sockets et threads)"? Selon la table des matières nikhilm.github.com/uvbook/index.html, libuv a une application plus large que boost :: asio.
Sergei Nikulov
7
@AlexanderKaraberov pourriez-vous développer les problèmes que l'ASIO a avec UDP?
Bruno Martinez
2

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

  • Boost.ASIO n'a pas de support officiel pour iOS et Android, par exemple, son système de construction ne fonctionne pas pour iOS dès la sortie de la boîte.
  • libuv se construit facilement pour iOS et Android, avec un support officiel pour Android directement dans leurs documents . Mon propre script de génération iOS générique pour les projets basés sur Autotools fonctionne sans problème.
kakyo
la source
Il est assez facile de créer un framework multiplateforme pour iOS et andriod en utilisant bazel en utilisant les règles de construction de boost de bazel.
nnrales Il y a