Como o libuv se compara ao Boost / ASIO?

239

Eu estaria interessado em aspectos como:

  • escopo / recursos
  • desempenho
  • maturidade
oberstet
fonte
20
Vamos voltar a esta pergunta e obter boas respostas!
Viet
\ o / .. espero que possamos obter respostas perspicazes!
oberstet

Respostas:

493

Escopo

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.


Lista de recursos

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.

Loop de Eventos

Enquanto o libuv e o Boost.Asio fornecem loops de eventos, existem algumas diferenças sutis entre os dois:

  • Enquanto o libuv suporta vários loops de eventos, ele não suporta a execução do mesmo loop de vários threads. Por esse motivo, é necessário tomar cuidado ao usar o loop padrão ( uv_default_loop()), em vez de criar um novo loop ( uv_loop_new()), pois outro componente pode estar executando o loop padrão.
  • Boost.Asio não tem a noção de um loop padrão; todos io_servicesã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.

Grupo de discussão

  • O libuv's fornece um pool de threads 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.
  • Embora o Boost.Asio não forneça um pool de encadeamentos, ele io_servicepode funcionar facilmente como um, como resultado da io_servicepermissã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.

Rosqueamento e Sincronização

  • O libuv fornece uma abstração para threads e tipos de sincronização.
  • O Boost.Thread fornece tipos de encadeamento e sincronização. Muitos desses tipos seguem de perto o padrão C ++ 11, mas também fornecem algumas extensões. Como o Boost.Asio permite que vários threads executem um único loop de eventos, ele fornece strands como um meio de criar uma chamada seqüencial de manipuladores de eventos sem usar mecanismos explícitos de bloqueio.

Operações do sistema de arquivos

  • O libuv fornece uma abstração para muitas operações do sistema de arquivos. Há uma função por operação e cada operação pode ser bloqueio síncrono ou assíncrono. Se um retorno de chamada for fornecido, a operação será executada de forma assíncrona dentro de um conjunto de encadeamentos interno. Se um retorno de chamada não for fornecido, a chamada será bloqueada síncrona.
  • O Boost.Filesystem fornece chamadas de bloqueio síncrono para muitas operações do sistema de arquivos. Eles podem ser combinados com o Boost.Asio e um pool de threads para criar operações assíncronas do sistema de arquivos.

Trabalho em rede

  • O libuv suporta operações assíncronas nos soquetes UDP e TCP, bem como a resolução de DNS. Os desenvolvedores de aplicativos devem estar cientes de que os descritores de arquivo subjacentes estão definidos como sem bloqueio. Portanto, operações síncronas nativas devem verificar valores de retorno e errno para EAGAINor EWOULDBLOCK.
  • O Boost.Asio é um pouco mais rico em seu suporte de rede. Além disso, muitos dos recursos de rede da libuv oferecem, o Boost.Asio, compatível com soquetes SSL e ICMP. Além disso, o Boost.Asio fornece operações de bloqueio síncrono e não síncrono, além de suas operações assíncronas. Existem inúmeras funções independentes que fornecem operações comuns de nível superior, como a leitura de uma quantidade definida de bytes ou até a leitura de um caractere delimitador especificado.

Sinal

  • O libuv fornece uma abstração kille manipulação de sinal com seu uv_signal_ttipo e uv_signal_*operações.
  • O Boost.Asio não fornece uma abstração kill, mas signal_setfornece tratamento de sinal.

IPC


Diferenças de API

Embora as APIs sejam diferentes apenas com base no idioma, aqui estão algumas diferenças importantes:

Associação de Operação e Manipulador

No Boost.Asio, há um mapeamento individual entre uma operação e um manipulador. Por exemplo, cada async_writeoperação chamará o WriteHandler uma vez. Isso é verdade para muitas operações e manipuladores libuv. No entanto, o libuv's uv_async_sendsuporta um mapeamento muitos-para-um. Várias uv_async_sendchamadas podem resultar no uv_async_cb sendo chamado uma vez.

Cadeias de chamada x loops de observador

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_receivechamada 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" );
}

Alocação de memória

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 bufferfor 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->acceptloop. Com o libuv, uv_listencria 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 listenapenas altera o estado do acceptor. Ele async_acceptescuta o evento de conexão e exige que o par seja alocado antes de ser chamado.


atuação

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.


Maturidade

Boost.Asio

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\nseja 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.

libuv

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_listena criação de um loop de observador. Isso é diferente de outros observadores que geralmente têm um uv_*_starte um uv_*_stoppar 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.

Tanner Sansbury
fonte
2
Valeu cara! Ótima resposta! Eu não consigo pensar em nada mais abrangente :)
Viet
1
Muito feliz com a resposta, concedo-lhe a recompensa :) Deixe o SO decidir a melhor resposta para si.
Viet
28
Resposta incrível. Isso abrange tanto a imagem de alto nível quanto as diferenças importantes e específicas em detalhes (como por exemplo, threading / eventloop). Muito obrigado!
oberstet
1
@oberstet: Não. Atualizei a resposta para mencionar que a maioria das operações do libuv é individual. No entanto, o libuv pode acumular várias uv_async_sendchamadas e lidar com todas elas com um único retorno de chamada. Está documentado aqui . Além disso, obrigado a todos.
precisa saber é o seguinte
2
O bloqueio interno no loop de eventos no Boost.Asio parece assustador do ponto de vista de desempenho. Como ele pode ter desempenho semelhante ao libuv sem bloqueio? Talvez adicionar uma declaração de aviso na seção desempenho possa ser útil.
Zeodtr 12/11/2014
46

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:

  1. A integração com o Node.js - o Libuv é consideravelmente melhor porque é direcionada para isso (podemos integrá-lo totalmente e usá-lo em todos os aspectos, por exemplo, nuvem, por exemplo, janelas azuis). Mas o Asio também implementa quase a mesma funcionalidade que no ambiente orientado a filas de eventos do Node.js.
  2. Desempenho do IOCP - Não pude ver grandes diferenças, porque essas duas bibliotecas abstraem a API do SO subjacente. Mas eles fazem isso de uma maneira diferente: o Asio usa muito recursos do C ++, como modelos e, às vezes, TMP. Libuv é uma biblioteca C nativa. Mas, no entanto, a realização do IOCP pela Asio é muito eficiente. Soquetes UDP no Asio não são bons o suficiente, é melhor usar o libuv para eles.

    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.

Oleksandr Karaberov
fonte
11
O que importa é a sua habilidade e experiência técnica. Saudações de um cubano.
dsign 30/10/12
2
Eu concordo com todos os seus pontos, exceto a documentação da Asio. A documentação oficial não faz justiça a esta maravilhosa biblioteca. Existem muitos outros documentos e uma conversa com o autor, que achei muito útil. E não encontrei um livro para o Asio. Você pode vincular isso na sua resposta? Será muito útil.
Vikas
@vikas Sim, eu concordo que a documentação seja ruim e, às vezes, contraditória, mas comparando com o libuv, é bom começar. Quanto aos livros, edito minha resposta, mas acho que você já viu isso antes (infelizmente não há um livro inteiramente dedicado ao Boost - apenas disperso )
Oleksandr Karaberov 30/10/12
O que você quer dizer com "Então o libuv é funcionalmente um subconjunto do Boost.Asio (TCP / UDP / Sockets e threads)"? De acordo com o TOC, nikhilm.github.com/uvbook/index.html, o libuv possui uma aplicação mais ampla e, em seguida, impulsiona :: asio.
31412 Sergei Nikulov
7
@AlexanderKaraberov você poderia expandir os problemas que a ASIO tem com o UDP?
de Bruno Martinez
2

Adicionando o status de portabilidade: Desde a publicação desta resposta e de acordo com minhas próprias tentativas:

  • O Boost.ASIO não tem suporte oficial para iOS e Android, por exemplo, seu sistema de compilação não funciona para iOS imediatamente.
  • O libuv cria facilmente para iOS e Android, com suporte oficial para Android diretamente nos documentos . Meu próprio script de construção do iOS genérico para projetos baseados em Autotools funciona sem problemas.
kakyo
fonte