Respostas:
O Boost.Asio é uma biblioteca C ++ que começou com foco em redes, mas seus recursos de E / S assíncronos foram estendidos a outros recursos. Além disso, com o Boost.Asio fazendo parte das bibliotecas do Boost, seu escopo é ligeiramente reduzido para evitar duplicação com outras bibliotecas do Boost. Por exemplo, o Boost.Asio não fornecerá uma abstração de thread, pois o Boost.Thread já fornece uma.
Por outro lado, libuv é uma biblioteca C concebida para ser a camada plataforma para Node.js . Ele fornece uma abstração para IOCP no Windows, kqueue no macOS e epoll no Linux. Além disso, parece que seu escopo aumentou um pouco para incluir abstrações e funcionalidades, como threads, poços de threads e comunicação entre threads.
Em sua essência, cada biblioteca fornece um loop de eventos e recursos de E / S assíncrona. Eles se sobrepõem a alguns dos recursos básicos, como cronômetros, soquetes e operações assíncronas. O libuv possui um escopo mais amplo e fornece funcionalidades adicionais, como abstrações de encadeamentos e sincronização, operações de sistemas de arquivos síncronos e assíncronos, gerenciamento de processos etc. recursos, como ICMP, SSL, operações de bloqueio síncrono e sem bloqueio e operações de nível superior para tarefas comuns, incluindo leitura de um fluxo até que uma nova linha seja recebida.
Aqui está uma breve comparação lado a lado em alguns dos principais recursos. Como os desenvolvedores que usam o Boost.Asio geralmente têm outras bibliotecas do Boost disponíveis, optei por considerar outras bibliotecas do Boost, caso sejam fornecidas diretamente ou sejam triviais para implementar.
libuv Boost Loop de eventos: sim Asio Conjunto de threads: sim Asio + Threads Rosqueamento: Tópicos: sim Tópicos Sincronização: sim Threads Operações do sistema de arquivos: Síncrono: sim Sistema de arquivos Assíncrono: sim Sistema de arquivos Asio + Temporizadores: sim Asio Scatter / Reúna E / S [1] : não Asio Rede: ICMP: não Asio Resolução DNS: Asio somente assíncrono SSL: não Asio TCP: Asio somente assíncrono UDP: Asio somente assíncrono Sinal: Manipulação: sim Asio Enviando: sim não IPC: Soquetes de domínio UNIX: yes Asio Pipe nomeado pelo Windows: sim Asio Gerenciamento de processos: Destacando: sim Processo Tubo de E / S: sim Processo Desova: sim Processo Consultas do sistema: CPU: sim não Interface de Rede: sim não Portas Seriais: não sim TTY: sim não Carregamento de biblioteca compartilhada: sim Extensão [2]
1. Dispersão / Recolha de I / O .
2. O Boost.Extension nunca foi enviado para revisão no Boost. Como observado aqui , o autor considera completo.
Enquanto o libuv e o Boost.Asio fornecem loops de eventos, existem algumas diferenças sutis entre os dois:
uv_default_loop()
), em vez de criar um novo loop ( uv_loop_new()
), pois outro componente pode estar executando o loop padrão.io_service
são seus próprios loops que permitem a execução de vários threads. Para dar suporte a esse Boost.Asio executa o bloqueio interno ao custo de algum desempenho . O histórico de revisões do Boost.Asio indica que houve várias melhorias de desempenho para minimizar o bloqueio.uv_queue_work
. O tamanho do conjunto de encadeamentos é configurável por meio da variável de ambiente UV_THREADPOOL_SIZE
. O trabalho será executado fora do loop de eventos e dentro do pool de threads. Depois que o trabalho estiver concluído, o manipulador de conclusão será colocado na fila para executar no loop de eventos.io_service
pode funcionar facilmente como um, como resultado da io_service
permissão de vários encadeamentos run
. Isso coloca a responsabilidade do gerenciamento e comportamento do encadeamento para o usuário, como pode ser visto neste exemplo.EAGAIN
or EWOULDBLOCK
.kill
e manipulação de sinal com seu uv_signal_t
tipo e uv_signal_*
operações.kill
, mas signal_set
fornece tratamento de sinal.uv_pipe_t
tipo.local::stream_protocol::socket
ou local::datagram_protocol::socket
, e windows::stream_handle
.Embora as APIs sejam diferentes apenas com base no idioma, aqui estão algumas diferenças importantes:
No Boost.Asio, há um mapeamento individual entre uma operação e um manipulador. Por exemplo, cada async_write
operação chamará o WriteHandler uma vez. Isso é verdade para muitas operações e manipuladores libuv. No entanto, o libuv's uv_async_send
suporta um mapeamento muitos-para-um. Várias uv_async_send
chamadas podem resultar no uv_async_cb sendo chamado uma vez.
Ao lidar com tarefas, como ler de um fluxo / UDP, manipular sinais ou aguardar temporizadores, as cadeias de chamadas assíncronas do Boost.Asio são um pouco mais explícitas. Com o libuv, um observador é criado para designar interesses em um evento específico. Um loop é iniciado para o observador, onde é fornecido um retorno de chamada. Ao receber o evento de interesse, o retorno de chamada será invocado. Por outro lado, o Boost.Asio exige que uma operação seja emitida sempre que o aplicativo estiver interessado em lidar com o evento.
Para ajudar a ilustrar essa diferença, aqui está um loop de leitura assíncrono com o Boost.Asio, em que a async_receive
chamada será emitida várias vezes:
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 ); --'
}
E aqui está o mesmo exemplo com o libuv, onde handle_read
é invocado cada vez que o observador observa que o soquete possui dados:
uv_read_start( socket, alloc_buffer, handle_read ); --.
|
.-------------------------------------------------'
|
V
void handle_read( ... )
{
fprintf( stdout, "got data\n" );
}
Como resultado das cadeias de chamadas assíncronas no Boost.Asio e dos observadores no libuv, a alocação de memória geralmente ocorre em momentos diferentes. Com os observadores, o libuv adia a alocação até depois de receber um evento que requer memória para lidar. A alocação é feita por meio de um retorno de chamada do usuário, invocado interno ao libuv e adia a responsabilidade de desalocação do aplicativo. Por outro lado, muitas das operações do Boost.Asio exigem que a memória seja alocada antes de emitir a operação assíncrona, como é o caso do buffer
for async_read
. O Boost.Asio fornece null_buffers
, que pode ser usado para escutar um evento, permitindo que os aplicativos adiem a alocação de memória até que a memória seja necessária, embora isso seja preterido.
Essa diferença de alocação de memória também se apresenta dentro do bind->listen->accept
loop. Com o libuv, uv_listen
cria um loop de eventos que chamará o retorno de chamada do usuário quando uma conexão estiver pronta para ser aceita. Isso permite que o aplicativo adie a alocação do cliente até que uma conexão esteja sendo tentada. Por outro lado, o Boost.Asio's listen
apenas altera o estado do acceptor
. Ele async_accept
escuta o evento de conexão e exige que o par seja alocado antes de ser chamado.
Infelizmente, não tenho números de referência concretos para comparar o libuv e o Boost.Asio. No entanto, observei um desempenho semelhante usando as bibliotecas em aplicativos em tempo real e quase em tempo real. Se números desejados são desejados, o teste de referência da libuv pode servir como ponto de partida.
Além disso, enquanto a criação de perfil deve ser feita para identificar gargalos reais, esteja ciente das alocações de memória. Para o libuv, a estratégia de alocação de memória é principalmente limitada ao retorno de chamada do alocador. Por outro lado, a API do Boost.Asio não permite um retorno de chamada do alocador e envia a estratégia de alocação ao aplicativo. No entanto, os manipuladores / retornos de chamada no Boost.Asio podem ser copiados, alocados e desalocados. O Boost.Asio permite que os aplicativos forneçam funções personalizadas de alocação de memória para implementar uma estratégia de alocação de memória para manipuladores.
O desenvolvimento da Asio remonta a pelo menos OCT-2004 e foi aceito no Boost 1.35 em 22 de março de 2006 após uma revisão por pares de 20 dias. Também serviu como implementação de referência e API para a proposta de biblioteca de rede para TR2 . O Boost.Asio possui uma boa quantidade de documentação , embora sua utilidade varie de usuário para usuário.
A API também tem uma sensação bastante consistente. Além disso, as operações assíncronas são explícitas no nome da operação. Por exemplo, accept
é um bloqueio síncrono e async_accept
é assíncrono. A API fornece funções gratuitas para tarefas comuns de E / S, por exemplo, lendo de um fluxo até que um \r\n
seja lido. Também foi dada atenção para ocultar alguns detalhes específicos da rede, como a ip::address_v4::any()
representação do endereço "todas as interfaces" de 0.0.0.0
.
Por fim, o Boost 1.47+ fornece rastreamento de manipulador , que pode ser útil na depuração, além de suporte ao C ++ 11.
Com base em seus gráficos no github, o desenvolvimento do Node.js. remonta a pelo menos fevereiro de 2009 e o desenvolvimento do libuv data de MAR-2011 . O uvbook é um ótimo lugar para uma introdução ao libuv. A documentação da API está aqui .
No geral, a API é bastante consistente e fácil de usar. Uma anomalia que pode ser uma fonte de confusão é uv_tcp_listen
a criação de um loop de observador. Isso é diferente de outros observadores que geralmente têm um uv_*_start
e um uv_*_stop
par de funções para controlar a vida do loop do observador. Além disso, algumas das uv_fs_*
operações têm uma quantidade razoável de argumentos (até 7). Com o comportamento síncrono e assíncrono sendo determinado na presença de um retorno de chamada (o último argumento), a visibilidade do comportamento síncrono pode ser diminuída.
Finalmente, uma rápida olhada no histórico do libuv commit mostra que os desenvolvedores são muito ativos.
uv_async_send
chamadas e lidar com todas elas com um único retorno de chamada. Está documentado aqui . Além disso, obrigado a todos.
Está bem. Tenho alguma experiência no uso das duas bibliotecas e posso esclarecer algumas coisas.
Primeiro, do ponto de vista conceitual, essas bibliotecas são bastante diferentes no design. Eles têm arquiteturas diferentes, porque são de escala diferente. O Boost.Asio é uma grande biblioteca de rede destinada a ser usada com protocolos TCP / UDP / ICMP, POSIX, SSL e assim por diante. O Libuv é apenas uma camada para abstração de plataforma cruzada do IOCP para Node.js, predominantemente. Portanto, o libuv é funcionalmente um subconjunto do Boost.Asio (recursos comuns apenas threads de sockets TCP / UDP, timers). Sendo esse o caso, podemos comparar essas bibliotecas usando apenas alguns critérios:
Integração com os novos recursos do C ++: o Asio é melhor (o Asio 1.51 usa extensivamente o modelo assíncrono do C ++ 11, semântica de movimento, modelos variados). headers description), muitas informações na Internet (videoconferências, blogs: http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg = 1 , etc.) E até livros (não para profissionais, mas mesmo assim: http://en.highscore.de/cpp/boost/index.html ). O Libuv possui apenas um livro on-line (mas também é bom) http://nikhilm.github.com/uvbook/index.htmle várias conversas em vídeo, por isso será difícil conhecer todos os segredos (esta biblioteca possui muitos deles). Para uma discussão mais específica das funções, veja meus comentários abaixo.
Como conclusão, devo dizer que tudo depende dos seus propósitos, do seu projeto e do que concretamente você pretende fazer.
Uma grande diferença é que o autor de Asio (Christopher Kohlhoff) está preparando sua biblioteca para inclusão na C ++ Standard Library, consulte 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
Adicionando o status de portabilidade: Desde a publicação desta resposta e de acordo com minhas próprias tentativas: