私は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コアを使用するのはなぜですか?