Come si confronta libuv con Boost / ASIO?


239

Sarei interessato ad aspetti come:

  • portata / caratteristiche
  • prestazione
  • scadenza

20
Torniamo a questa domanda e otteniamo buone risposte!
Viet

\ o / .. spero che avremo delle risposte perspicaci!
oberstet,

Risposte:


493

Scopo

Boost.Asio è una libreria C ++ iniziata con un focus sulla rete, ma le sue capacità di I / O asincrone sono state estese ad altre risorse. Inoltre, con Boost.Asio che fa parte delle librerie Boost, il suo ambito è leggermente ristretto per impedire la duplicazione con altre librerie Boost. Ad esempio, Boost.Asio non fornirà un'astrazione di thread, poiché Boost.Thread ne fornisce già uno.

D'altra parte, libuv è una libreria C progettato per essere lo strato di piattaforma per Node.js . Fornisce un'astrazione per IOCP su Windows, kqueue su macOS ed epoll su Linux. Inoltre, sembra che il suo ambito sia leggermente aumentato per includere astrazioni e funzionalità, come thread, threadpools e comunicazioni inter-thread.

Al loro centro, ogni libreria fornisce un loop di eventi e funzionalità di I / O asincrone. Si sovrappongono per alcune delle funzionalità di base, quali timer, socket e operazioni asincrone. libuv ha un ambito più ampio e offre funzionalità aggiuntive, come astrazioni di thread e sincronizzazione, operazioni di file system sincrone e asincrone, gestione dei processi, ecc. Al contrario, Boost.Automazioni di messa a fuoco di rete originali di Asio, in quanto forniscono un set più ricco di reti correlate funzionalità come ICMP, SSL, operazioni di blocco e non blocco sincroni e operazioni di livello superiore per attività comuni, inclusa la lettura da un flusso fino alla ricezione di una nuova riga.


Elenco delle caratteristiche

Ecco il breve confronto affiancato su alcune delle principali funzionalità. Poiché gli sviluppatori che usano Boost.Asio hanno spesso a disposizione altre librerie Boost, ho deciso di prendere in considerazione ulteriori librerie Boost se fornite direttamente o banali da implementare.

                         libuv Boost
Event Loop: si Asio
Threadpool: si Asio + Discussioni
threading:              
  Discussioni: si Discussioni
  Sincronizzazione: sì Discussioni
Operazioni sul file system:
  Sincrono: sì FileSystem
  Asincrono: si Asio + Filesystem
Timer: si Asio
Scatter / Gather I / O [1] : no Asio
networking:
  ICMP: no Asio
  Risoluzione DNS: Asio solo asincrono
  SSL: no Asio
  TCP: Asio solo asincrono
  UDP: Asio solo asincrono
Segnale:
  Manipolazione: si Asio
  Invio: sì no
IPC:
  Socket di dominio UNIX: sì Asio
  Pipe con nome Windows: si Asio
Gestione dei processi:
  Staccare: sì Processo
  Tubazione I / O: sì Processo
  Generazione: si Processo
Domande di sistema:
  CPU: sì no
  Interfaccia di rete: sì no
Porte seriali: no sì
TTY: sì no
Caricamento libreria condivisa: sì Estensione [2]

1. Scatter / Gather I / O .

2. Boost . L' estensione non è mai stata sottoposta a revisione per Boost. Come notato qui , l'autore lo considera completo.

Loop degli eventi

Mentre sia libuv che Boost.Asio forniscono loop di eventi, ci sono alcune sottili differenze tra i due:

  • Mentre libuv supporta più loop di eventi, non supporta l'esecuzione dello stesso loop da più thread. Per questo motivo, è necessario prestare attenzione quando si utilizza il ciclo predefinito ( uv_default_loop()), anziché creare un nuovo ciclo ( uv_loop_new()), poiché un altro componente potrebbe eseguire il ciclo predefinito.
  • Boost.Asio non ha l'idea di un ciclo predefinito; tutti io_servicesono i propri loop che consentono l'esecuzione di più thread. Per supportare questo Boost.Asio esegue il blocco interno a scapito di alcune prestazioni . Revisione del Boost.Asio storia indica che ci sono stati diversi miglioramenti delle prestazioni per ridurre al minimo il bloccaggio.

ThreadPool

  • libuv's fornisce un thread-through uv_queue_work. La dimensione del thread pool è configurabile tramite la variabile di ambiente UV_THREADPOOL_SIZE. Il lavoro verrà eseguito al di fuori del loop degli eventi e all'interno del pool di thread. Una volta completato il lavoro, il gestore di completamento verrà messo in coda per essere eseguito all'interno del ciclo degli eventi.
  • Mentre Boost.Asio non fornisce un pool di thread, io_servicepuò facilmente funzionare come uno come risultato del io_serviceconsentire a più thread di invocare run. Ciò attribuisce all'utente la responsabilità della gestione e del comportamento dei thread, come si può vedere in questo esempio.

Threading e sincronizzazione

  • libuv fornisce un'astrazione ai thread e ai tipi di sincronizzazione.
  • Boost.Thread fornisce un thread e tipi di sincronizzazione. Molti di questi tipi seguono da vicino lo standard C ++ 11, ma forniscono anche alcune estensioni. Come risultato di Boost.Asio che consente a più thread di eseguire un singolo ciclo di eventi, fornisce i trefoli come mezzo per creare un richiamo sequenziale dei gestori di eventi senza utilizzare meccanismi di blocco espliciti.

Operazioni sul file system

  • libuv fornisce un'astrazione a molte operazioni del file system. Esiste una funzione per operazione e ogni operazione può essere un blocco sincrono o asincrono. Se viene fornito un callback, l'operazione verrà eseguita in modo asincrono all'interno di un pool di thread interno. Se non viene fornito un callback, la chiamata sarà bloccata in modo sincrono.
  • Boost.Filesystem fornisce chiamate di blocco sincrono per molte operazioni del file system. Questi possono essere combinati con Boost.Asio e un threadpool per creare operazioni di file system asincrone.

Networking

  • libuv supporta operazioni asincrone su socket UDP e TCP, oltre alla risoluzione DNS. Gli sviluppatori di applicazioni devono essere consapevoli del fatto che i descrittori di file sottostanti sono impostati su non bloccanti. Pertanto, le operazioni sincrone native dovrebbero controllare i valori di ritorno e errno per EAGAINo EWOULDBLOCK.
  • Boost.Asio è un po 'più ricco nel suo supporto di rete. Inoltre molte delle funzionalità di networking di libuv, Boost.Asio supportano socket SSL e ICMP. Inoltre, Boost.Asio fornisce operazioni di blocco sincrono e non-blocco sincrono, oltre alle sue operazioni asincrone. Esistono numerose funzioni indipendenti che forniscono operazioni comuni di livello superiore, come la lettura di una determinata quantità di byte o fino alla lettura di un carattere delimitatore specificato.

Segnale

  • libuv fornisce un'astrazione kille la gestione del segnale con il suo uv_signal_ttipo e uv_signal_*operazioni.
  • Boost.Asio non provoca un'astrazione kill, ma signal_setfornisce la gestione del segnale.

IPC


Differenze API

Mentre le API sono diverse in base alla sola lingua, qui ci sono alcune differenze chiave:

Associazione Operatori e Gestori

All'interno di Boost.Asio, esiste un mapping uno a uno tra un'operazione e un gestore. Ad esempio, ogni async_writeoperazione invocherà WriteHandler una volta. Questo è vero per molte operazioni e gestori di libuv. Tuttavia, libuv's uv_async_sendsupporta un mapping molti-a-uno. uv_async_sendChiamate multiple possono comportare la chiamata una volta uv_async_cb .

Chiama Chains vs. Watcher Loop

Quando si tratta di attività, come la lettura da un flusso / UDP, la gestione dei segnali o l'attesa di timer, Boost Le catene di chiamate asincrone di Asio sono un po 'più esplicite. Con libuv, viene creato un osservatore per designare gli interessi in un particolare evento. Viene quindi avviato un loop per il watcher, in cui viene fornito un callback. Alla ricezione dell'evento di interesse, verrà richiamato il callback. D'altra parte, Boost.Asio richiede un'operazione da eseguire ogni volta che l'applicazione è interessata a gestire l'evento.

Per illustrare questa differenza, ecco un ciclo di lettura asincrono con Boost.Asio, in cui la async_receivechiamata verrà emessa più volte:

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 );   --'
}    

Ed ecco lo stesso esempio con libuv, dove handle_readviene invocato ogni volta che l'osservatore osserva che il socket ha dei dati:

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

Allocazione della memoria

Come risultato delle catene di chiamate asincrone in Boost.Asio e gli osservatori in libuv, l'allocazione della memoria si verifica spesso in momenti diversi. Con gli osservatori, libuv nega l'allocazione fino a quando non riceve un evento che richiede memoria per essere gestito. L'allocazione viene effettuata tramite un callback dell'utente, invocato internamente a libuv e difende la responsabilità di deallocazione dell'applicazione. D'altra parte, molte delle operazioni Boost.Asio richiedono che la memoria sia allocata prima di eseguire l'operazione asincrona, come nel caso del bufferfor async_read. Boost.Asio fornisce null_buffers, che può essere utilizzato per ascoltare un evento, consentendo alle applicazioni di differire l'allocazione della memoria fino a quando la memoria è necessaria, sebbene questa sia obsoleta.

Questa differenza di allocazione della memoria si presenta anche all'interno del bind->listen->acceptloop. Con libuv, uv_listencrea un loop di eventi che richiamerà la richiamata dell'utente quando una connessione è pronta per essere accettata. Ciò consente all'applicazione di differire l'allocazione del client fino a quando non viene tentata una connessione. D'altra parte, Boost.Asio listencambia solo lo stato di acceptor. I async_acceptintercetta l'evento di connessione, e richiede il peer da destinare prima di essere richiamato.


Prestazione

Sfortunatamente, non ho numeri di riferimento concreti per confrontare libuv e Boost.Asio. Tuttavia, ho osservato prestazioni simili utilizzando le librerie in applicazioni in tempo reale e quasi in tempo reale. Se si desiderano numeri concreti, il test di riferimento di libuv può servire da punto di partenza.

Inoltre, mentre la profilazione deve essere eseguita per identificare i colli di bottiglia effettivi, tenere presente le allocazioni di memoria. Per libuv, la strategia di allocazione della memoria è principalmente limitata al callback dell'allocatore. D'altra parte, l'API di Boost.Asio non consente un callback dell'allocatore, ma trasferisce invece la strategia di allocazione all'applicazione. Tuttavia, i gestori / callback in Boost.Asio possono essere copiati, allocati e deallocati. Boost.Asio consente alle applicazioni di fornire funzioni personalizzate di allocazione della memoria al fine di implementare una strategia di allocazione della memoria per i gestori.


Scadenza

Boost.Asio

Lo sviluppo di Asio risale almeno all'OCT-2004, ed è stato accettato in Boost 1.35 il 22-MAR-2006 dopo aver subito una revisione tra pari di 20 giorni. È stato anche l'implementazione di riferimento e l'API per la proposta di libreria di rete per TR2 . Boost.Asio ha una buona quantità di documentazione , anche se la sua utilità varia da utente a utente.

L'API ha anche un aspetto abbastanza coerente. Inoltre, le operazioni asincrone sono esplicite nel nome dell'operazione. Ad esempio, acceptè il blocco sincrono ed async_acceptè asincrono. L'API fornisce funzioni gratuite per attività di I / O comuni, ad esempio la lettura da un flusso fino alla \r\nlettura di un. È stata inoltre prestata attenzione a nascondere alcuni dettagli specifici della rete, come la ip::address_v4::any()rappresentazione dell'indirizzo "tutte le interfacce" di 0.0.0.0.

Infine, Boost 1.47+ fornisce il monitoraggio dei gestori , che può rivelarsi utile durante il debug, oltre al supporto C ++ 11.

libuv

Sulla base dei loro grafici github, lo sviluppo di Node.js risale almeno al FEB-2009 e lo sviluppo di libuv al MAR-2011 . L' uvbook è il luogo ideale per un'introduzione a libuv. La documentazione dell'API è qui .

Nel complesso, l'API è abbastanza coerente e facile da usare. Un'anomalia che può essere fonte di confusione è che uv_tcp_listencrea un ciclo di monitoraggio. Ciò è diverso dagli altri watcher che generalmente hanno una uv_*_starte una uv_*_stopcoppia di funzioni per controllare la durata del loop degli watcher. Inoltre, alcune delle uv_fs_*operazioni hanno una discreta quantità di argomenti (fino a 7). Con il comportamento sincrono e asincrono determinato sulla presenza di un callback (l'ultimo argomento), la visibilità del comportamento sincrono può essere ridotta.

Infine, una rapida occhiata alla cronologia degli impegni di libuv mostra che gli sviluppatori sono molto attivi.


2
Grazie uomo! Bella risposta! Non riesco a pensare a qualcosa di più completo :)
Viet

1
Molto contento della risposta, ti conferisco la generosità :) Lascia che sia il SO a decidere la risposta migliore per se stesso.
Viet,

28
Risposta incredibile. Questo copre sia l'immagine di alto livello, sia specifiche, importanti differenze nei dettagli (come ad esempio threading / eventloop). Grazie mille!
oberstet

1
@oberstet: No. Ho aggiornato la risposta per menzionare che la maggior parte delle operazioni di libuv sono individuali. Tuttavia, libuv può accumulare più uv_async_sendchiamate e gestirle tutte con un singolo callback. È documentato qui . Inoltre, grazie a tutti.
Tanner Sansbury,

2
Il blocco interno sul loop degli eventi su Boost.Asio sembra spaventoso dal punto di vista delle prestazioni. Come può avere prestazioni simili a libuv senza lock? Forse aggiungere un'istruzione di avvertimento nella sezione delle prestazioni può essere utile.
zeodtr,

46

Ok. Ho una certa esperienza nell'uso di entrambe le librerie e posso chiarire alcune cose.

Innanzitutto, da un punto di vista concettuale, queste librerie sono abbastanza diverse nel design. Hanno architetture diverse, perché hanno dimensioni diverse. Boost.Asio è una grande libreria di rete destinata ad essere utilizzata con i protocolli TCP / UDP / ICMP, POSIX, SSL e così via. Libuv è solo uno strato per l'astrazione multipiattaforma di IOCP per Node.js, prevalentemente. Quindi libuv è funzionalmente un sottoinsieme di Boost.Asio (funzionalità comuni solo thread socket, timer TCP / UDP). Stando così le cose, possiamo confrontare queste librerie usando solo alcuni criteri:

  1. Integrazione con Node.js - Libuv è notevolmente migliore perché è mirato a questo (possiamo integrarlo completamente e utilizzarlo in tutti gli aspetti, ad esempio cloud, ad esempio windows azzurro). Ma Asio implementa anche quasi le stesse funzionalità dell'ambiente basato sulla coda di eventi Node.js.
  2. Prestazioni IOCP - Non ho potuto vedere grandi differenze, perché entrambe queste librerie astraggono l'API del SO sottostante. Ma lo fanno in un modo diverso: Asio usa pesantemente funzionalità C ++ come template e talvolta TMP. Libuv è una libreria C nativa. Tuttavia, la realizzazione di IOCP da parte di Asio è molto efficiente. I socket UDP in Asio non sono abbastanza buoni, è meglio usare libuv per loro.

    Integrazione con le nuove funzionalità C ++: Asio è migliore (Asio 1.51 utilizza ampiamente il modello asincrono C ++ 11, sposta la semantica, i modelli variadici). Per quanto riguarda la maturità, Asio è un progetto più stabile e maturo con una buona documentazione (se confrontato con libuv descrizione delle intestazioni), molte informazioni su Internet (videoconferenze, blog: http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg = 1 , ecc.) E persino libri (non per professionisti ma comunque: http://en.highscore.de/cpp/boost/index.html ). Libuv ha un solo libro online (ma anche buono) http://nikhilm.github.com/uvbook/index.htmle diversi video talk, quindi sarà difficile conoscere tutti i segreti (questa libreria ne ha molti). Per una discussione più specifica delle funzioni vedi i miei commenti qui sotto.

In conclusione, dovrei dire che tutto dipende dai tuoi scopi, dal tuo progetto e da cosa concretamente intendi fare.


11
Ciò che conta è la tua abilità tecnica ed esperienza. Cordiali saluti da un cubano.
firma il

2
Sono d'accordo con tutti i tuoi punti tranne la documentazione di Asio. La documentazione ufficiale non rende giustizia a questa meravigliosa biblioteca. Ci sono un sacco di altri documenti e un discorso sull'autore che ho trovato molto utile. E non ho trovato un libro per Asio. Puoi collegarlo nella tua risposta? Sarebbe di grande aiuto.
Vikas,

@vikas Sì, sono d'accordo che la documentazione sia scarsa e talvolta contraddittoria, ma rispetto alla libuv è utile per iniziare. Per quanto riguarda i libri, modifico la mia risposta ma penso che tu l'abbia vista prima (sfortunatamente non esiste un libro interamente dedicato a Boost - solo sparso informazioni)
Oleksandr Karaberov,

Cosa intendi con "Quindi libuv è funzionalmente un sottoinsieme di Boost.Asio (TCP / UDP / Socket e thread)"? Secondo TOC nikhilm.github.com/uvbook/index.html libuv ha un'applicazione più ampia di boost :: asio.
Sergei Nikulov,

7
@AlexanderKaraberov potresti espandere i problemi che ASIO ha con UDP?
Bruno Martinez,


2

Aggiunta dello stato di portabilità: al momento di pubblicare questa risposta e secondo i miei tentativi:

  • Boost.ASIO non ha supporto ufficiale per iOS e Android, ad esempio il suo sistema di build non funziona per iOS.
  • libuv si costruisce facilmente per iOS e Android, con il supporto ufficiale per Android proprio nei loro documenti . Il mio script di build iOS generico per progetti basati su Autotools funziona senza problemi.

È abbastanza facile costruire un framework multipiattaforma per iOS e Android usando bazel usando le regole di build di bazel boost.
nnrales
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.