2016-12-11 2 views
5

アルゴリズムの高速化のための並列プログラミングを学び、Javaを選択したかったのです。
配列にlongの整数を加算する2つの関数を書いておきます。単純に1つは配列を繰り返し、2つ目は配列を分割し、分割されたスレッドの部分を合計します。Javaの複数のスレッドは、パフォーマンスのゲインが非常に小さい

私は2つのスレッドを使って論理的に約2倍のスピードアップを期待していました。しかし、私が得たものはわずか24%のスピードです。さらに、より多くのスレッドを使用して、私は2つのスレッドに対して何らかの改善(おそらく1%未満)を得ていません。スレッド作成/オーバーヘッドのオーバーヘッドがあるはずですが、それほど大きくないはずです。

私は行方不明またはコードのどこに誤りがあるのか​​説明してください。

import java.util.concurrent.ThreadLocalRandom; 


public class ParallelTest { 


public static long sum1 (long[] num, int a, int b) { 
    long r = 0; 
    while (a < b) { 
     r += num[a]; 
     ++a; 
    } 
    return r; 
} 

public static class SumThread extends Thread { 
    private long num[]; 
    private long r; 
    private int a, b; 

    public SumThread (long[] num, int a, int b) { 
     super(); 
     this.num = num; 
     this.a = a; 
     this.b = b; 
    } 

    @Override 
    public void run() { 
     r = ParallelTest.sum1(num, a, b); 
    } 

    public long getSum() { 
     return r; 
    } 
} 


public static long sum2 (long[] num, int a, int b, int threadCnt) throws InterruptedException { 
    SumThread[] th = new SumThread[threadCnt]; 
    int i = 0, c = (b - a + threadCnt - 1)/threadCnt; 

    for (;;) { 
     int a2 = a + c; 
     if (a2 > b) { 
      a2 = b; 
     } 
     th[i] = new SumThread(num, a, a2); 
     th[i].start(); 
     if (a2 == b) { 
      break; 
     } 
     a = a2; 
     ++i; 
    } 

    for (i = 0; i < threadCnt; ++i) { 
     th[i].join(); 
    } 
    long r = 0; 
    for (i = 0; i < threadCnt; ++i) { 
     r += th[i].getSum(); 
    } 
    return r; 
} 

public static void main(String[] args) throws InterruptedException { 
    final int N = 230000000; 
    long[] num = new long[N]; 

    for (int i = 0; i < N; ++i) { 
     num[i] = ThreadLocalRandom.current().nextLong(1, 9999); 
    } 

    // System.out.println(Runtime.getRuntime().availableProcessors()); 

    long timestamp = System.nanoTime(); 
    System.out.println(sum1(num, 0, num.length)); 
    System.out.println(System.nanoTime() - timestamp); 

    for (int n = 2; n <= 4; ++n) { 
     timestamp = System.nanoTime(); 
     System.out.println(sum2(num, 0, num.length, n)); 
     System.out.println(System.nanoTime() - timestamp); 
    } 


} 
} 

EDIT:私は4つのコア(8つのスレッド)とi7のプロセッサを持っている。ここで コードです。コードによって与えられた 出力は、次のとおりです。私はあなたが期待しているとあなたは同じくらいスピードアップを取得しない理由数の理由を考えることができ

1149914787860 
175689196 
1149914787860 
149224086 
1149914787860 
147709988 
1149914787860 
138243999 

答えて

3

プログラムは、おそらくちょうど2つのスレッドを制限され、メインメモリの帯域幅です。

+0

つまり、CPU集約型のタスクをループで実行すると、より多くのスレッドでパフォーマンスが向上します。 – Somnium

+0

@Somnium - 正しい。 – rcgldr

3

  1. スレッド作成のオーバーヘッドは相当です。スレッドstart()は、スレッドスタックとその "red-zone"を割り当ててネイティブスレッドを作成するために複数のシステムコールを必要とする高価な操作です。

  2. Nスレッドがすべて同時に開始するわけではありません。これは、計算の並列部分を完了するための時間は、最初のスレッドの開始時間である最後のスレッドの終了時間にほぼ等しいことを意味します。それは、あるスレッドが作業の一部を行う時間よりも長くなります。 (スレッド作成時間のN-1倍)...

  3. Nスレッドは(基本的に)配列のN個のディスジョイントされたセクションのシリアルスキャンを実行します。これはメモリ帯域幅が集中しているため、スキャンする方法はメモリキャッシュが無効になることを意味します。したがって、パフォーマンスはシステムのメインメモリハードウェアの速度と帯域幅によって制限される可能性があります。それはほぼ同じ速度ラムは、プロセッサにデータを供給できるように、データをフェッチ小さなループ、だとして

関連する問題