質問

.NET 4.0のパラレルライブラリをいじっていました。最近、私たちの大規模なシステムの1つを使用しなければならないいくつかの珍しい読み取り/書き込み操作のためのカスタムORMを開発しました。これにより、属性を使用してオブジェクトを飾り、データベースから引く必要がある列と、書き込みに出力するXMLを反映させることができます。

私はこのラッパーが多くのプロジェクトで再利用されることを想像しているので、私はできるだけ多くの速度を絞り出したいと思います。このライブラリは、主に.NET Webアプリケーションで使用されます。私が作成したクラスを突くために、スローアウェイコンソールアプリケーションを使用してフレームワークをテストしています。

私は今、マルチスレッドに付属するオーバーヘッドの教訓を学びました。マルチスレッドにより、速度が遅くなります。読んでから、長い間それをやってきた人にとっては直感的なようですが、実際には私にとっては直感に反しています。方法を30回実行する方法 同時に 順次30回実行するよりも遅くなりますか?

私は同じ共有オブジェクトで戦う必要がある複数のスレッドによって問題を引き起こしているとは思わない(私はまだそれを確実に伝えるかどうかはまだ十分ではないが)。これらすべてのスレッドを産み、ランタイムがそれらをすべてまっすぐに保つこと。そう:

  • 私は主に学習演習としてそれをやっていますが、これは悲観的ですか?些細なタスクでは、マルチスレッドはやり過ぎですか?私の主な目標は、UIなどの応答性ではなく、スピードです。
  • IISで同じマルチスレッドコードを実行すると、スレッドプールですでに作成されたスレッドのためにスピードアップされますが、現在はコンソールアプリを使用しています。私はいくつかのテストを実行しようとしていますが、私が知っていることに欠けているいくつかの基本的な知識があると思います どうして それは何らかの方法です。私のコンソールアプリも2つのコアを使用してデスクトップで実行されていますが、Webアプリのサーバーにはさらに多くのコアがあるため、変数としても使用する必要があります。
役に立ちましたか?

解決

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

デスクトップマシンでは、デュアルコアCPU(せいぜいクワッドかもしれません)があると思います。これは、2/4スレッドのみが同時に実行できることを意味します。

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

基本的な提案として、計算を最適化しようとしている場合は、CPUごとに1つのスレッドを目指します。これ以上のことで、あなたは実際に余分な作業をしているわけではありません。あなたは同じCPUでアウトにスレッドを交換するだけです。あなたのコンピューターが中に限られた数の労働者を持っていると考えてみてください、あなたはあなたが利用できる労働者の数よりも同時に仕事をすることはできません。

.NET 4.0 Parallel Task Libraryのいくつかの新機能を使用すると、スレッドの数のスケーラビリティを説明することを行うことができます。たとえば、タスクの束を作成することができ、タスクパラレルライブラリは利用可能なCPUの数を内部的に把握し、CPUを過負荷にしないようにスレッドの数を作成/使用するため、30のタスクを作成できます。ただし、デュアルコアマシンでは、TPライブラリは2つのスレッドのみを作成し、キューにします。明らかに、これは、より大きなマシンで実行できると、非常にうまくスケーリングされます。または、ようなものを使用することもできます ThreadPool.QueueUserWorkItem(...) たくさんのタスクを列に並べると、プールはこれらのタスクを実行するために使用するスレッドの数を自動的に管理します。

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

一部のCPUはスレッドで巧妙なことをすることができ、CPUごとに複数のスレッドを実行できます - 参照 ハイパースレッディング - しかし、あなたのタスクマネージャーをチェックしてください、あなたが今日のデスクトップに4-8以上の仮想CPUを持っているならば、私は非常に驚くでしょう

他のヒント

これには非常に多くの問題があるので、カバーの下で何が起こっているのかを理解することは報われます。 Joe Duffyによる「Windows On Windowsの同時プログラミング」ブックと「Java Concurrency in Practice」の本を強くお勧めします。後者は、マルチスレッドコードを書くときに理解する必要があるレベルでのプロセッサアーキテクチャについて説明しています。あなたがヒットするつもりである1つの問題は、あなたのコードを傷つけること、またはおそらくそれがない可能性が高いことです。

述べられているように、スレッドのスケジュールと実行のオーバーヘッドがありますが、スレッド間でデータを共有すると、より大きなオーバーヘッドがあることがわかります。そのデータは、プロセッサのキャッシュからメインメモリに流される可能性があり、これにより、コードに深刻なスローダウンが発生します。

これは、管理された環境が私たちを保護することになっている一種の低レベルのものですが、非常に並行コードを書くとき、これはまさにあなたが対処しなければならない一種の問題です。

私の同僚は、並行してパフォーマンスの問題についてスクリーンキャストを記録しました。

http://rocksolidknowledge.com/screencasts.mvc/watch?video=parallelloops.wmv

あなたはORMについて話しているので、私はある程度のI/Oが進行していると思います。この場合、スレッドの作成とコンテキストスイッチングのオーバーヘッドは比較的存在しません。

ほとんどの場合、I/Oの競合が発生している可能性があります。(特に回転ハードドライブではなく、他のストレージデバイスでも)、同じデータセットを読み取ると、順番に読み取ると同じデータを読み取ることができます。 -注文。したがって、30のデータベースクエリを実行している場合、それらがすべて同じI/Oデバイスに裏打ちされており、クエリがキャッシュにない場合、それらは並行して順番に順番に実行される可能性があります。それらを並行して実行すると、システムがほぼ同時にI/Oの読み取りリクエストを持っている可能性があります。これにより、OSがそれぞれの小さなビットを順番に読み取る可能性があります。

しかし、それは単なる推測です。これ以上知らずに、何があなたの減速を引き起こしているのかを実際に判断することは不可能です。

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top