web-dev-qa-db-ja.com

スレッドを作成するときのオーバーヘッドはどれくらいですか?

いくつかの本当にひどいコードをレビューしました-シリアルポートでメッセージを送信するコードは、送信されるすべてのメッセージごとに新しいスレッドでメッセージをパッケージ化およびアセンブルするために新しいスレッドを作成します。はい、pthreadが作成されるすべてのメッセージに対して、ビットが適切に設定され、スレッドが終了します。なぜ誰かがそのようなことをするのかはわかりませんが、実際にスレッドを作成するときのオーバーヘッドはどれくらいですか?

35
jdt141

...シリアルポートにメッセージを送信します... pthreadが作成されるすべてのメッセージに対して、ビットが適切に設定され、スレッドが終了します。 ...実際にスレッドを作成するときのオーバーヘッドはどれくらいですか?

これはシステム固有です。たとえば、前回VMSスレッドを使用したときは悪夢のように遅かった(何年もですが、メモリから1つのスレッドが毎秒10のようなものを作成できました(そして、スレッドを終了せずに数秒間それを維持した場合、コアになります))、一方、Linuxではおそらく数千を作成できます。正確に知りたい場合は、システムでベンチマークしてください。ただし、メッセージについて詳しくは知らずに、平均5バイトか100kか、連続して送信されるか、回線がアイドル状態になるか、アプリの遅延要件がどれほど関連するかを知っているだけではあまり役に立ちません。スレッド作成オーバーヘッドの絶対測定としてのコードのスレッド使用の適切性。また、パフォーマンスを設計上の主要な考慮事項にする必要はないかもしれません。

7
Tony Delroy

この古いスレッドを復活させるために、単純なテストコードをいくつか実行しました。

#include <thread>

int main(int argc, char** argv)
{
  for (volatile int i = 0; i < 500000; i++)
    std::thread([](){}).detach();
  return 0;
}

g++ test.cpp -std=c++11 -lpthread -O3 -o testでコンパイルしました。次に、古い(カーネル2.6.18)負荷の重い(データベースの再構築を行う)低速のラップトップ(Intelコアi5-2540M)で3回連続で実行しました。 3つの連続した実行の結果:5.647秒、5.515秒、および5.561秒。そのため、このマシンではスレッドごとに10マイクロ秒を超えるわずかな時間を見ていますが、おそらくあなたのマシンではずっと少ないでしょう。

シリアルポートが10マイクロ秒あたり約1ビットで最大になることを考えると、オーバーヘッドはそれほど大きくありません。今、もちろん、渡された/キャプチャされた引数に関係するさまざまな追加のスレッド損失があります(ただし、関数呼び出し自体がいくつかを課す可能性があります)、コア間のキャッシュのスローダウン(異なるコアの複数のスレッドが同じメモリを同時に戦っている場合)、しかし、一般に、あなたがすでに「本当にひどいコード」という概念に先手を打ってラベル付けしているにもかかわらず、あなたが提示したユースケースがパフォーマンスに悪影響を与えることは非常に疑いますスレッドを起動します。

良いアイデアかどうかは、状況の詳細に大きく依存します。呼び出しスレッドは他に何を担当していますか?パケットの準備と書き出しには正確に何が関係しますか?それらはどのくらいの頻度で書き出され(どのような種類の分布?均一、クラスター化など?)、その構造はどのようなものですか?システムにはいくつのコアがありますか?詳細などに応じて、最適なソリューションは「スレッドなし」から「共有スレッドプール」、「各パケットのスレッド」までのいずれかです。

スレッドプールは魔法ではなく、場合によってはユニークスレッドと比較してスローダウンする可能性があることに注意してください。スレッドの最大のスローダウンの1つは、同時に複数のスレッドが使用するキャッシュメモリと、別のスレッドからの更新を探して処理するには、これを行う必要があります。そのため、プロセッサが他のプロセスがメモリのセクションを変更したかどうか不明な場合、プライマリスレッドまたは子処理スレッドのいずれかが待機する必要があります。対照的に、理想的な状況では、特定のタスクの一意の処理スレッドは、呼び出したタスクとメモリを1回だけ(起動時に)共有するだけでよく、その後、互いに干渉することはありません。

38
Nafnlaus

特にプロセスを作成する代替手段と比較すると、スレッドの作成は安価であると常に言われてきました。あなたが話しているプログラムに、並行して実行する必要のある操作が多くない場合、スレッド化は必要ないかもしれません。私をバックアップするいくつかの文献:

http://www.personal.kent.edu/~rmuhamma/OpSystems/Myos/threads.htm

スレッドは、という意味で安価です

  1. スタックとレジスタ用のストレージのみが必要なため、スレッドの作成は安価です

  2. スレッドは、動作しているオペレーティングシステムのリソースをほとんど使用しません。つまり、スレッドは新しいアドレス空間、グローバルデータ、プログラムコード、またはオペレーティングシステムリソースを必要としません。

  3. スレッドを使用する場合、コンテキストの切り替えは高速です。理由は、PC、SPおよびレジスターのみを保存または復元する必要があるからです。

もっと同じ こちら

Operating System Concepts 8th Edition (155ページ)で、著者はスレッド化の利点について書いています:

プロセス作成のためのメモリとリソースの割り当てはコストがかかります。スレッドは、それらが属するプロセスのリソースを共有するため、作成とコンテキストの作成がより経済的です。スレッドを切り替えます。オーバーヘッドの違いを経験的に測定することは困難な場合がありますが、一般に、プロセスの作成と管理にはスレッドよりもはるかに時間がかかります。たとえば、Solarisでは、プロセスの作成はスレッドの作成よりも約30倍遅く、コンテキストの切り替えは約5倍遅くなります。

11
ubiquibacon

これは絶対にしたくないでしょう。単一のスレッドまたはスレッドのプールを作成し、メッセージが利用可能になるとシグナルを送信します。シグナルを受信すると、スレッドは必要なメッセージ処理を実行できます。

オーバーヘッドの観点から、特にWindowsでのスレッドの作成/破棄はかなり高価です。具体的には、数十マイクロ秒のオーダー。ほとんどの場合、動的にサイズ変更されたスレッドプールの例外を除き、アプリの開始/終了時にのみ実行する必要があります。

10

スレッド作成には多少のオーバーヘッドがありますが、シリアルポートの通常遅いボーレート(19200ビット/秒が最も一般的)と比較すると、問題ではありません。

9
ruslik

比較のために、OSXを見てみましょう: Link

  • カーネルデータ構造:約1 KBスタックスペース:512 KB(セカンダリスレッド):8 MB(OS Xメインスレッド)、1 MB(iOSメインスレッド)

  • 作成時間:約90マイクロ秒

Posixスレッドの作成もこれに近いはずです(遠くない数字)。

4
Lunar Mushrooms

スレッドでのスレッドの作成と計算はかなり高価です。すべてのデータ構造を設定する必要があり、新しいスレッドが実際に実行されるように、カーネルに登録されたスレッドとスレッド切り替えが発生する必要があります(不特定で予測不可能な時間に)。 thread.startを実行しても、スレッドのメイン関数がすぐに呼び出されるわけではありません。記事(typokingで言及)が指摘しているように、スレッドの作成はプロセスの作成と比較して安価です。全体的に、かなり高価です。

私は決してスレッドを使用しません

  • 短い計算のために
  • コードのフローで結果が必要な計算(つまり、スレッドを開始し、その計算結果を返すのを待つ)

あなたの例では、すべてのシリアル通信を処理し、永久的なスレッドを作成することは(すでに指摘したように)理にかなっています。

hth

マリオ

3
Mario The Spoon

作成したVOIPアプリで上記の「ひどい」デザインを使用しました。それは非常にうまく機能しました...ローカルに接続されたコンピューターの場合、遅延やパケットの欠落/欠落はまったくありません。データパケットが到着するたびに、スレッドが作成され、そのデータを処理して出力デバイスに渡します。もちろん、パケットは大きいため、ボトルネックは発生しませんでした。その間、メインスレッドはループバックして待機し、別の着信パケットを受信できます。

私は必要なスレッドが事前に作成される他のデザインを試しましたが、これはそれ自体の問題を作成します。最初に、着信パケットを取得して確定的な方法で処理するために、スレッド用にコードを適切に設計する必要があります。複数の(事前に割り当てられた)スレッドを使用する場合、パケットが「順不同」で処理される可能性があります。単一の(事前に割り当てられた)スレッドを使用して、着信パケットをループおよびピックアップすると、スレッドが問題を検出し、データを処理するスレッドを残して終了する可能性があります。

着信データパケットをそれぞれ処理するスレッドを作成すると、特にマルチコアシステムや着信パケットが大きい場合に非常にきれいに機能します。また、より直接的に質問に答えるために、スレッド作成の代わりに、事前に割り当てられたスレッドを管理するランタイムプロセスを作成することもできます。データのハンドオフと処理を同期でき、エラーを検出できるため、単純に新しいスレッドを作成するのと同じくらいオーバーヘッドが増えます。それはすべて、設計と要件に依存します。

3
user2074102