0

私はJavaでエグゼキュータサービスを試していますが、フィボナッチを実行するために次のコードを記述しました(はい、エグゼキュータサービスにストレスを与えるために、非常に再帰的なバージョンです)。

驚いたことに、nThreadsを1に設定すると、実行速度が速くなります。これは、エグゼキュータサービスに送信される各「タスク」のサイズが非常に小さいことに関連している可能性があります。ただし、nThreadsを1に設定した場合も、同じ数である必要があります。

共有Atomic変数へのアクセスがこの問題を引き起こす可能性があるかどうかを確認するために、「テキストを参照」というコメントを付けて3行をコメント化し、システムモニターを調べて実行にかかる時間を確認しました。しかし、結果は同じです。

なぜこれが起こっているのか考えていますか?

ところで、私はそれをFork/Joinを使用した同様の実装と比較したいと思いました。F/Jの実装よりもはるかに遅いことがわかりました。

public class MainSimpler {
    static int N=35;
    static AtomicInteger result = new AtomicInteger(0), pendingTasks = new AtomicInteger(1);
    static ExecutorService executor;

    public static void main(String[] args) {
        int nThreads=2;
        System.out.println("Number of threads = "+nThreads);
        executor = Executors.newFixedThreadPool(nThreads);
        Executable.inQueue = new AtomicInteger(nThreads);
        long before = System.currentTimeMillis();
        System.out.println("Fibonacci "+N+" is ... ");
        executor.submit(new FibSimpler(N));
        waitToFinish();
        System.out.println(result.get());
        long after = System.currentTimeMillis();        
        System.out.println("Duration: " + (after - before) + " milliseconds\n");
    }

    private static void waitToFinish() {
        while (0 < pendingTasks.get()){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        executor.shutdown();
    }
}



class FibSimpler implements Runnable {
    int N;
    FibSimpler (int n) { N=n; }

    @Override
    public void run() {
        compute();
        MainSimpler.pendingTasks.decrementAndGet(); // see text
    }

    void compute() {
        int n = N;
        if (n <= 1) {
            MainSimpler.result.addAndGet(n); // see text
            return;
        }
        MainSimpler.executor.submit(new FibSimpler(n-1));
        MainSimpler.pendingTasks.incrementAndGet(); // see text
        N = n-2;
        compute();  // similar to the F/J counterpart
    }
}

ランタイム(概算):

  • 1スレッド:11秒
  • 2スレッド:19秒
  • 4スレッド:19秒

更新:エグゼキュータサービス内で1つのスレッドを使用しても、プログラム全体でマシンの4つのコアすべてが使用されることに気付きました(各コアの平均使用率は約80%です)。これは、エグゼキュータサービス内でより多くのスレッドを使用するとプロセス全体が遅くなる理由を説明できますが、エグゼキュータサービス内でアクティブなスレッドが1つだけの場合、このプログラムが4コアを使用するのはなぜですか?

4

1 に答える 1

2

これは、executor サービスに送信される各「タスク」のサイズが非常に小さいという事実に関連している可能性があります。

これは確かに当てはまり、結果として、主にコンテキスト切り替えのオーバーヘッドを測定しています。n == 1 の場合、コンテキストの切り替えがないため、パフォーマンスが向上します。

しかし、nThreads を 1 に設定した場合でも、同じ数でなければなりません。

ここで「1 より高い」という意味だったと思います。

重いロック競合の問題に直面しています。複数のスレッドがある場合、 のロックresultは常に競合します。スレッドは、スレッドを更新する前に相互に待機する必要があり、スレッドのresult速度が低下します。スレッドが 1 つしかない場合、JVM はおそらくそれを検出してロックの省略を実行します。つまり、実際にはロックをまったく実行しません。

問題をNタスクに分割するのではなくN/nThreads、スレッドで同時に処理できるタスクに分割すると、パフォーマンスが向上する可能性があります (nThreads最大で利用可能な物理コア/スレッドの数を選択すると仮定します)。次に、各スレッドは独自の作業を行い、独自の合計を計算し、スレッドが完了したときにのみそれを総計に追加します。それでも、fib(35)スレッド管理のコストがメリットを上回ると私は予想しています。おそらく試してみてくださいfib(1000)

于 2012-11-30T11:31:33.157 に答える