Risposte:
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.
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]
2. Boost . L' estensione non è mai stata sottoposta a revisione per Boost. Come notato qui , l'autore lo considera completo.
Mentre sia libuv che Boost.Asio forniscono loop di eventi, ci sono alcune sottili differenze tra i due:
uv_default_loop()
), anziché creare un nuovo ciclo ( uv_loop_new()
), poiché un altro componente potrebbe eseguire il ciclo predefinito.io_service
sono 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.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.io_service
può facilmente funzionare come uno come risultato del io_service
consentire 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.EAGAIN
o EWOULDBLOCK
.kill
e la gestione del segnale con il suo uv_signal_t
tipo e uv_signal_*
operazioni.kill
, ma signal_set
fornisce la gestione del segnale.uv_pipe_t
tipo.local::stream_protocol::socket
o local::datagram_protocol::socket
, e windows::stream_handle
.Mentre le API sono diverse in base alla sola lingua, qui ci sono alcune differenze chiave:
All'interno di Boost.Asio, esiste un mapping uno a uno tra un'operazione e un gestore. Ad esempio, ogni async_write
operazione invocherà WriteHandler una volta. Questo è vero per molte operazioni e gestori di libuv. Tuttavia, libuv's uv_async_send
supporta un mapping molti-a-uno. uv_async_send
Chiamate multiple possono comportare la chiamata una volta uv_async_cb .
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_receive
chiamata 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_read
viene 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" );
}
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 buffer
for 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->accept
loop. Con libuv, uv_listen
crea 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 listen
cambia solo lo stato di acceptor
. I async_accept
intercetta l'evento di connessione, e richiede il peer da destinare prima di essere richiamato.
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.
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\n
lettura 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.
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_listen
crea un ciclo di monitoraggio. Ciò è diverso dagli altri watcher che generalmente hanno una uv_*_start
e una uv_*_stop
coppia 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.
uv_async_send
chiamate e gestirle tutte con un singolo callback. È documentato qui . Inoltre, grazie a tutti.
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:
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.
Una grande differenza è che l'autore di Asio (Christopher Kohlhoff) sta preparando la sua biblioteca per l'inclusione nella C ++ Standard Library, vedi http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2175 .pdf e http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4370.html
Aggiunta dello stato di portabilità: al momento di pubblicare questa risposta e secondo i miei tentativi: