web-dev-qa-db-ja.com

libuvはBoost / ASIOと比較してどうですか?

次のような側面に興味があります。

  • スコープ/機能
  • 性能
  • 成熟
217
oberstet

範囲

Boost.Asio は、ネットワーキングに重点を置いて開始されたC++ライブラリですが、その非同期I/O機能は他のリソースに拡張されています。また、Boost.AsioがBoostライブラリの一部であるため、他のBoostライブラリとの重複を防ぐために、その範囲がわずかに狭められています。たとえば、Boost.Asioは Boost.Thread が既に提供しているため、スレッドの抽象化を提供しません。

一方、 libuv は、 Node.js のプラットフォーム層になるように設計されたCライブラリです。 Windowsでは IOCP 、macOSでは kqueue 、Linuxでは epoll の抽象化を提供します。さらに、スレッド、スレッドプール、スレッド間通信などの抽象化と機能を含めるために、スコープがわずかに増加したように見えます。

コアでは、各ライブラリがイベントループと非同期I/O機能を提供します。これらは、タイマー、ソケット、非同期操作などの基本機能の一部と重複しています。 libuvはより広い範囲を持ち、スレッドと同期の抽象化、同期と非同期のファイルシステム操作、プロセス管理などの追加機能を提供します。対照的に、Boost.Asioの元のネットワークフォーカスサーフェスは、ネットワーク関連の豊富なセットを提供します。 ICMP、SSL、同期ブロックおよび非ブロック操作などの機能、および改行が受信されるまでストリームから読み取るなどの一般的なタスクの高レベル操作。


機能リスト

以下に、いくつかの主要な機能の簡単な比較を示します。 Boost.Asioを使用する開発者は他のBoostライブラリを利用できることが多いため、直接提供されるか実装が簡単な場合、追加のBoostライブラリを検討することを選択しました。

 libuv Boost 
イベントループ:はいAsio 
スレッドプール:はいAsio +スレッド
スレッド:
スレッド:はいスレッド
同期:はいスレッド
ファイルシステム操作:
同期:はいFileSystem 
非同期:はいAsio + Filesystem 
タイマー:はいAsio 
 Scatter/Gather I/O[1]:Asio 
ネットワーキングなし:
 ICMP:Asio 
 DNS解像度:非同期のみAsio 
 SSL:Asio 
 TCP:非同期のみAsio 
 UDP:非同期のみAsio 
 Signal:
処理:はいAsio 
送信:はいno 
 IPC:
 UNIXドメインソケット:はいAsio 
 Windows名前付きパイプ:はいAsio 
プロセス管理:
切り離し:はいProcess 
 I/Oパイプ:はいProcess 
生成:はいプロセス
システムクエリ:
 CPU:はいいいえ
ネットワークインターフェイス:はいいいえ
シリアルポート:いいえはい
 TTY:はいいいえ
共有ライブラリの読み込み:はい拡張[2]

1. 散乱/収集I/O

2. Boost.Extension は、Boostへのレビューのために送信されませんでした。 here のように、著者はそれが完全であると考えています。

イベントループ

LibuvとBoost.Asioの両方がイベントループを提供しますが、2つの間にわずかな違いがあります。

  • Libuvは複数のイベントループをサポートしていますが、複数のスレッドから同じループを実行することはサポートしていません。このため、別のコンポーネントがデフォルトループを実行している可能性があるため、新しいループ(uv_default_loop())を作成するのではなく、デフォルトループ(uv_loop_new())を使用する場合は注意が必要です。
  • Boost.Asioにはデフォルトのループという概念はありません。すべてのio_serviceは、複数のスレッドを実行できる独自のループです。これをサポートするために、Boost.Asioは 内部ロック を実行しますが、一部のコストがかかります パフォーマンス 。 Boost.Asioのリビジョン history は、ロックを最小化するためにいくつかのパフォーマンスの改善があったことを示します。

スレッドプール

  • libuvはuv_queue_workを介してスレッドプールを提供します。スレッドプールのサイズは、環境変数UV_THREADPOOL_SIZEで設定できます。作業は、イベントループ外およびスレッドプール内で実行されます。作業が完了すると、完了ハンドラーがキューに入れられ、イベントループ内で実行されます。
  • Boost.Asioはスレッドプールを提供しませんが、io_serviceio_serviceの結果として簡単に機能し、複数のスレッドがrunを呼び出すことができます。 this の例に見られるように、これはユーザーにスレッド管理と動作の責任を負わせます。

スレッド化と同期

  • libuvは、スレッドと同期タイプの抽象化を提供します。
  • Boost.Thread は、スレッドと同期タイプを提供します。これらのタイプの多くは、C++ 11標準に厳密に従いますが、いくつかの拡張機能も提供します。 Boost.Asioが複数のスレッドに単一のイベントループの実行を許可する結果、明示的なロックメカニズムを使用せずにイベントハンドラーの順次呼び出しを作成する手段として strands を提供します。

ファイルシステム操作

  • libuvは、多くのファイルシステム操作の抽象化を提供します。操作ごとに1つの関数があり、各操作は同期ブロッキングまたは非同期のいずれかです。コールバックが提供される場合、操作は内部スレッドプール内で非同期的に実行されます。コールバックが提供されない場合、呼び出しは同期ブロッキングになります。
  • Boost.Filesystem は、多くのファイルシステム操作に対して同期ブロッキング呼び出しを提供します。これらをBoost.Asioおよびスレッドプールと組み合わせて、非同期ファイルシステム操作を作成できます。

ネットワーキング

  • libuvは、UDPおよびTCPソケットの非同期操作、およびDNS解決をサポートします。アプリケーション開発者は、基礎となるファイル記述子が非ブロッキングに設定されていることに注意する必要があります。したがって、ネイティブの同期操作は、EAGAINまたはEWOULDBLOCKの戻り値とerrnoをチェックする必要があります。
  • Boost.Asioは、ネットワーキングサポートがもう少し豊富です。さらに、libuvのネットワークが提供する機能の多く、Boost.AsioはSSLおよびICMPソケットをサポートしています。さらに、Boost.Asioは、非同期操作に加えて、同期ブロック操作と同期非ブロック操作を提供します。一定量のバイトの読み取りや、指定された区切り文字が読み取られるまで、一般的な高レベルの操作を提供する多数の独立した関数があります。

信号

  • libuvは、uv_signal_t型とuv_signal_*操作を使用して、抽象化killとシグナル処理を提供します。
  • Boost.Asioはkillの抽象化を提供しませんが、その signal_set はシグナル処理を提供します。

IPC


APIの違い

APIは言語のみに基づいて異なりますが、いくつかの重要な違いがあります。

操作とハンドラーの関連付け

Boost.Asio内には、操作とハンドラーの間に1対1のマッピングがあります。たとえば、各 async_write 操作は、WriteHandlerを1回呼び出します。これは多くのlibuv操作とハンドラーに当てはまります。ただし、libuvのuv_async_sendは多対1のマッピングをサポートします。複数の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操作の多くでは、async_readbufferの場合など、非同期操作を発行する前にメモリを割り当てる必要があります。 Boost.Asioは null_buffers を提供します。これはイベントのリッスンに使用でき、メモリが必要になるまでアプリケーションがメモリ割り当てを延期できますが、これは非推奨です。

このメモリ割り当ての違いは、bind->listen->acceptループ内にも現れます。 libuvでは、uv_listenは、接続を受け入れる準備ができたときにユーザーコールバックを呼び出すイベントループを作成します。これにより、アプリケーションは、接続が試行されるまでクライアントの割り当てを延期できます。一方、Boost.Asioの listen は、 acceptor の状態のみを変更します。 async_accept は接続イベントをリッスンし、呼び出す前にピアを割り当てる必要があります。


性能

残念ながら、libuvとBoost.Asioを比較する具体的なベンチマーク値はありません。ただし、リアルタイムおよびほぼリアルタイムのアプリケーションでライブラリを使用して、同様のパフォーマンスを観察しました。ハードナンバーが必要な場合、libuvの ベンチマークテスト が開始点として使用できます。

さらに、実際のボトルネックを特定するためにプロファイリングを行う必要がありますが、メモリの割り当てに注意してください。 libuvの場合、メモリ割り当て戦略は主にアロケーターコールバックに制限されます。一方、Boost.AsioのAPIはアロケーターコールバックを許可せず、代わりに割り当て戦略をアプリケーションにプッシュします。ただし、Boost.Asioのハンドラー/コールバックはコピー、割り当て、および割り当て解除できます。 Boost.Asioでは、ハンドラー用のメモリ割り当て戦略を実装するために、アプリケーションが カスタムメモリ割り当て 関数を提供できます。


成熟

Boost.Asio

Asioの開発は少なくともOCT-2004にまで遡り、20日間のピアレビューを経て2006年3月22日にBoost 1.35に受け入れられました。また、 TR2のネットワークライブラリの提案 のリファレンス実装およびAPIとしても機能しました。 Boost.Asioにはかなりの ドキュメント がありますが、その有用性はユーザーによって異なります。

APIには、かなり一貫した感触もあります。さらに、非同期操作は操作名で明示的です。たとえば、acceptは同期ブロッキングで、async_acceptは非同期です。 APIは、\r\nが読み取られるまでストリームから読み取るなど、一般的なI/Oタスク用の無料の関数を提供します。また、0.0.0.0の「すべてのインターフェース」アドレスを表すip::address_v4::any()など、ネットワーク固有の詳細を隠すように注意が払われています。

最後に、Boost 1.47+は handler tracking を提供します。これはデバッグ時に便利であることが証明でき、C++ 11のサポートも可能です。

libuv

Githubグラフに基づいて、Node.jsの開発は少なくとも FEB-2009 に遡り、libuvの開発は MAR-2011 に遡ります。 vbook はlibuvの紹介に最適な場所です。 APIドキュメントは here です。

全体的に、APIはかなり一貫性があり、使いやすいです。混乱の原因になる可能性のある異常の1つは、uv_tcp_listenがウォッチャーループを作成することです。これは、一般的にウォッチャーループの寿命を制御する関数のuv_*_startuv_*_stopのペアを持つ他のウォッチャーとは異なります。また、いくつかのuv_fs_*操作には、かなりの数の引数(最大7)があります。コールバック(最後の引数)の存在で決定される同期および非同期の動作により、同期動作の可視性が低下する可能性があります。

最後に、libuv commit history をざっと見ると、開発者が非常に活発であることがわかります。

463
Tanner Sansbury

OK。私は両方のライブラリを使用した経験があり、いくつかのことを明確にすることができます。

まず、概念的な観点から、これらのライブラリは設計がまったく異なります。規模が異なるため、アーキテクチャが異なります。 Boost.Asioは、TCP/UDP/ICMPプロトコル、POSIX、SSLなどで使用することを目的とした大規模なネットワークライブラリです。 Libuvは、主にNode.jsの IOCP のクロスプラットフォーム抽象化のための単なるレイヤーです。したがって、libuvは機能的にBoost.Asioのサブセットです(一般的な機能はTCP/UDPソケットスレッド、タイマーのみです)。そのため、いくつかの基準のみを使用してこれらのライブラリを比較できます。

  1. Node.jsとの統合-Libuvはこれを目的としているため、かなり優れています(Windows Azureなどのクラウドなど、あらゆる側面で完全に統合して使用できます)。しかし、AsioはNode.jsイベントキュー駆動型環境とほぼ同じ機能も実装しています。
  2. IOCPのパフォーマンス-どちらのライブラリも基盤となるOS APIを抽象化するため、大きな違いは見られませんでした。しかし、彼らは別の方法でそれを行います。Asioは、テンプレートやTMPなどのC++機能を頻繁に使用します。 LibuvはネイティブCライブラリです。しかし、IOCPのAsio実現は非常に効率的です。 AsioのUDPソケットは十分ではないため、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にはオンラインブックが1つしかありませんが(これも良い) http://nikhilm.github.com/uvbook/index.html といくつかのビデオトークがあるため、すべての秘密を知ることは困難です(これはライブラリにはたくさんあります)。関数のより具体的な説明については、以下の私のコメントを参照してください。

結論として、それはすべてあなたの目的、あなたのプロジェクト、具体的にあなたが何をしようとしているのかに依存していると言っておくべきです。

45

大きな違いの1つは、Asio(Christopher Kohlhoff)の著者がC++標準ライブラリに含めるために自分のライブラリをグルーミングしていることです。 http://www.open-std.org/jtc1/sc22/wg21/docs/papers /2007/n2175.pdf および http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4370.html

16
Vinnie Falco