私は、Javaでのレイトレーシングプログラムを書いていると、Runnableインタフェースを使用してマルチスレッド実装しました。各スレッドは、800本の縦線の部分をレンダリングします。二つのスレッドを使用している場合、彼らは400行ごとにレンダリングします。8つのスレッド、100行それぞれについて、オンので。
私のソリューションは、現在取り組んでいるが、複数のスレッドを使用した場合の時間は、それは増加をレンダリングするのにかかります。あなたがスレッド数を倍増する際の時間の量は約3倍による各線増加をレンダリングします。合計はまだ悪いです約50%の時間は増加をレンダリングするように、しかし、より多くの行がsimultanouslyレンダリング。これは、より高速にする必要があります。
私は、スレッドが唯一の正しいラインをレンダリングすることを知って、私は同じピクセルは複数回描画されませんことを確認しています。私は、レンダリングループの中に独自の変数に必ず各スレッドの書き込みを作ってみました、そして全体のループが行われた後、合計/それらを結合します。私のCPUは、8つのスレッドを持ち、さらには8つのスレッドでレンダリングするときにCPU使用率が100高くはなく%です。
class Multithread implements Runnable {
Camera camera;
CountDownLatch latch;
...
//Constructor for thread
Multithread(Scene s, Camera c, int thread, int threadcount, CountDownLatch cdl){
camera = c;
latch = cdl;
...
}
public void run(){
try{
...
//This is the render function
camera.render(...);
//When all threads unlatch, main class will write PNG
latch.countDown();
}
catch (Exception e){System.out.println ("Exception is caught");}
}
}
public class Camera {
//The final pixel values are stored in the 2D-array
ColorDbl[][] finalImage;
Camera(int w){
Width = w;
finalImage = new ColorDbl[w][w]
}
//Start rendering
void render(Scene S, int start, int end){
//Create temporary, partial image
ColorDbl[][] tempImage = new ColorDbl[Width][Width];
Ray r;
ColorDbl temp;
//Render lines of pixels in the interval start-end
for(int j = start; j < end; ++j){
for(int i = 0; i < Width; ++i){
r = new Ray(...);
temp = r.CastRay(...);
tempImage[i][j] = temp;
}
}
//Copy rendered lines to final image
for(int j=start; j<end; ++j){
for(int i=0; i<Width; ++i){
finalImage[i][j] = tempImage[i][j];
}
}
}
public static void main(String[] args) throws IOException{
//Create camera and scene
Camera camera = new Camera(800);
Scene scene = new Scene();
//Create threads
int threadcount = 4;
CountDownLatch latch = new CountDownLatch(threadcount);
for (int thread=0; thread<threadcount; thread++){
new Thread(new Multithread(scene, camera, thread, threadcount, latch)).start();
}
//Wait for threads to finish
try{
latch.await();
}catch(InterruptedException e){System.out.println ("Exception");}
//Write PNG
c.write(...);
}
}
2つのスレッドの代わりに1を使用している場合、私はレンダリング速度のほぼ倍増を期待するが、代わりにそれは長くは50%になります。私は誰もが私の問題を解決することを期待していないが、それはマルチスレッドを実装することになると私は実際にいくつかの指導をいただければ幸いです。これについて間違った道を進んアムI?
あなたが投稿ソースコードでは、私は明らかにボトルネックが表示されません。並列コードが遅く実行されている場合は、最も一般的な説明は、オーバーヘッドため、同期のどちらかである、または余分な仕事をして。
それは同期になると、高い混雑は非常にゆっくりと並列コードの実行を行うことができます。これは、スレッド(またはプロセス)を意味することができます(例えば、ロックを待っている)限られたリソース上で戦っているが、それはまた、非常に高価になることができアトミック操作を使用して、同じメモリにアクセスするような、より微妙なことができます。あなたの例では、私はそのような何も表示されませんでした。唯一の同期操作は重要ではありません最後のカウントダウンラッチ、ように見えます。不平等なワークロードにも拡張性に害を与えることができますが、それはあなたの例では考えにくいです。
余分な作業を行うことは問題である可能性があります。たぶん、あなたは、順次1よりも並列バージョンでは、より多くのデータをコピーしていますか?それは、いくつかのオーバーヘッドを説明できます。もう一つの推測では、並列バージョンでは、キャッシュの局所性が悪影響を受けてきたということです。キャッシュの効果は(経験則として、メモリアクセスがキャッシュ内に遅い50〜100倍のワークロードは、もはやフィットの要因になることができます)重要であることに注意してください。
どのようにあなたのボトルネックを見つけるには?一般的には、それがプロファイリングと呼ばれています。専用のツールがありますが、例えば、VisualVMのは、プロファイラとして使用することができますJavaのための無料ツールです。別のより簡単な、しかし多くの場合、非常に効果的な最初のアプローチは、あなたのプログラムを実行し、いくつかのランダムなスレッドダンプを取ることです。あなたは明らかにボトルネックがある場合は、それはあなたがスタックトレースでそれを見るだろうと思われます。
この技術は、多くの場合、貧乏人のプロファイラと呼ばれているが、私はそれが非常に効果的(参照見つかっこの回答の詳細については)。また、あなたはまた、生産に安全にそれを適用することができますので、あなたは、最適化コードにある場合、それはあなたがあなたのローカルマシン上で実行することができないという巧妙なトリックです。
IDEの(EclipseやIntelliJのような)あなたはプロセスIDを知っていればスレッドダンプを取るためのサポートを持っていますが、コマンドラインから直接それをトリガすることができます。
kill -3 JAVA_PID
プログラム(またはそれを実行するJVM)は、すべての現在のスレッドの現在のスタックトレースを出力します。あなたは時代のカップルを繰り返した場合、あなたはあなたのプログラムは、そのほとんどの時間を費やしているのアイデアを得る必要があります。
また、あなたの順次バージョンと比較することができます。たぶん、あなたは、パラレルバージョンのオーバーヘッドを説明し、いくつかのパターンに気づきます。
私は少し始めるために助けたことを願っています。