私は単純な計算を実行しようとしています(Math.random()
10000000回を呼び出しています)。意外なことに、単純なメソッドで実行すると、ExecutorServiceを使用するよりもはるかに高速に実行されます。ExecutorServiceスローマルチスレッドパフォーマンス
私はどのように私は私の現在のコードに基づいてパフォーマンスを向上させるん
をExecutorService's surprising performance break-even point --- rules of thumb?で別のスレッドを読んで、バッチを使用してCallable
を実行することで答えを追跡しようとしましたが、パフォーマンスはまだ悪いですか?
import java.util.*;
import java.util.concurrent.*;
public class MainTest {
public static void main(String[]args) throws Exception {
new MainTest().start();;
}
final List<Worker> workermulti = new ArrayList<Worker>();
final List<Worker> workersingle = new ArrayList<Worker>();
final int count=10000000;
public void start() throws Exception {
int n=2;
workersingle.add(new Worker(1));
for (int i=0;i<n;i++) {
// worker will only do count/n job
workermulti.add(new Worker(n));
}
ExecutorService serviceSingle = Executors.newSingleThreadExecutor();
ExecutorService serviceMulti = Executors.newFixedThreadPool(n);
long s,e;
int tests=10;
List<Long> simple = new ArrayList<Long>();
List<Long> single = new ArrayList<Long>();
List<Long> multi = new ArrayList<Long>();
for (int i=0;i<tests;i++) {
// simple
s = System.currentTimeMillis();
simple();
e = System.currentTimeMillis();
simple.add(e-s);
// single thread
s = System.currentTimeMillis();
serviceSingle.invokeAll(workersingle); // single thread
e = System.currentTimeMillis();
single.add(e-s);
// multi thread
s = System.currentTimeMillis();
serviceMulti.invokeAll(workermulti);
e = System.currentTimeMillis();
multi.add(e-s);
}
long avgSimple=sum(simple)/tests;
long avgSingle=sum(single)/tests;
long avgMulti=sum(multi)/tests;
System.out.println("Average simple: "+avgSimple+" ms");
System.out.println("Average single thread: "+avgSingle+" ms");
System.out.println("Average multi thread: "+avgMulti+" ms");
serviceSingle.shutdown();
serviceMulti.shutdown();
}
long sum(List<Long> list) {
long sum=0;
for (long l : list) {
sum+=l;
}
return sum;
}
private void simple() {
for (int i=0;i<count;i++){
Math.random();
}
}
class Worker implements Callable<Void> {
int n;
public Worker(int n) {
this.n=n;
}
@Override
public Void call() throws Exception {
// divide count with n to perform batch execution
for (int i=0;i<(count/n);i++) {
Math.random();
}
return null;
}
}
}
このコード
Average simple: 920 ms
Average single thread: 1034 ms
Average multi thread: 1393 ms
EDIT用出力:各スレッドのための新しいランダムオブジェクトにMath.random()を変更した後の性能が原因Math.random(に苦しむ)である同期方法.. 、パフォーマンスが(スレッドごとにランダムでMath.random()を交換した後)
新しいコードの出力
を向上Average simple: 928 ms
Average single thread: 1046 ms
Average multi thread: 642 ms
あなたは正しいです!私はMath.random()が同期されていることを認識しませんでした。一度、各Workerに新しいRandomオブジェクトを配置すると、パフォーマンスは大幅に向上しました – GantengX
Randomオブジェクトを共有しようとすると、パフォーマンスはまだ損なわれます。なぜこれがそうであるか知っていますか? Random.nextDoubleは同期されておらず、AtomicLong.compareAndSetを呼び出すRandom.next(int)を呼び出します。私はなぜこれがパフォーマンスに影響するのかわかりません – GantengX
同じリソースに対して複数のスレッドが再び競合していることに戻ります。この場合はAtomicLongです。一度に1つのスレッドだけがその値を更新でき、nextDouble()の呼び出しごとに2回更新されます。 –