web-dev-qa-db-ja.com

ノンブロッキングI / OはマルチスレッドブロッキングI / Oよりも本当に速いですか?どうやって?

I/Oのブロックと非I/Oのブロックに関する技術的な詳細をWebで検索しましたが、I/OをブロックするよりもI/Oをブロックしない方が速いと言う人が何人かいました。たとえば、 このドキュメント にあります。

ブロッキングI/Oを使用する場合、もちろん現在ブロックされているスレッドは他に何もできません...ブロックされているためです。ただし、スレッドがブロックされ始めるとすぐに、OSは別のスレッドに切り替えることができ、ブロックされたスレッドに対して何かするまで戻ることはできません。 CPUを必要とし、ブロックされていない別のスレッドがシステムにある限り、イベントベースの非ブロッキングアプローチと比較して、CPUのアイドル時間はないはずです。

CPUがアイドル状態である時間を短縮することに加えて、特定の時間枠でコンピューターが実行できるタスクの数を増やすもう1つのオプションがあります。スレッドの切り替えによって生じるオーバーヘッドを減らします。しかし、これはどのように行うことができますか?そして、オーバーヘッドは測定可能な効果を示すのに十分な大きさですか?これがどのように機能するかを考えてみましょう。

  1. ファイルの内容をロードするために、アプリケーションはこのタスクをイベントベースのI/Oフレームワークに委任し、ファイル名とともにコールバック関数を渡します
  2. イベントフレームワークはオペレーティングシステムに委任します。オペレーティングシステムは、DMAハードディスクのコントローラーをプログラムして、ファイルをメモリに直接書き込む
  3. イベントフレームワークでは、さらにコードを実行できます。
  4. ディスクからメモリへのコピーが完了すると、DMAコントローラーにより割り込みが発生します。
  5. オペレーティングシステムの割り込みハンドラーは、ファイルがメモリに完全にロードされていることをイベントベースのI/Oフレームワークに通知します。どうやってそれをしますか?信号を使用しますか?
  6. イベントI/Oフレームワーク内で現在実行されているコードは終了します。
  7. イベントベースのI/Oフレームワークは、キューをチェックし、ステップ5からのオペレーティングシステムのメッセージを確認し、ステップ1で取得したコールバックを実行します。

それはどのように機能しますか?そうでない場合、どのように機能しますか?つまり、明示的にスタックに触れる必要なくイベントシステムが動作できることを意味します(スタックをバックアップし、スレッドを切り替えながら別のスレッドのスタックをメモリにコピーする必要がある実際のスケジューラなど)。これは実際にどれくらいの時間を節約しますか?それ以上ありますか?

109
yankee

ノンブロッキングまたは非同期I/Oの最大の利点は、スレッドが並行して作業を継続できることです。もちろん、追加のスレッドを使用してこれを達成することもできます。全体的な(システム)パフォーマンスを最高にするために述べたように、複数のスレッドではなく非同期I/Oを使用する方が良いと思います(したがって、スレッドの切り替えを減らします)。

並列に接続された1000のクライアントを処理するネットワークサーバープログラムの可能な実装を見てみましょう。

  1. 接続ごとに1つのスレッド(I/Oをブロックすることができますが、非I/Oをブロックすることもできます)。
    各スレッドにはメモリリソース(カーネルメモリも必要)が必要です。これは不利です。また、スレッドが追加されるたびに、スケジューラの作業が増えます。
  2. すべての接続に1つのスレッド。
    スレッドが少ないため、システムの負荷がかかります。ただし、1つのプロセッサを100%に駆動し、他のすべてのプロセッサをアイドル状態にしてしまう可能性があるため、マシンの完全なパフォーマンスを使用できなくなります。
  3. 各スレッドがいくつかの接続を処理する少数のスレッド
    スレッドが少ないため、システムの負荷がかかります。そして、利用可能なすべてのプロセッサを使用できます。 Windowsでは、このアプローチは Thread Pool API でサポートされています。

もちろん、スレッドを増やすこと自体は問題ではありません。ご存知のように、非常に多くの接続/スレッドを選択しました。ダーススレッドのみについて話している場合、3つの可能な実装に違いがあるとは思わないでしょう(これは、Raymond ChenがMSDNブログ投稿で提案していることです プロセスごとのスレッド? )。

Windowsで unbufferedファイルI/O を使用すると、書き込みはページサイズの倍数のサイズでなければなりません。私はそれをテストしていませんが、これはバッファリングされた同期および非同期書き込みの書き込みパフォーマンスにもプラスの影響を与える可能性があるようです。

説明するステップ1から7は、それがどのように機能するかについての良いアイデアを与えます。 Windowsでは、オペレーティングシステムは、イベントまたはコールバックを使用して、非同期I/O(WriteFile構造を持つOVERLAPPED構造)の完了について通知します。コールバック関数は、たとえば、コードがWaitForMultipleObjectsExbAlertableに設定してtrueを呼び出す場合にのみ呼び出されます。

Webでさらに読む:

43
Werner Henze

I/Oには、ハードドライブからのデータの読み取りや書き込み、ネットワークリソースへのアクセス、Webサービスの呼び出し、データベースからのデータの取得など、複数の種類の操作が含まれます。プラットフォームと操作の種類に応じて、非同期I/Oは通常、操作を実行するためのハードウェアまたは低レベルのシステムサポートを利用します。これは、CPUへの影響をできるだけ少なくして実行されることを意味します。

アプリケーションレベルでは、非同期I/Oにより、スレッドがI/O操作の完了を待つ必要がなくなります。非同期I/O操作が開始されるとすぐに、それが起動されたスレッドが解放され、コールバックが登録されます。操作が完了すると、最初の利用可能なスレッドで実行するためにコールバックがキューに入れられます。

I/O操作が同期的に実行される場合、操作が完了するまで実行中のスレッドは何もしません。ランタイムは、I/O操作がいつ完了するかを知らないため、実行中の実際のCPUバウンド操作を持つ他のスレッドが使用できるCPU時間を待機スレッドに定期的に提供します。

そのため、@ user1629468で述べたように、非同期I/Oはパフォーマンスを向上させるのではなく、スケーラビリティを向上させます。これは、Webアプリケーションの場合のように、使用可能なスレッドの数が限られているコンテキストで実行する場合に明らかです。 Webアプリケーションは通常、スレッドプールを使用し、そこから各要求にスレッドを割り当てます。長時間実行されるI/O操作で要求がブロックされると、Webプールが枯渇し、Webアプリケーションがフリーズしたり、応答が遅くなったりするリスクがあります。

私が気づいたことの1つは、非常に高速なI/O操作を処理する場合、非同期I/Oは最適なオプションではないということです。その場合、I/O操作の完了を待機している間、スレッドをビジーにしないことの利点はそれほど重要ではなく、操作が1つのスレッドで開始され、別のスレッドで完了するという事実は、全体的な実行にオーバーヘッドを追加します。

私が最近行った非同期I/Oとマルチスレッドのトピックに関するより詳細な研究を読むことができます here

28

AIOを使用する主な理由は、スケーラビリティのためです。いくつかのスレッドのコンテキストで見ると、その利点は明らかではありません。しかし、システムが数千のスレッドに拡張されると、AIOのパフォーマンスは大幅に向上します。警告は、AIOライブラリがそれ以上のボトルネックを導入すべきではないということです。

4
fissurezone

任意の形式のマルチコンピューティングによる速度の改善を想定するには、複数のCPUベースのタスクが複数のコンピューティングリソース(通常はプロセッサコア)で同時に実行されているか、またはすべてのタスクが同じリソース-つまり、一部のタスクはシステムサブコンポーネント(ディスクストレージなど)に依存し、一部のタスクは別のタスク(周辺機器からの通信の受信)に依存し、さらに他のタスクはプロセッサコアの使用を必要とする場合があります。

最初のシナリオは、多くの場合「パラレル」プログラミングと呼ばれます。 2番目のシナリオはしばしば「同時」または「非同期」プログラミングと呼ばれますが、「同時」は、オペレーティングシステムが複数のタスクの実行をインターリーブすることを許可する場合にも使用されることがあります。直列に配置するか、複数のリソースを使用して並列実行を実現できる場合。後者の場合、「同時」とは一般に、タスク実行の実際の同時性という観点からではなく、プログラムでの実行の記述方法を指します。

このすべてについて暗黙の仮定で話すのは非常に簡単です。たとえば、「非同期I/OはマルチスレッドI/Oより高速になります」などの主張をすばやく行う人もいます。この主張はいくつかの理由で疑わしい。まず、特定の非同期I/Oフレームワークがマルチスレッドで正確に実装されている場合があります。この場合、それらは同じものであり、1つの概念が他の概念より「速い」とは意味がありません。

第二に、非同期フレームワークのシングルスレッド実装(シングルスレッドイベントループなど)がある場合でも、そのループが何をしているのかを引き続き仮定する必要があります。たとえば、シングルスレッドイベントループでできる愚かなことの1つは、CPUにバインドされた2つの異なるタスクを非同期的に完了するように要求することです。理想的なシングルプロセッサコア(最新のハードウェア最適化を無視する)のみを搭載したマシンでこれを実行した場合、このタスクを「非同期」で実行しても、独立して管理される2つのスレッドまたは1つの単独プロセスで実行する場合と実際に異なることはありません-スレッドコンテキストスイッチングまたはオペレーティングシステムスケジュールの最適化に違いが生じる可能性がありますが、両方のタスクがCPUに送信される場合、どちらの場合も同様です。

あなたが出くわすかもしれない異常な、または愚かなコーナーケースの多くを想像することは有用です。

「非同期」は、たとえば上記のように同時である必要はありません。1つのプロセッサコアを備えたマシンで2つのCPUバウンドタスクを「非同期で」実行します。

マルチスレッド実行は同時である必要はありません:単一のプロセッサコアを持つマシンで2つのスレッドを生成するか、2つのスレッドに他の種類の希少なリソースを取得するように要求します(たとえば、1つだけを確立できるネットワークデータベースを想像してください)一度に接続)。スレッドの実行はinterleavedかもしれませんが、オペレーティングシステムスケジューラは適切と見なしますが、シングルコア(またはより一般的には、以下の場合、スレッドコンテキストスイッチングから合計ランタイムを減らすことはできません)実行するコアよりも多くのスレッドを生成するか、リソースが維持できるものより多くのスレッドをリソースに要求します)。これと同じことがマルチプロセッシングにも当てはまります。

したがって、非同期I/Oもマルチスレッドも、実行時間に関してパフォーマンスを向上させる必要はありません。彼らは物事を遅くすることさえできます。

ただし、リモートデータベースなどのネットワーク接続リソースからデータを取得するためにネットワーク呼び出しを行い、ローカルCPUにバインドされた計算を行う特定のプログラムのように、特定のユースケースを定義する場合、ハードウェアに関する特定の仮定が与えられた2つの方法のパフォーマンスの違い。

質問:実行する必要がある計算ステップの数と、それらを実行するためのリソースの独立したシステムの数。独立したシステムサブコンポーネントの使用を必要とする計算ステップのサブセットはありますか?プロセッサコアはいくつありますか?複数のプロセッサまたはスレッドを使用して別々のコアでタスクを完了するためのオーバーヘッドはどれくらいですか?

タスクが主に独立したサブシステムに依存している場合、非同期ソリューションが適している可能性があります。処理に必要なスレッドの数が多く、コンテキストスイッチングがオペレーティングシステムにとって重要にならない場合は、シングルスレッドの非同期ソリューションの方が適している可能性があります。

タスクが同じリソースにバインドされている場合(たとえば、複数の同じネットワークまたはローカルリソースに同時にアクセスする必要がある場合)、マルチスレッドはおそらく不十分なオーバーヘッドをもたらしますが、シングルスレッドの非同期mayオーバーヘッド、このようなリソースが限られた状況では、速度を上げることもできません。そのような場合、唯一のオプション(高速化が必要な場合)は、そのリソースの複数のコピーを使用可能にすることです(たとえば、リソースがCPUの場合は複数のプロセッサコア、リソースが不足している場合はより多くの同時接続をサポートするより良いデータベース接続制限されたデータベースなどです)。

別の言い方をすると、オペレーティングシステムが2つのタスクに対して単一のリソースの使用をインターリーブできるようにしますcannotは、一方のタスクにリソースを使用させ、もう一方のタスクに待機させてから2番目のタスクを許可させるよりも高速です順次終了します。さらに、インターリーブのスケジューラコストは、実際の状況では実際にスローダウンを引き起こします。 CPU、ネットワークリソース、メモリリソース、周辺機器、またはその他のシステムリソースのインターリーブされた使用が発生するかどうかは関係ありません。

4
ely

ノンブロッキングI/Oの1つの可能な実装は、まさにI/Oをブロックし、コールバックメカニズムを介してI/Oの発信元のスレッドに通知するバックグラウンドスレッドのプールを備えたものです。実際、これがglibcの [〜#〜] aio [〜#〜] モジュールの仕組みです。 ここ は、実装に関する曖昧な詳細です。

これは非常に移植性の高い優れたソリューションですが(スレッドがある場合)、OSは通常、ノンブロッキングI/Oをより効率的に処理できます。 このウィキペディアの記事 は、スレッドプール以外の可能な実装をリストしています。

2
Miguel

現在、プロトスレッドを使用して組み込みプラットフォームで非同期ioを実装しています。ノンブロッキングioは、16000fpsと160fpsでの実行を区別します。ノンブロッキングioの最大の利点は、ハードウェアが実行している間に他のことを実行するようにコードを構成できることです。デバイスの初期化も並行して実行できます。

マーティン

2
user2826084

Nodeでは、複数のスレッドが起動されますが、C++ランタイムの下位層です。

「そのため、NodeJSはシングルスレッドですが、これは半分真実です。実際には、イベントドリブンでバックグラウンドワーカーを備えたシングルスレッドです。 Node.jsのI/O APIは、イベントループに対応するために設計上非同期/非ブロッキングであるためです。

https://codeburst.io/how-node-js-single-thread-mechanism-work-understanding-event-loop-in-nodejs-230f7440b0ea

「Node.jsは非ブロッキングです。つまり、すべての関数(コールバック)はイベントループに委任され、異なるスレッドによって実行されます(または実行されます)。これはNode.jsランタイムによって処理されます。」

https://itnext.io/multi-threading-and-multi-process-in-node-js-ffa5bb5cde98

「ノードはブロックされていないため高速です...」という説明は少しマーケティング的であり、これは素晴らしい質問です。効率的でスケーラブルですが、シングルスレッドではありません。

1

非同期I/Oが機能しないという反例があります。私は、below-using boost :: asioに似たプロキシを書いています。 https://github.com/ArashPartow/proxy/blob/master/tcpproxy_server.cpp

ただし、私の場合のシナリオは、1つのセッションで着信(クライアント側からの)メッセージは高速ですが、発信(サーバー側へ)は遅いため、着信速度に追いつくため、またはプロキシのスループット全体を最大化するために、 1つの接続で複数のセッション。

したがって、この非同期I/Oフレームワークはもう機能しません。各スレッドにセッションを割り当てることにより、サーバーに送信するスレッドプールが必要です。

0
Zhidian Du

私が知っている限りでは、非同期I/Oは(明確にするためにMSシステムについて話しているだけです)を使用しているということです I/O完了ポートと呼ばれます 。非同期呼び出しを使用することにより、フレームワークはそのようなアーキテクチャを自動的に活用し、これは標準のスレッド化メカニズムよりもはるかに効率的であると想定されています。個人的な経験として、スレッドをブロックするのではなくAsyncCallsを好む場合、アプリケーションをより反応的に感じると言えます。

0
Felice Pollano