I'd be interested in aspects like:
- scope/features
- performance
- maturity
ベストアンサー1
Scope
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ブースト イベントループ: はい Asio スレッドプール: はい Asio + スレッド スレッド: スレッド: はい スレッド 同期: はい スレッド ファイル システム操作: 同期: はい ファイルシステム 非同期: はい Asio + ファイルシステム タイマー: はい Asio スキャッター/ギャザーI/O [1] : Asioなし ネットワーキング: ICMP: Asio なし DNS 解決: 非同期のみの Asio SSL: Asio なし TCP: 非同期のみの Asio UDP: 非同期のみの Asio 信号: 取り扱い:はいアシオ 送信: はい いいえ 国際PC: UNIXドメインソケット: はい Asio Windows 名前付きパイプ: はい Asio プロセス管理: 取り外し: はい プロセス I/Oパイプ: はいプロセス 産卵: はい プロセス システムクエリ: CPU: はい いいえ ネットワークインターフェース: はい いいえ シリアルポート: いいえ はい TTY: はい いいえ 共有ライブラリの読み込み: はい 拡張機能[2]
2. Boost.Extension はBoost にレビューのために提出されたことはありません。ここで述べたように、作者はそれが完成していると考えています。
イベントループ
libuv と Boost.Asio はどちらもイベント ループを提供しますが、両者の間には微妙な違いがいくつかあります。
uv_default_loop()
libuv は複数のイベント ループをサポートしていますが、複数のスレッドから同じループを実行することはサポートしていません。このため、別のコンポーネントがデフォルト ループを実行している可能性があるため、新しいループ ( ) を作成するのではなく、デフォルト ループ ( )を使用する場合は注意がuv_loop_new()
必要です。- Boost.Asio にはデフォルトのループという概念はありません。すべては
io_service
複数のスレッドを実行できる独自のループです。これをサポートするために、Boost.Asio はパフォーマンスを犠牲にして内部ロックを実行します。Boost.Asio の改訂履歴を見ると、ロックを最小限に抑えるためにパフォーマンスが何度か改善されたことがわかります。
スレッドプール
- libuv は を通じてスレッドプールを提供します
uv_queue_work
。スレッドプールのサイズは環境変数 で設定できますUV_THREADPOOL_SIZE
。作業はイベント ループの外側、スレッドプール内で実行されます。作業が完了すると、完了ハンドラがキューに入れられ、イベント ループ内で実行されます。 - Boost.Asio はスレッドプールを提供しませんが、複数のスレッドが を呼び出すことができる
io_service
ため、 は簡単にスレッドプールとして機能することができます。この例でわかるように、これにより、スレッドの管理と動作の責任がユーザーに移ります。io_service
run
スレッドと同期
- libuv はスレッドと同期タイプの抽象化を提供します。
- Boost.Thread は、スレッドと同期型を提供します。これらの型の多くは C++11 標準に厳密に従っていますが、いくつかの拡張機能も提供しています。Boost.Asio では、複数のスレッドが単一のイベント ループを実行できるため、明示的なロック メカニズムを使用せずにイベント ハンドラーを順番に呼び出す手段としてストランドが提供されます。
ファイルシステム操作
- libuv は、多くのファイル システム操作の抽象化を提供します。操作ごとに 1 つの関数があり、各操作は同期ブロッキングまたは非同期のいずれかになります。コールバックが提供されている場合、操作は内部スレッドプール内で非同期に実行されます。コールバックが提供されていない場合、呼び出しは同期ブロッキングになります。
- 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
シグナル処理を提供します。
国際PC
- libuv は、Unix ドメイン ソケットとWindows 名前付きパイプを単一の
uv_pipe_t
型で抽象化します。 - Boost.Asio はこれら 2 つを
local::stream_protocol::socket
またはlocal::datagram_protocol::socket
とに分離しますwindows::stream_handle
。
APIの違い
API は言語によってのみ異なりますが、主な違いは次のとおりです。
オペレーションとハンドラーの協会
Boost.Asio では、操作とハンドラーの間に 1 対 1 のマッピングがあります。たとえば、各操作はWriteHandlerasync_write
を 1 回呼び出します。これは、多くの libuv 操作とハンドラーに当てはまります。ただし、libuv は多対 1 のマッピングをサポートしています。複数の呼び出しにより、 uv_async_cb が1 回呼び出される場合があります。uv_async_send
uv_async_send
コールチェーンとウォッチャーループ
ストリーム/UDP からの読み取り、シグナルの処理、タイマーの待機などのタスクを処理する場合、Boost.Asio の非同期呼び出しチェーンはもう少し明確になります。libuv では、特定のイベントへの関心を指定するためにウォッチャーが作成されます。次に、ウォッチャーのループが開始され、コールバックが提供されます。関心のあるイベントを受信すると、コールバックが呼び出されます。一方、Boost.Asio では、アプリケーションがイベントの処理に関心を持つたびに、操作を発行する必要があります。
async_receive
この違いを説明するために、呼び出しが複数回発行されるBoost.Asio を使用した非同期読み取りループを次に示します。
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 操作の多くでは、 for の場合のように、非同期操作を発行する前にメモリを割り当てる必要がありますbuffer
。Boost.Asioasync_read
は、イベントをリッスンするために使用できる を提供します。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 では、ハンドラのメモリ割り当て戦略を実装するために、アプリケーションがカスタム メモリ割り当て関数を提供できます。
成熟
ブースト.Asio
Asio の開発は少なくとも 2004 年 10 月にさかのぼり、20 日間のピアレビューを経て、2006 年 3 月 22 日に Boost 1.35 に採用されました。また、TR2 のネットワーク ライブラリ提案のリファレンス実装および API としても機能しました。Boost.Asio には十分な量のドキュメントがありますが、その有用性はユーザーによって異なります。
API もかなり一貫した感覚を持っています。さらに、非同期操作は操作名に明示されています。たとえば、accept
は同期ブロッキングで、async_accept
は非同期です。API は、一般的な I/O タスク用のフリー関数を提供します。たとえば、が読み込まれるまでストリームから読み取ります。また、が「すべてのインターフェイス」のアドレスを表す\r\n
など、ネットワーク固有の詳細を隠すことにも配慮されています。ip::address_v4::any()
0.0.0.0
最後に、Boost 1.47+ では、デバッグ時に役立つハンドラー トラッキングと C++11 のサポートが提供されます。
リブヴ
github のグラフによると、Node.js の開発は少なくとも2009 年 2 月まで遡り、libuv の開発は2011 年 3 月まで遡ります。uvbook はlibuvの入門書として最適です。API ドキュメントはこちら にあります。
全体的に、API はかなり一貫性があり、使いやすいです。混乱の原因となる可能性のある 1 つの異常は、uv_tcp_listen
ウォッチャー ループを作成することです。これは、ウォッチャー ループの存続期間を制御するために 1 組の関数を通常持つ他のウォッチャーとは異なりますuv_*_start
。uv_*_stop
また、一部のuv_fs_*
操作には、かなりの数の引数 (最大 7 個) があります。同期動作と非同期動作はコールバック (最後の引数) の存在によって決定されるため、同期動作の可視性が低下する可能性があります。
最後に、libuv のコミット履歴をざっと見てみると、開発者が非常にアクティブであることがわかります。