[解決済み】libuvはBoost/ASIOと比べてどうなのか?
質問
などに興味があります。
- スコープ/フィーチャー
- 性能
- 成熟度
解決方法は?
範囲
ブースト.アシオ は、ネットワークに着目してスタートしたC++ライブラリですが、その非同期I/O機能は他のリソースにも拡張されています。 また、Boost.AsioはBoostライブラリの一部であるため、他のBoostライブラリとの重複を防ぐために、その範囲は若干狭くなっています。 例えば、Boost.Asioはスレッドの抽象化を提供しません。 ブースト.スレッド はすでに提供されています。
一方 libuv のプラットフォーム層として設計された C ライブラリです。 Node.js . を抽象化したものです。 IOCP をWindows上で実行します。 クエイクエ はmacOSで、そして エポール を追加しました。 さらに、スレッド、スレッドプール、スレッド間通信といった抽象化された機能を含むように、その範囲が若干拡大したように見えます。
各ライブラリの中核は、イベントループと非同期I/O機能を提供することです。 libuv はより広い範囲をカバーし、スレッドや同期の抽象化、同期・非同期ファイルシステム操作、プロセス管理などの追加機能を提供します。 一方、Boost.Asioは、ICMP、SSL、同期ブロッキングおよびノンブロッキング操作、ストリームから改行を受信するまで読み込むなどの一般的なタスクに対する高レベル操作など、ネットワーク関連の機能をより豊富に提供しており、ネットワークに焦点を当てた本来の姿が浮かび上がっています。
機能一覧
ここでは、主要な機能を簡単に並べて比較しています。 Boost.Asioを使っている開発者は、他のBoostライブラリを利用していることが多いので、直接提供されているか、実装が簡単であれば、追加のBoostライブラリも考慮することにしました。
libuv ブースト イベントループ:あり Asio スレッドプール: はい Asio + Threads スレッド化 スレッド: はい スレッド 同期: はい スレッド ファイルシステム操作。 同期: はい ファイルシステム 非同期: はい Asio + Filesystem タイマー:あり Asio スキャッター/ギャザーI/O [1] アシオなし ネットワーキング ICMP: なし Asio DNS解決:非同期のみAsio SSL:なし アシオ TCP:非同期型アシオ UDP:非同期のみAsio シグナル ハンドリング: はい Asio 送信: はい いいえ IPC UNIX ドメインソケット: はい Asio Windows 名前付きパイプ:あり Asio プロセス管理。 デタッチ: はい プロセス I/Oパイプ:あり プロセス スポーン: はい プロセス システムクエリ。 CPU: はい いいえ ネットワークインターフェース: はい いいえ シリアルポート: いいえ はい TTY: はい いいえ 共有ライブラリのロード: はい 拡張 [2]
<サブ 1. スキャッター/ギャザーI/O .
<サブ 2. ブースト.エクステンション はBoostに審査に出されることはなかった。 前述のとおり こちら というもので、作者は完成していると考えています。
イベントループ
libuv と Boost.Asio の両方がイベントループを提供しますが、両者には微妙な違いがあります。
-
libuv は複数のイベントループをサポートしますが、複数のスレッドから同じループを実行することはサポートしません。 このため、デフォルトのループ (
uv_default_loop()
)、新しいループを作るのではなく、(uv_loop_new()
というのも、他のコンポーネントがデフォルトのループを実行している可能性があるからです。 -
Boost.Asioにはデフォルトループという概念はありません。
io_service
は、複数のスレッドを実行することができる独自のループです。 これをサポートするためにBoost.Asioは以下を実行します。 内部ロック の代償として パフォーマンス . Boost.Asioの修正 歴史 は、ロックを最小化するためにいくつかのパフォーマンス改善が行われたことを示しています。
スレッドプール
-
libuv では、スレッドプールを
uv_queue_work
. スレッドプールのサイズは、環境変数UV_THREADPOOL_SIZE
. 作業はイベントループの外側で、スレッドプール内で実行されます。 作業が完了すると、完了ハンドラがイベントループ内で実行されるようにキューに入れられます。 -
Boost.Asioはスレッドプールを提供しませんが
io_service
の結果、簡単に1つの機能として機能することができます。io_service
を複数のスレッドで呼び出すことができます。run
. これは、スレッドの管理と動作の責任をユーザーに負わせるもので、次のようになります。 これ の例です。
スレッドと同期化
- libuv はスレッドと同期型の抽象化を提供します。
- Boost.Thread(ブースト・スレッド は、スレッドと同期の型を提供します。 これらの型の多くは C++11 標準に忠実に準拠しているが、いくつかの拡張も提供する。 Boost.Asio では、複数のスレッドが 1 つのイベントループを実行できるようにした結果、以下のようなものが提供されます。 ストランド は、明示的なロック機構を使用せずにイベントハンドラの連続呼び出しを作成するための手段として使用されます。
ファイルシステム操作
- libuv は多くのファイルシステム操作の抽象化を提供します。 操作ごとに1つの関数があり、各操作は同期ブロックか非同期のいずれかになります。 コールバックが提供された場合、その操作は内部のスレッドプール内で非同期に実行されます。 コールバックが提供されない場合、呼び出しは同期ブロッキングになります。
- ブースト.ファイルシステム は、多くのファイルシステム操作のための同期ブロッキング呼び出しを提供します。 これらを Boost.Asio やスレッドプールと組み合わせることで、非同期なファイルシステム操作を実現することができます。
ネットワーキング
-
libuv は UDP と TCP ソケットの非同期操作と DNS 解決をサポートしています。 アプリケーション開発者は、基礎となるファイルディスクリプタがノンブロッキングに設定されていることに注意する必要があります。 したがって、ネイティブの同期操作では、戻り値をチェックし
エルノ
に対して
EAGAIN
またはEWOULDBLOCK
. - Boost.Asioは、ネットワークサポートが少し豊富です。 libuv のネットワーキングが提供する多くの機能に加えて、Boost.Asio は SSL と ICMP ソケットをサポートしています。 さらに、Boost.Asioは非同期操作に加えて、同期ブロッキングおよび同期ノンブロッキング操作を提供します。 また、設定したバイト数だけ読み込む、指定した区切り文字を読み込むなど、一般的な上位処理を行うフリースタンディング関数が多数用意されています。
シグナル
-
libuv は抽象化された
kill
でのシグナル処理とuv_signal_t
型とuv_signal_*
演算を行う。 -
Boost.Asioは、以下のような抽象化を提供しません。
kill
が、そのsignal_set
はシグナルハンドリングを提供します。
IPC
-
libuvのアブストラクト
ユニックスドメインソケット
と
Windows 名前付きパイプ
を介して、1つの
uv_pipe_t
タイプになります。 -
Boost.Asioでは、この2つを分離して
local::stream_protocol::socket
またはlocal::datagram_protocol::socket
であり、かつwindows::stream_handle
.
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
に対して
async_read
. 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では、アプリケーションから カスタムメモリアロケーション 関数を使用することで、ハンドラのメモリ割り当て戦略を実装することができます。
成熟度
ブースト.アシオ
Asioの開発は少なくとも2004年10月までさかのぼり、20日間のピアレビューを経て2006年3月22日にBoost 1.35にアクセプトされました。 のリファレンス実装とAPIとして使用されました。 TR2へのネットワークライブラリ提案 . Boost.Asioは、かなりの量の ドキュメント その有用性はユーザーによって異なりますが。
また、APIもかなり統一感があります。 さらに、非同期操作は操作名で明示される。 例えば
accept
は同期ブロッキングであり
async_accept
は非同期である。 APIは、一般的なI/Oタスクのためのフリー関数を提供する。
\r\n
が読み込まれます。 また、ネットワーク固有の詳細を隠蔽するために、例えば
ip::address_v4::any()
のすべてのインターフェイスのアドレスを表します。
0.0.0.0
.
最後に、Boost 1.47+ では ハンドラートラッキング C++11のサポートと同様に、デバッグ時に有用であることが証明されています。
libuv
Node.jsの開発は、githubのグラフから、少なくとも以下の時期までさかのぼります。 2009年2月 libuv の開発年代は 2011年3月 . その ユーブック は libuv の紹介に最適な場所です。 API ドキュメントは こちら .
全体として、APIはかなり一貫性があり、使いやすいと思います。 混乱の元となりそうな異常事態のひとつは
uv_tcp_listen
はウォッチャー・ループを作成します。 これは、他のウォッチャーが一般的に
uv_*_start
と
uv_*_stop
のペアで、ウォッチャー・ループの寿命を制御する関数です。 また、いくつかの
uv_fs_*
の操作には、それなりの数の引数があります(最大7個)。 コールバック(最後の引数)の有無で同期・非同期の動作が決まるため、同期動作の視認性が低下してしまうことがあります。
最後に、libuvをざっと見てみましょう。 コミット履歴 は、開発者が非常にアクティブであることを示しています。
関連
-
[解決済み] UbuntuにBoostをインストールする方法
-
[解決済み] explicit キーワードの意味は?
-
[解決済み] 文字列の単語を反復処理するにはどうすればよいですか?
-
[解決済み] 1ビットのセット、クリア、トグルはどのように行うのですか?
-
[解決済み] C++11では、標準化されたメモリモデルが導入されました。その意味するところは?そして、C++プログラミングにどのような影響を与えるのでしょうか?
-
[解決済み] Linux上で動作するC++コードのプロファイリングを行うにはどうすればよいですか?
-
[解決済み] 0.1fを0にすると、なぜ10倍もパフォーマンスが落ちるのですか?
-
[解決済み] C++11のT&&(ダブルアンパサンド)の意味とは?
-
[解決済み】C/C++の"-->"演算子とは何ですか?
-
[解決済み] Intel CPU の _mm_popcnt_u64 で、32 ビットのループカウンターを 64 ビットに置き換えると、パフォーマンスが著しく低下します。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】識別子 "string "は未定義?
-
[解決済み】C++ 式はポインタからオブジェクトへの型を持っている必要があります。
-
[解決済み】テンプレートの引数1が無効です(Code::Blocks Win Vista) - テンプレートは使いません。
-
[解決済み】'cout'は型名ではない
-
[解決済み] 非常に基本的なC++プログラムの問題 - バイナリ式への無効なオペランド
-
[解決済み] 式はクラス型を持つ必要があります。
-
[解決済み】#include<iostream>は存在するのですが、「識別子 "cout "は未定義です」というエラーが出ます。なぜですか?
-
[解決済み】C++の余分な資格エラー
-
[解決済み】Enterキーを押して続行する
-
[解決済み】'std::cout'への未定義の参照