3

私は.NET4.0のParallelライブラリをいじくり回してきました。最近、私は、大規模システムの1つが使用しなければならないいくつかの異常な読み取り/書き込み操作用のカスタムORMを開発しました。これにより、オブジェクトを属性で装飾し、データベースからプルする必要のある列と、書き込み時に出力する必要のあるXMLを反映して把握することができます。

このラッパーは多くのプロジェクトで再利用されることを想定しているので、できるだけスピードを絞りたいと思います。このライブラリは、主に.NETWebアプリケーションで使用されます。使い捨てのコンソールアプリケーションを使用してフレームワークをテストし、作成したクラスを調べています。

マルチスレッドに伴うオーバーヘッドの教訓を学びました。マルチスレッドを使用すると、実行速度が低下します。読んでみると、長い間やっている人には直感的に思えますが、実際には直感に反しています。メソッドを同時に30回実行すると、連続して30回実行するよりも遅くなる可能性があります。 ?

複数のスレッドが同じ共有オブジェクトをめぐって争わなければならないことで問題が発生しているとは思わないので(まだはっきりとはわかりませんが)、速度低下はオーバーヘッドから来ていると思いますそれらすべてのスレッドを生成し、ランタイムがそれらをすべてまっすぐに保ちます。それで:

  • 私は主に学習演習としてやっていますが、これは悲観的なことですか?些細な非IOタスクの場合、マルチスレッドはやり過ぎですか?私の主な目標は速度であり、UIなどの応答性ではありません。
  • IISで同じマルチスレッドコードを実行すると、スレッドプールにスレッドがすでに作成されているため、速度が向上しますが、現在、コンソールアプリを使用しています。これは、別の方法で指示するまでシングルスレッドであると想定しています。私はいくつかのテストを実行しようとしていますが、なぜそれが何らかの方法になるのかを知るために私が見逃しているいくつかの基本的な知識があると思います。私のコンソールアプリも2つのコアを備えたデスクトップで実行されていますが、Webアプリのサーバーにはそれ以上のコアがあるため、それを変数としても使用する必要があるかもしれません。
4

3 に答える 3

8

スレッドは実際にはすべて同時に実行されるわけではありません。

デスクトップマシンでは、デュアルコアCPU(おそらく最大でクアッド)を使用していると思います。これは、2/4スレッドのみを同時に実行できることを意味します。

30個のスレッドを生成した場合、OSは、それらすべてを実行し続けるために、それらの30個のスレッドをコンテキストスイッチする必要があります。コンテキストスイッチは非常にコストがかかるため、速度が低下します。

基本的な提案として、計算を最適化しようとしている場合は、CPUごとに1スレッドを目指します。これ以上のことで、実際には余分な作業をしていません。同じCPUでスレッドをスワップインしているだけです。コンピューターの内部には限られた数のワーカーがいると考えてみてください。使用可能なワーカーの数よりも多くの作業を同時に実行することはできません。

.net 4.0並列タスクライブラリの新機能のいくつかを使用すると、スレッド数のスケーラビリティを考慮した処理を実行できます。たとえば、一連のタスクを作成できます。タスク並列ライブラリは、使用可能なCPUの数を内部的に把握し、CPUが過負荷にならないように、作成/使用するスレッドの数を最適化するため、30個のタスクを作成できます。ただし、デュアルコアマシンでは、TPライブラリは2つのスレッドしか作成せず、キューに入れます。明らかに、これは、より大きなマシンで実行するときに非常にうまくスケーリングします。またはThreadPool.QueueUserWorkItem(...)、一連のタスクをキューに入れるようなものを使用できます。プールは、それらのタスクを実行するために使用されるスレッドの数を自動的に管理します。

はい、スレッド作成には多くのオーバーヘッドがありますが、.netスレッドプール(または4.0の並列タスクライブラリ)を使用している場合は、.netがスレッド作成を管理し、実際には.netよりも少ないスレッドを作成することがあります。作成したタスクの数。利用可能なスレッドでタスクを内部的に交換します。実際に実際のスレッドの明示的な作成を制御したい場合は、Threadクラスを使用する必要があります。

[一部のCPUはスレッドを巧妙に処理でき、CPUごとに複数のスレッドを実行できます(ハイパースレッディングを参照)。ただし、タスクマネージャーを確認してください。今日のデスクトップに4〜8個を超える仮想CPUがある場合は非常に驚きます]

于 2010-01-12T16:12:04.263 に答える
2

これには非常に多くの問題があるため、内部で何が起こっているのかを理解することは有益です。JoeDuffyによる「ConcurrentProgrammingonWindows」の本と、「JavaConcurrencyinPractice」の本を強くお勧めします。後者は、マルチスレッドコードを作成するときに理解する必要があるレベルのプロセッサアーキテクチャについて説明しています。コードに悪影響を与える可能性のある問題の1つは、キャッシュ、またはおそらくコードの欠如です。

すでに述べたように、スレッドのスケジューリングと実行にはオーバーヘッドがありますが、スレッド間でデータを共有すると、オーバーヘッドが大きくなる場合があります。そのデータはプロセッサキャッシュからメインメモリにフラッシュされる可能性があり、それによってコードの速度が大幅に低下します。

これは、管理された環境が私たちを保護することになっている一種の低レベルのものですが、高度に並列化されたコードを作成する場合、これはまさにあなたが対処しなければならない種類の問題です。

私の同僚は、Parallel.ForとParallel.ForEachのパフォーマンスの問題に関するスクリーンキャストを録画しました。これは次のことに役立ちます。

http://rocksolidknowledge.com/ScreenCasts.mvc/Watch?video=ParallelLoops.wmv

于 2010-01-12T16:27:15.417 に答える
1

あなたはORMについて話しているので、ある程度のI/Oが行われていると思います。この場合、スレッドの作成とコンテキストの切り替えのオーバーヘッドは比較的少なくなります。

ほとんどの場合、I / Oの競合が発生しています。同じデータセットを順番どおりに読み取らない場合は、読み取る場合よりも遅くなる可能性があります(特に、回転式ハードドライブだけでなく、他のストレージデバイスでも)。 -注文。したがって、30個のデータベースクエリを実行している場合、それらがすべて同じI / Oデバイスによってサポートされており、クエリがキャッシュにない場合は、並列よりも順番に実行される可能性があります。それらを並行して実行すると、システムに大量のI / O読み取り要求がほぼ同時に発生し、OSがそれぞれのビットを順番に読み取る可能性があります。これにより、ドライブヘッドが前後にジャンプし、貴重なミリ秒が無駄になります。

しかし、それは単なる推測です。詳細を知らずに、速度低下の原因を実際に特定することはできません。

スレッドの作成は、2つの数値を加算する場合と比較すると「非常にコストがかかる」ものですが、通常、簡単にやり過ぎてしまうことはありません。操作が非常に短い場合(たとえば、ミリ秒以下)、新しいスレッドではなくスレッドプールを使用すると、時間を大幅に節約できます。ただし、一般的に、操作がそれほど短い場合は、とにかく並列処理の粒度を再検討する必要があります。おそらく、計算をより大きなチャンクに分割する方がよいでしょう。たとえば、各アイテムを個別に処理するのではなく、一度に小さな作業アイテムのバッチ全体を処理するワーカータスクの数をかなり少なくすることによって。

于 2010-01-12T16:25:08.897 に答える