11

Intelスレッドビルディングブロックに感銘を受けました。私はスレッドコードではなくタスクを書く方法が好きで、私の限られた理解でそれが内部でどのように機能するかが好きです(タスクはプールにあり、4コアには100スレッドはありません、タスクはオンになっていないため実行が保証されていません独自のスレッドであり、プールの奥深くにある可能性がありますが、別の関連タスクで実行される可能性があるため、通常のスレッドセーフでないコードなどの悪いことを行うことはできません。

ライティングタスクについてもっと知りたいと思いました。私は「タスクベースのマルチスレッド-100コアをプログラムする方法」のビデオが好きですhttp://www.gdcvault.com/sponsor.php?sponsor_id=1(現在最後から2番目のリンク。警告は「素晴らしい」ではありません)。私のお気に入りの部分は、「迷路の解決は並行して行う方がよい」というものでした。これは、48分マーク付近です(左側のリンクをクリックできます。この部分は、実際に見る必要があるすべてです)。

ただし、タスクの記述方法に関するコード例とAPIをもっと見たいと思います。誰かが良いリソースを持っていますか?クラスまたはコードの一部がプールにプッシュされた後にどのように見えるか、またはすべてのコピーを作成する必要があるときに奇妙なコードがどのように見えるか、すべてのどれだけがプールにプッシュされるかはわかりません。

4

2 に答える 2

7

Javaには、スレッドビルディングブロックに似た並列タスクフレームワークがあります。これは、Fork-Joinフレームワークと呼ばれます。現在のJavaSE6で使用でき、今後のJavaSE7に含まれる予定です。

javadocクラスのドキュメントに加えて、フレームワークの使用を開始するために利用できるリソースがあります。jsr166ページから、「これらのクラスの追加のドキュメント、メモ、アドバイス、例などを含むwikiもあります」と記載されています。

行列の乗算などのフォーク結合の例は、開始するのに適した場所です。

インテルの2009年のスレッド化の課題のいくつかを解決するために、フォーク結合フレームワークを使用しました。フレームワークは軽量でオーバーヘッドが少ないです。私のものはKight'sTour問題の唯一のJavaエントリであり、競合他社の他のエントリを上回りました。Javaのソースと記述は、チャレンジサイトからダウンロードできます。

編集:

クラスまたはコードの一部がプールにプッシュされた後、どのように見えるかわかりません[...]

RecursiveTaskなどのForKJoinTaskサブクラスの1つをサブクラス化することにより、独自のタスクを作成できます。フィボナッチ数列を並列に計算する方法は次のとおりです。(javadocsから取得-コメントは私のものです。)RecursiveTask

 // declare a new task, that itself spawns subtasks. 
 // The task returns an Integer result.
 class Fibonacci extends RecursiveTask<Integer> {
   final int n;      // the n'th number in the fibonacci sequence to compute
   Fibonnaci(int n) { this.n = n; } // constructor
   Integer compute() {   // this method is the main work of the task
     if (n <= 1)         // 1 or 0, base case to end recursion
        return n;
     Fibonacci f1 = new Fibonacci(n - 1);  // create a new task to compute n-1
     f1.fork();                            // schedule to run asynchronously
     Fibonacci f2 = new Fibonacci(n - 2);  // create a new task to compute n-2
     return f2.invoke() + f1.join();       // wait for both tasks to compute.
       // f2 is run as part of this task, f1 runs asynchronously. 
       // (you could create two separate tasks and wait for them both, but running
       // f2 as part of this task is a little more efficient.
   }
 }

次に、このタスクを実行して結果を取得します

// default parallelism is number of cores
ForkJoinPool pool = new ForkJoinPool(); 
Fibonacci f = new Fibonacci(100);
int result = pool.invoke(f);

これは、物事を単純にするための簡単な例です。実際には、タスクによって実行される作業はタスクフレームワークのオーバーヘッドと比較して取るに足らないものであるため、パフォーマンスはそれほど良くありません。経験則として、タスクはいくつかの重要な計算を実行する必要があります-フレームワークのオーバーヘッドを重要ではないものにするのに十分ですが、1つの大きなタスクを実行する問題の最後に1つのコアになってしまうほどではありません。大きなタスクを小さなタスクに分割することで、他のコアがアイドル状態のときに1つのコアが多くの作業を残さないようにします。小さなタスクを使用すると、より多くのコアがビジー状態になりますが、タスクが実際に機能しないほど小さくはなりません。

[...]または、すべてのコピーを作成する必要があるときにコードがどのように奇妙に見えるか、およびすべてのどれだけがプールにプッシュされるか。

タスク自体のみがプールにプッシュされます。理想的には、何もコピーしたくないです。プログラムの速度を低下させる干渉やロックの必要性を回避するために、タスクは理想的には独立したデータで動作する必要があります。読み取り専用データはすべてのタスク間で共有でき、コピーする必要はありません。スレッドが協力して大規模なデータ構造を構築する必要がある場合は、それらを個別に構築し、最後にそれらを組み合わせるのが最善です。結合は個別のタスクとして実行することも、各タスクでパズルのピースをソリューション全体に追加することもできます。多くの場合、これには何らかの形式のロックが必要ですが、タスクの作業がソリューションの更新作業よりもはるかに大きい場合は、パフォーマンスの大きな問題にはなりません。私の騎士」

タスクと並行性の操作は、通常のシングルスレッドプログラミングからのパラダイムシフトです。多くの場合、特定の問題を解決するために可能な設計はいくつかありますが、スレッド化されたソリューションに適しているのはそのうちのいくつかだけです。マルチスレッドの方法でおなじみの問題を再キャストする方法の感触をつかむには、数回の試行が必要になる場合があります。学ぶための最良の方法は、例を見て、それを自分で試すことです。常にプロファイルを作成し、スレッド数を変更した場合の影響を測定します。プールコンストラクターのプールで使用するスレッド(コア)の数を明示的に設定できます。タスクが線形に分割されると、スレッドの数が増えるにつれてほぼ線形のスピードアップが期待できます。

于 2010-05-29T15:11:51.683 に答える
0

解決不可能な問題を解決していると主張する「フレームワーク」(最適なタスクスケジューリングはNP困難)で遊ぶことは、まったく役に立ちません。本を読んだり、並行アルゴリズムに関する記事を読んだりするよりも役立ちます。いわゆる「タスク」は、問題の分離可能性(互いに独立して計算できる部分)を定義するための空想的な名前にすぎません。分離可能な問題のクラスは非常に小さく、それらはすでに古い本でカバーされています。

分離できない問題については、データを交換するためにフェーズとフェーズ間のデータバリアを計画する必要があります。同時データ交換のためのデータバリアの最適なオーケストレーションは、NP困難であるだけでなく、原則として一般的な方法で解決することは不可能です-すべての可能なインターリーブの履歴を調べる必要があります-これは、すでに指数関数的なセットのべき集合のようなものです(数学ではNからR)。私が言及する理由は、ソフトウェアがあなたのためにこれを行うことは決してできないこと、そしてそれを行う方法は本質的に実際のアルゴリズムと並列化が可能かどうかの成否に依存することを明確にするためです(理論的には可能であっても)。

高度な並列処理に入ると、キューを維持することすらできなくなり、メモリバスもなくなります。100個のCPUが単一の共有整数で同期しようとしたり、メモリバスの裁定取引を行ったりすることを想像してみてください。実行するすべてのものを事前に計画および事前構成し、ホワイトボード上で基本的に正しいことを証明する必要があります。Intelのスレッディングビルディングブロックは、その世界では小さな子供です。これらは、メモリバスを共有できる少数のコア用です。分離可能な問題を実行することは、「フレームワーク」なしで実行できる簡単なことです。

したがって、できるだけ多くの異なる並列アルゴリズムを読み取る必要があります。1つの問題に対してほぼ最適なデータバリアレイアウトを調査するには、通常1〜3年かかります。最初のネイバーだけが効率的にデータを交換できるため(1つのデータバリアサイクル中)、シングルチップ上で16以上のコアを使用する場合はレイアウトになります。したがって、実際には、Intelの売り込みやJavaのおもちゃよりも、IBMの実験的な30コアCPUを使用したCUDAと論文および結果を調べることで、はるかに多くのことを学ぶことができます。

無駄になるリソースのサイズ(コアとメモリの数)が、それらが達成するスピードアップよりもはるかに大きいデモの問題に注意してください。2倍速く何かを解決するために4コアと4倍のRAMが必要な場合、ソリューションは並列化のためにスケーラブルではありません。

于 2010-08-27T12:22:07.030 に答える