¿Cómo se compara libuv con Boost / ASIO?

239

Me interesarían aspectos como:

  • alcance / características
  • actuación
  • madurez
oberstet
fuente
20
¡Retrocedamos esta pregunta y obtengamos buenas respuestas!
Viet
\ o / .. ¡espero que obtengamos algunas respuestas perspicaces!
oberstet

Respuestas:

493

Alcance

Boost.Asio es una biblioteca de C ++ que comenzó con un enfoque en las redes, pero sus capacidades de E / S asíncronas se han extendido a otros recursos. Además, con Boost.Asio como parte de las bibliotecas de Boost, su alcance se reduce ligeramente para evitar la duplicación con otras bibliotecas de Boost. Por ejemplo, Boost.Asio no proporcionará una abstracción de subproceso, ya que Boost.Thread ya proporciona uno.

Por otro lado, libuv es una biblioteca de C diseñado para ser la capa de plataforma para Node.js . Proporciona una abstracción para IOCP en Windows, kqueue en macOS y epoll en Linux. Además, parece que su alcance ha aumentado ligeramente para incluir abstracciones y funcionalidades, como hilos, agrupaciones de hilos y comunicación entre hilos.

En esencia, cada biblioteca proporciona un bucle de eventos y capacidades de E / S asíncronas. Se superponen para algunas de las características básicas, como temporizadores, sockets y operaciones asincrónicas. libuv tiene un alcance más amplio y proporciona funcionalidad adicional, como abstracciones de hilos y sincronización, operaciones de sistemas de archivos síncronos y asíncronos, gestión de procesos, etc. capacidades, como ICMP, SSL, operaciones de bloqueo sincrónico y sin bloqueo, y operaciones de nivel superior para tareas comunes, incluida la lectura de una secuencia hasta que se recibe una nueva línea.


Lista de características

Aquí está la breve comparación lado a lado de algunas de las principales características. Dado que los desarrolladores que usan Boost.Asio a menudo tienen otras bibliotecas de Boost disponibles, he optado por considerar bibliotecas de Boost adicionales si se proporcionan directamente o son triviales de implementar.

                         libuv Boost
Bucle de eventos: sí Asio
Threadpool: sí Asio + Threads
Roscado:              
  Hilos: sí Hilos
  Sincronización: sí Hilos
Operaciones del sistema de archivos:
  Sincrónico: sí FileSystem
  Asíncrono: sí Asio + Sistema de archivos
Temporizadores: sí Asio
Scatter / Gather I / O [1] : no Asio
Redes:
  ICMP: no Asio
  Resolución DNS: Asio solo asíncrono
  SSL: no Asio
  TCP: Asio solo asíncrono
  UDP: Asio solo asíncrono
Señal:
  Manejo: sí Asio
  Envío: si no
IPC:
  Sockets de dominio UNIX: sí Asio
  Canalización con nombre de Windows: sí Asio
Gestión de proceso:
  Separación: sí Proceso
  Tubería de E / S: sí Proceso
  Desove: sí Proceso
Consultas del sistema:
  CPU: sí no
  Interfaz de red: sí no
Puertos seriales: no si
TTY: si no
Carga de la biblioteca compartida: sí Extensión [2]

1. dispersión / agrupación I / O .

2. Boost.Extension nunca se envió para su revisión a Boost. Como se señaló aquí , el autor lo considera completo.

Bucle de eventos

Si bien tanto libuv como Boost.Asio proporcionan bucles de eventos, existen algunas diferencias sutiles entre los dos:

  • Si bien libuv admite múltiples bucles de eventos, no admite ejecutar el mismo bucle desde múltiples subprocesos. Por esta razón, se debe tener cuidado al usar el bucle predeterminado ( uv_default_loop()), en lugar de crear un nuevo bucle ( uv_loop_new()), ya que otro componente puede estar ejecutando el bucle predeterminado.
  • Boost.Asio no tiene la noción de un bucle predeterminado; todos io_serviceson sus propios bucles que permiten ejecutar múltiples subprocesos. Para admitir este Boost.Asio realiza un bloqueo interno a costa de cierto rendimiento . El historial de revisión de Boost.Asio indica que ha habido varias mejoras de rendimiento para minimizar el bloqueo.

Threadpool

  • libuv's proporciona un conjunto de subprocesos a través uv_queue_work. El tamaño del conjunto de hilos es configurable a través de la variable de entorno UV_THREADPOOL_SIZE. El trabajo se ejecutará fuera del bucle de eventos y dentro del conjunto de hilos. Una vez que se completa el trabajo, el controlador de finalización se pondrá en cola para ejecutarse dentro del bucle de eventos.
  • Si bien Boost.Asio no proporciona un conjunto de subprocesos, io_servicepuede funcionar fácilmente como uno solo porque io_servicepermite invocar varios subprocesos run. Esto pone la responsabilidad de la gestión de hilos y el comportamiento del usuario, como se puede ver en este ejemplo.

Subprocesos y sincronización

  • libuv proporciona una abstracción a hilos y tipos de sincronización.
  • Boost.Thread proporciona un hilo y tipos de sincronización. Muchos de estos tipos siguen de cerca al estándar C ++ 11, pero también proporcionan algunas extensiones. Como resultado de que Boost.Asio permite que varios subprocesos ejecuten un solo bucle de eventos, proporciona hebras como un medio para crear una invocación secuencial de controladores de eventos sin usar mecanismos de bloqueo explícitos.

Operaciones del sistema de archivos

  • libuv proporciona una abstracción para muchas operaciones del sistema de archivos. Hay una función por operación, y cada operación puede ser bloqueo síncrono o asíncrono. Si se proporciona una devolución de llamada, la operación se ejecutará de forma asíncrona dentro de un conjunto de subprocesos interno. Si no se proporciona una devolución de llamada, la llamada será un bloqueo sincrónico.
  • Boost.Filesystem proporciona llamadas de bloqueo sincrónico para muchas operaciones del sistema de archivos. Estos se pueden combinar con Boost.Asio y un conjunto de subprocesos para crear operaciones asincrónicas del sistema de archivos.

Redes

  • libuv admite operaciones asincrónicas en sockets UDP y TCP, así como la resolución DNS. Los desarrolladores de aplicaciones deben tener en cuenta que los descriptores de archivo subyacentes están configurados en sin bloqueo. Por lo tanto, las operaciones síncronas nativas deben verificar los valores de retorno y errno para EAGAINo EWOULDBLOCK.
  • Boost.Asio es un poco más rico en su soporte de redes. Además, muchas de las funciones que ofrece la red de libuv, Boost.Asio admite sockets SSL e ICMP. Además, Boost.Asio proporciona bloqueo sincrónico y operaciones sin bloqueo sincrónico, además de sus operaciones asincrónicas. Existen numerosas funciones independientes que proporcionan operaciones comunes de nivel superior, como leer una cantidad establecida de bytes o hasta que se lea un carácter delimitador especificado.

Señal

  • libuv proporciona una abstracción killy manejo de señal con su uv_signal_ttipo y uv_signal_*operaciones.
  • Boost.Asio no proporciona una abstracción kill, pero signal_setproporciona manejo de señal.

IPC


Diferencias API

Si bien las API son diferentes según el idioma solo, aquí hay algunas diferencias clave:

Asociación de Operadores y Manipuladores

Dentro de Boost.Asio, hay un mapeo uno a uno entre una operación y un controlador. Por ejemplo, cada async_writeoperación invocará al WriteHandler una vez. Esto es cierto para muchas de las operaciones y controladores de libuv. Sin embargo, libuv's uv_async_sendadmite un mapeo de muchos a uno. Varias uv_async_sendllamadas pueden provocar que se llame a uv_async_cb una vez.

Call Chains vs. Watcher Loops

Cuando se trata de tareas, como leer desde un flujo / UDP, manejar señales o esperar temporizadores, las cadenas de llamadas asíncronas de Boost.Asio son un poco más explícitas. Con libuv, se crea un observador para designar intereses en un evento en particular. Luego se inicia un bucle para el observador, donde se proporciona una devolución de llamada. Al recibir el evento de intereses, se invocará la devolución de llamada. Por otro lado, Boost.Asio requiere que se emita una operación cada vez que la aplicación esté interesada en manejar el evento.

Para ayudar a ilustrar esta diferencia, aquí hay un ciclo de lectura asíncrono con Boost.Asio, donde la async_receivellamada se emitirá varias veces:

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

Y aquí está el mismo ejemplo con libuv, donde handle_readse invoca cada vez que el observador observa que el socket tiene datos:

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

Asignación de memoria

Como resultado de las cadenas de llamadas asincrónicas en Boost.Asio y los observadores en libuv, la asignación de memoria a menudo ocurre en diferentes momentos. Con los observadores, libuv difiere la asignación hasta después de recibir un evento que requiere memoria para manejar. La asignación se realiza a través de una devolución de llamada del usuario, invocada internamente a libuv, y difiere la responsabilidad de desasignación de la aplicación. Por otro lado, muchas de las operaciones Boost.Asio requieren que la memoria se asigne antes de emitir la operación asincrónica, como el caso de bufferfor async_read. Boost.Asio proporciona null_buffers, que se puede usar para escuchar un evento, permitiendo que las aplicaciones difieran la asignación de memoria hasta que se necesite memoria, aunque esto está en desuso.

Esta diferencia de asignación de memoria también se presenta dentro del bind->listen->acceptbucle. Con libuv, uv_listencrea un bucle de eventos que invocará la devolución de llamada del usuario cuando una conexión esté lista para ser aceptada. Esto permite que la aplicación difiera la asignación del cliente hasta que se intente una conexión. Por otro lado, Boost.Asio listensolo cambia el estado de la acceptor. Las async_acceptescuchas para el evento de conexión, y requiere que la pareja se asignarán antes de ser invocado.


Actuación

Desafortunadamente, no tengo ningún número de referencia concreto para comparar libuv y Boost.Asio. Sin embargo, he observado un rendimiento similar al usar las bibliotecas en aplicaciones en tiempo real y casi en tiempo real. Si se desean números duros, la prueba de referencia de libuv puede servir como punto de partida.

Además, aunque se debe realizar un perfil para identificar cuellos de botella reales, tenga en cuenta las asignaciones de memoria. Para libuv, la estrategia de asignación de memoria se limita principalmente a la devolución de llamada del asignador. Por otro lado, la API de Boost.Asio no permite una devolución de llamada del asignador, y en su lugar empuja la estrategia de asignación a la aplicación. Sin embargo, los controladores / devoluciones de llamada en Boost.Asio pueden copiarse, asignarse y desasignarse. Boost.Asio permite que las aplicaciones proporcionen funciones de asignación de memoria personalizadas para implementar una estrategia de asignación de memoria para los manejadores.


Madurez

Boost.Asio

El desarrollo de Asio se remonta al menos a OCT-2004, y fue aceptado en Boost 1.35 el 22-MAR-2006 después de someterse a una revisión por pares de 20 días. También sirvió como implementación de referencia y API para la propuesta de biblioteca de red para TR2 . Boost.Asio tiene una buena cantidad de documentación , aunque su utilidad varía de usuario a usuario.

La API también tiene una sensación bastante consistente. Además, las operaciones asincrónicas son explícitas en el nombre de la operación. Por ejemplo, acceptes bloqueo síncrono y async_acceptes asíncrono. La API proporciona funciones gratuitas para tareas de E / S comunes, por ejemplo, leer desde una secuencia hasta que \r\nse lea una. También se ha prestado atención para ocultar algunos detalles específicos de la red, como la ip::address_v4::any()representación de la dirección de "todas las interfaces" 0.0.0.0.

Finalmente, Boost 1.47+ proporciona un seguimiento de controladores , que puede resultar útil al depurar, así como compatibilidad con C ++ 11.

libuv

Según sus gráficos de github, el desarrollo de Node.js se remonta al menos a FEB-2009 , y el desarrollo de libuv a MAR-2011 . El uvbook es un gran lugar para una introducción a libuv. La documentación de la API está aquí .

En general, la API es bastante consistente y fácil de usar. Una anomalía que puede ser una fuente de confusión es que uv_tcp_listencrea un circuito de observación. Esto es diferente de otros observadores de que generalmente tienen una uv_*_starty uv_*_stoppar de funciones para controlar la vida del bucle observador. Además, algunas de las uv_fs_*operaciones tienen una cantidad decente de argumentos (hasta 7). Con el comportamiento sincrónico y asincrónico que se determina en presencia de una devolución de llamada (el último argumento), la visibilidad del comportamiento sincrónico puede verse disminuida.

Finalmente, un rápido vistazo al libuv historial de confirmación de muestra que los desarrolladores son muy activos.

Tanner Sansbury
fuente
2
¡Gracias hombre! ¡Gran respuesta! No puedo pensar en nada más completo :)
Viet
1
Muy contento con la respuesta, le otorgo la recompensa :) Deje que el SO decida la mejor respuesta por sí mismo.
Viet
28
Increíble respuesta. Esto cubre tanto la imagen de alto nivel, como las diferencias de detalles específicas e importantes (por ejemplo, threading / eventloop). ¡Muchas gracias!
oberstet
1
@oberstet: no. He actualizado la respuesta para mencionar que la mayoría de las operaciones de libuv son individuales. Sin embargo, libuv puede acumular múltiples uv_async_sendllamadas y manejarlas todas con una sola devolución de llamada. Está documentado aquí . Además, gracias a todos.
Tanner Sansbury
2
El bloqueo interno en el bucle de eventos en Boost.Asio parece aterrador desde el punto de vista del rendimiento. ¿Cómo puede tener un rendimiento similar al libuv sin bloqueo? Tal vez agregar una declaración de advertencia en la sección de rendimiento puede ser útil.
zeodtr
46

Okay. Tengo cierta experiencia en el uso de ambas bibliotecas y puedo aclarar algunas cosas.

Primero, desde un punto de vista conceptual, estas bibliotecas son bastante diferentes en diseño. Tienen arquitecturas diferentes, porque son de diferente escala. Boost.Asio es una gran biblioteca de red destinada a ser utilizada con protocolos TCP / UDP / ICMP, POSIX, SSL, etc. Libuv es solo una capa para la abstracción multiplataforma de IOCP para Node.js, predominantemente. Entonces, libuv es funcionalmente un subconjunto de Boost.Asio (características comunes solo hilos de sockets TCP / UDP, temporizadores). Siendo ese el caso, podemos comparar estas bibliotecas usando solo unos pocos criterios:

  1. Integración con Node.js: Libuv es considerablemente mejor porque está dirigido a esto (podemos integrarlo completamente y usarlo en todos los aspectos, por ejemplo, en la nube, por ejemplo, ventanas azules). Pero Asio también implementa casi la misma funcionalidad que en el entorno controlado por la cola de eventos Node.js.
  2. Rendimiento de IOCP: no pude ver grandes diferencias, porque ambas bibliotecas resumen la API del sistema operativo subyacente. Pero lo hacen de una manera diferente: Asio utiliza en gran medida características de C ++ como plantillas y, a veces, TMP. Libuv es una biblioteca C nativa. Sin embargo, la realización de Asio de IOCP es muy eficiente. Los sockets UDP en Asio no son lo suficientemente buenos, es mejor usar libuv para ellos.

    Integración con nuevas características de C ++: Asio es mejor (Asio 1.51 usa ampliamente el modelo asíncrono C ++ 11, mueve semántica, plantillas variadas). En cuanto a la madurez, Asio es un proyecto más estable y maduro con buena documentación (si se compara con libuv descripción de encabezados), mucha información a través de Internet (videoconferencias, blogs: http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg = 1 , etc.) e incluso libros (no para profesionales, sin embargo: http://en.highscore.de/cpp/boost/index.html ). Libuv tiene solo un libro en línea (pero también bueno) http://nikhilm.github.com/uvbook/index.htmly varias videoconferencias, por lo que será difícil conocer todos los secretos (esta biblioteca tiene muchos de ellos). Para una discusión más específica de las funciones, vea mis comentarios a continuación.

Como conclusión, debería decir que todo depende de sus propósitos, su proyecto y lo que concretamente tiene la intención de hacer.

Oleksandr Karaberov
fuente
11
Lo que importa es su habilidad técnica y experiencia. Saludos cordiales de un cubano.
dsign
2
Estoy de acuerdo con todos sus puntos, excepto la documentación de Asio. La documentación oficial no hace justicia a esta maravillosa biblioteca. Hay muchos otros documentos y una charla de impulso del autor que encontré muy útil. Y no he encontrado un libro para Asio. ¿Puedes vincular eso en tu respuesta? Será de gran ayuda.
Vikas
@vikas Sí, estoy de acuerdo con que la documentación es deficiente y, a veces, contradictoria, pero en comparación con libuv es agradable para comenzar. En cuanto a los libros, edito mi respuesta, pero creo que la has visto antes (desafortunadamente no hay un libro dedicado exclusivamente a Boost, solo disperso información)
Oleksandr Karaberov
¿Qué quiere decir con "Entonces, libuv es funcionalmente un subconjunto de Boost.Asio (TCP / UDP / Sockets and threads)"? De acuerdo con TOC, nikhilm.github.com/uvbook/index.html libuv tiene una aplicación más amplia y luego aumenta :: asio.
Sergei Nikulov
77
@AlexanderKaraberov, ¿podría ampliar los problemas que ASIO tiene con UDP?
Bruno Martinez
2

Agregar el estado de portabilidad: a partir de la publicación de esta respuesta y de acuerdo con mis propios intentos:

  • Boost.ASIO no tiene soporte oficial para iOS y Android, por ejemplo, su sistema de compilación no funciona para iOS de fábrica.
  • libuv se construye fácilmente para iOS y Android, con soporte oficial para Android directamente en sus documentos . Mi propio script genérico de compilación de iOS para proyectos basados ​​en Autotools funciona sin problemas.
kakyo
fuente
Es bastante fácil construir un marco multiplataforma para iOS y Android usando Bazel usando las reglas de construcción de Bazel Boost.
nnrales hace