Как libuv сравнивается с Boost / ASIO?

240

Я был бы заинтересован в таких аспектах, как:

  • Область применения / особенности
  • производительность
  • зрелость
oberstet
источник
20
Давайте вернемся к этому вопросу и получим хорошие ответы!
Вьетнам
\ о / .. надеюсь, мы получим несколько проницательных ответов!
oberstet

Ответы:

494

Объем

Boost.Asio - это библиотека C ++, которая была основана с упором на работу в сети, но ее возможности асинхронного ввода-вывода были расширены на другие ресурсы. Кроме того, поскольку Boost.Asio является частью библиотек Boost, его область действия немного сужена, чтобы предотвратить дублирование с другими библиотеками Boost. Например, Boost.Asio не будет предоставлять абстракцию потока, поскольку Boost.Thread уже обеспечивает ее.

С другой стороны, libuv библиотека C предназначена для платформы слой для Node.js . Он предоставляет абстракцию для IOCP в Windows, kqueue в macOS и epoll в Linux. Кроме того, похоже, что его область немного увеличилась, чтобы включить абстракции и функциональные возможности, такие как потоки, пулы потоков и связь между потоками.

По своей сути каждая библиотека предоставляет цикл обработки событий и возможности асинхронного ввода-вывода. Они перекрываются для некоторых основных функций, таких как таймеры, сокеты и асинхронные операции. libuv имеет более широкую область действия и предоставляет дополнительные функциональные возможности, такие как абстракции потоков и синхронизации, синхронные и асинхронные операции с файловой системой, управление процессами и т. д. В отличие от оригинальных сетевых фокусных поверхностей Boost.Asio, поскольку он предоставляет более богатый набор связанных с сетью такие возможности, как ICMP, SSL, синхронные операции блокировки и неблокирования, а также операции более высокого уровня для общих задач, включая чтение из потока до получения новой строки.


Список возможностей

Вот краткое сравнение некоторых основных функций. Поскольку разработчики, использующие Boost.Asio, часто имеют другие доступные библиотеки Boost, я решил рассмотреть дополнительные библиотеки Boost, если они либо предоставлены напрямую, либо тривиальны для реализации.

                         Libuv Boost
Цикл событий: да Asio
Threadpool: да Asio + Темы
Заправка:              
  Темы: да Темы
  Синхронизация: да Темы
Операции с файловой системой:
  Синхронный: да Файловая система
  Асинхронный: да Asio + Файловая система
Таймеры: да Asio
Scatter / Gather I / O [1] : нет Asio
Сеть:
  ICMP: нет Asio
  Разрешение DNS: только асинхронный Asio
  SSL: нет Asio
  TCP: только асинхронный Asio
  UDP: только асинхронный Asio
Сигнал:
  Обработка: да Asio
  Отправка: да нет
IPC:
  Доменные сокеты UNIX: да Asio
  Windows Named Pipe: есть Asio
Управление процессом:
  Отделение: да Процесс
  Труба ввода / вывода: да Процесс
  Нерест: да Процесс
Системные запросы:
  Процессор: да нет
  Сетевой интерфейс: да нет
Серийные порты: нет да
TTY: да нет
Загрузка общей библиотеки: есть расширение [2]

1. Scatter / Gather ввода / вывода .

2. Boost.Extension никогда не отправлялось на рассмотрение в Boost. Как отмечено здесь , автор считает, что это завершено.

Цикл событий

Хотя и libuv, и Boost.Asio предоставляют циклы событий, между ними есть некоторые тонкие различия:

  • Хотя libuv поддерживает несколько циклов событий, она не поддерживает запуск одного и того же цикла из нескольких потоков. По этой причине необходимо соблюдать осторожность при использовании цикла loop ( uv_default_loop()), а не при создании нового цикла ( uv_loop_new()), поскольку другой компонент может выполнять цикл по умолчанию.
  • Boost.Asio не имеет понятия цикла по умолчанию; все они io_serviceимеют свои собственные циклы, которые позволяют запускать несколько потоков. Для поддержки этого Boost.Asio выполняет внутреннюю блокировку за счет некоторой производительности . Пересмотр Boost.Asio в истории указывает на то, что были некоторые улучшения производительности для минимизации блокировки.

Threadpool

  • libuv обеспечивает пул потоков через uv_queue_work. Размер пула потоков настраивается через переменную окружения UV_THREADPOOL_SIZE. Работа будет выполнена вне цикла событий и в пуле потоков. После завершения работы обработчик завершения будет поставлен в очередь для запуска в цикле событий.
  • Хотя Boost.Asio не предоставляет пул потоков, он io_serviceможет легко функционировать как единое целое в результате io_serviceразрешения вызова нескольких потоков run. Это возлагает ответственность за управление потоками и поведение на пользователя, как можно видеть в этом примере.

Потоки и синхронизация

  • libuv предоставляет абстракцию для потоков и типов синхронизации.
  • Boost.Thread предоставляет поток и типы синхронизации. Многие из этих типов близко следуют стандарту C ++ 11, но также предоставляют некоторые расширения. В результате Boost.Asio, позволяющего нескольким потокам запускать один цикл событий, он предоставляет цепочки в качестве средства для последовательного вызова обработчиков событий без использования явных механизмов блокировки.

Операции с файловой системой

  • libuv предоставляет абстракцию для многих операций файловой системы. Для каждой операции предусмотрена одна функция, и каждая операция может быть синхронной или асинхронной. Если предусмотрен обратный вызов, то операция будет выполняться асинхронно в пределах внутреннего пула потоков. Если обратный вызов не предусмотрен, то вызов будет синхронной блокировкой.
  • Boost.Filesystem обеспечивает синхронные вызовы блокировки для многих операций файловой системы. Их можно комбинировать с Boost.Asio и пулом потоков для создания операций асинхронной файловой системы.

сетей

  • libuv поддерживает асинхронные операции над сокетами UDP и TCP, а также разрешение DNS. Разработчики приложений должны знать, что базовые файловые дескрипторы настроены на неблокирование. Поэтому собственные синхронные операции должны проверять возвращаемые значения и errno для EAGAINили EWOULDBLOCK.
  • Boost.Asio немного более богат сетевой поддержкой. Кроме того, многие функции, которые предоставляет libuv для сетей, поддерживают Boost.Asio с сокетами SSL и ICMP. Кроме того, Boost.Asio обеспечивает синхронную блокировку и синхронные неблокирующие операции в дополнение к асинхронным операциям. Существует множество автономных функций, которые обеспечивают общие высокоуровневые операции, такие как чтение заданного количества байтов или до считывания указанного символа разделителя.

сигнал

  • libuv обеспечивает абстракцию killи обработку сигналов с ее uv_signal_tтипом и uv_signal_*операциями.
  • Boost.Asio не предоставляет абстракцию kill, но signal_setобеспечивает обработку сигналов.

IPC


Различия API

Хотя API-интерфейсы различаются в зависимости от языка, вот несколько ключевых отличий:

Ассоциация Операторов и Обработчиков

В Boost.Asio существует однозначное сопоставление между операцией и обработчиком. Например, каждая async_writeоперация будет вызывать WriteHandler один раз. Это верно для многих операций и обработчиков libuv. Тем не менее, libuv uv_async_sendподдерживает отображение многие-к-одному. Многократные uv_async_sendвызовы могут привести к тому, что uv_async_cb будет вызван один раз.

Цепочки вызовов против петель наблюдателя

При работе с такими задачами, как чтение из потока / UDP, обработка сигналов или ожидание по таймерам, асинхронные цепочки вызовов Boost.Asio немного более явны. С libuv, наблюдатель создается для обозначения интересов в конкретном событии. Затем для наблюдателя запускается цикл, где предоставляется обратный вызов. После получения события интереса, обратный вызов будет вызван. С другой стороны, Boost.Asio требует, чтобы операция выполнялась каждый раз, когда приложение заинтересовано в обработке события.

Чтобы проиллюстрировать это различие, приведем асинхронный цикл чтения с Boost.Asio, где async_receiveвызов будет выполняться несколько раз:

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

И вот тот же пример с libuv, где handle_readвызывается каждый раз, когда наблюдатель наблюдает, что сокет имеет данные:

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

Выделение памяти

В результате асинхронных цепочек вызовов в Boost.Asio и наблюдателей в libuv распределение памяти часто происходит в разное время. С наблюдателями libuv откладывает распределение до тех пор, пока не получит событие, которое требует памяти для обработки. Распределение выполняется с помощью обратного вызова пользователя, вызывается изнутри libuv и откладывает ответственность приложения за освобождение. С другой стороны, для многих операций Boost.Asio требуется выделение памяти перед выполнением асинхронной операции, например, bufferдля for async_read. Boost.Asio обеспечивает null_buffersвозможность прослушивания события, позволяя приложениям откладывать выделение памяти до тех пор, пока она не понадобится, хотя это не рекомендуется.

Эта разница в распределении памяти также проявляется в bind->listen->acceptцикле. С libuv uv_listenсоздает цикл обработки событий, который будет вызывать обратный вызов пользователя, когда соединение будет готово к приему. Это позволяет приложению отложить выделение клиента до попытки подключения. С другой стороны, Boost.Asio listenтолько изменяет состояние acceptor. В async_acceptпрослушивает случае соединения, и требует пира , которые будут выделены , прежде чем ссылаться.


Производительность

К сожалению, у меня нет конкретных показателей для сравнения libuv и Boost.Asio. Тем не менее, я наблюдал похожую производительность при использовании библиотек в приложениях реального времени и почти реального времени. Если желательны жесткие числа, отправной точкой может служить тест libuv .

Кроме того, в то время как профилирование должно быть сделано, чтобы идентифицировать фактические узкие места, помните о распределении памяти. Для libuv стратегия выделения памяти в основном ограничена обратным вызовом распределителя. С другой стороны, API Boost.Asio не допускает обратный вызов распределителя и вместо этого выдвигает стратегию выделения в приложение. Однако обработчики / обратные вызовы в Boost.Asio могут быть скопированы, выделены и освобождены. Boost.Asio позволяет приложениям предоставлять пользовательские функции выделения памяти для реализации стратегии выделения памяти для обработчиков.


зрелость

Boost.Asio

Разработка Asio началась как минимум в октябре 2004 года, и она была принята в Boost 1.35 22 марта 2006 года после 20-дневной экспертной оценки. Он также служил эталонной реализацией и API для предложения сетевой библиотеки для TR2 . Boost.Asio имеет достаточное количество документации , хотя его полезность варьируется от пользователя к пользователю.

У API также есть довольно последовательное чувство. Кроме того, асинхронные операции явно указаны в имени операции. Например, acceptсинхронная блокировка и async_acceptасинхронная. API предоставляет бесплатные функции для обычной задачи ввода-вывода, например, чтение из потока до тех пор, пока не \r\nбудет прочитано. Также было уделено внимание скрытию некоторых специфических для сети деталей, таких как ip::address_v4::any()представление адреса «все интерфейсы» 0.0.0.0.

Наконец, Boost 1.47+ обеспечивает отслеживание обработчиков , которое может оказаться полезным при отладке, а также поддержку C ++ 11.

libuv

Основываясь на своих графах github, разработка Node.js началась, по крайней мере , в феврале 2009 года , а разработка libuv - в марте 2011 года . Uvbook является отличным местом для введения libuv. Документация по API находится здесь .

В целом, API довольно последовательный и простой в использовании. Одна аномалия, которая может быть источником путаницы, - это uv_tcp_listenсоздание петли наблюдателя. Это отличается от других наблюдателей, которые обычно имеют uv_*_startи uv_*_stopпару функций для управления жизненным циклом наблюдателя. Также некоторые uv_fs_*операции имеют приличное количество аргументов (до 7). С синхронным и асинхронным поведением, определяемым при наличии обратного вызова (последний аргумент), видимость синхронного поведения может быть уменьшена.

Наконец, быстрый взгляд на историю коммита libuv показывает, что разработчики очень активны.

Тэннер Сансбери
источник
2
Спасибо чувак! Отличный ответ! Я не могу придумать ничего более всеобъемлющего :)
Viet
1
Очень доволен ответом, я награждаю вас щедростью :) Позвольте СУ решить для себя лучший ответ.
Вьетнам
29
Невероятный ответ. Это относится как к высокоуровневой картине, так и к конкретным, важным различиям в деталях (например, многопоточность / eventloop). Большое спасибо!
Оберштет
1
@oberstet: Нет. Я обновил ответ, чтобы упомянуть, что большинство операций libuv выполняются один на один. Однако libuv может накапливать несколько uv_async_sendвызовов и обрабатывать их все с помощью одного обратного вызова. Это задокументировано здесь . Также спасибо всем.
Таннер Сансбери
2
Внутренняя блокировка в цикле событий в Boost.Asio выглядит страшно с точки зрения производительности. Как он может иметь аналогичную производительность без блокировки libuv? Может быть, полезно добавить предупреждение о производительности.
Zeodtr
46

Хорошо. У меня есть некоторый опыт использования обеих библиотек, и я могу кое-что прояснить.

Во-первых, с концептуальной точки зрения эти библиотеки совершенно разные по дизайну. У них разные архитектуры, потому что они разного масштаба. Boost.Asio - это большая сетевая библиотека, предназначенная для использования с протоколами TCP / UDP / ICMP, POSIX, SSL и так далее. Libuv - это всего лишь слой для межплатформенной абстракции IOCP для Node.js, преимущественно. Таким образом, libuv функционально является подмножеством Boost.Asio (общие функции только для потоков TCP / UDP Sockets, таймеров). В этом случае мы можем сравнить эти библиотеки, используя только несколько критериев:

  1. Интеграция с Node.js - Libuv значительно лучше, потому что он предназначен для этого (мы можем полностью интегрировать его и использовать во всех аспектах, например, в облаке, например, в Windows Azure). Но Asio также реализует почти ту же функциональность, что и в среде, управляемой очередью событий Node.js.
  2. Производительность IOCP - я не мог видеть больших различий, потому что обе эти библиотеки абстрагируют базовый OS API. Но они делают это по-другому: Asio интенсивно использует функции C ++, такие как шаблоны и иногда TMP. Libuv является родной C-библиотекой. Но тем не менее реализация IOCP в Asio очень эффективна. UDP-сокеты в Asio недостаточно хороши, для них лучше использовать libuv.

    Интеграция с новыми функциями C ++: Asio лучше (Asio 1.51 широко использует асинхронную модель C ++ 11, семантику перемещения, шаблоны с переменным числом аргументов). Что касается зрелости, Asio - более стабильный и зрелый проект с хорошей документацией (если сравнивать его с libuv). описание заголовков), много информации через Интернет (видео-беседы, блоги: http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg = 1 и т. Д.) И даже книги (не для профессионалов, но тем не менее: http://en.highscore.de/cpp/boost/index.html ). У Libuv есть только одна онлайн-книга (но тоже хорошая) http://nikhilm.github.com/uvbook/index.htmlи несколько видео-бесед, поэтому будет сложно узнать все секреты (в этой библиотеке их много). Для более конкретного обсуждения функций смотрите мои комментарии ниже.

В заключение я должен сказать, что все зависит от ваших целей, вашего проекта и того, что конкретно вы намерены делать.

Александр Караберов
источник
11
Важны ваши технические навыки и опыт. Добрый привет от кубинца.
dsign
2
Я согласен со всеми вашими пунктами, кроме документации Asio. Официальная документация не имеет никакого отношения к этой замечательной библиотеке. Есть много других документов и разговоров с автором, которые я нашел очень полезными. И я не встречал книгу для Асио. Можете ли вы связать это в своем ответе? Это будет очень полезно.
Викас
@vikas Да, я согласен, что документация скудная и иногда противоречивая, но по сравнению с libuv она хороша для начала. Что касается книг, я редактирую свой ответ, но думаю, что вы видели его раньше (к сожалению, нет книги, полностью посвященной Boost - только разбросанной информация)
Александр Караберов
Что вы подразумеваете под "Так что libuv функционально является подмножеством Boost.Asio (TCP / UDP / Сокеты и потоки)"? Согласно TOC nikhilm.github.com/uvbook/index.html libuv имеет более широкое применение, чем boost :: asio.
Сергей Никулов
7
@AlexanderKaraberov Не могли бы вы рассказать о проблемах ASIO с UDP?
Бруно Мартинес
2

Добавление статуса переносимости: На момент публикации этого ответа и в соответствии с моими собственными попытками:

  • Boost.ASIO не имеет официальной поддержки iOS и Android, например, его система сборки не работает для iOS из коробки.
  • libuv легко собирается для iOS и Android, с официальной поддержкой Android прямо в их документах . Мой собственный универсальный скрипт сборки iOS для проектов на основе Autotools работает без проблем.
kakyo
источник
Довольно просто создать кроссплатформенный фреймворк для iOS и andriod, используя bazel, используя правила сборки bazel boost.
nnrales