Basit bir hesaplama yapmaya çalışıyorum (Math.random()
10000000 kez çağırır). Şaşırtıcı bir şekilde basit bir yöntemle çalıştırmak, ExecutorService'i kullanmanın çok daha hızlı bir şekilde gerçekleştirilmesini sağlar.ExecutorService yavaş çoklu iş parçacığı performansı
Ben ExecutorService's surprising performance break-even point --- rules of thumb? başka konuyu okumuş ve Callable
kullanarak toplu yürüterek cevap takip etmeye çalıştı, ancak performans benim şimdiki koduna göre hala
nasıl performansını artırmak do kötü mü?
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;
}
}
}
Bu kod
Average simple: 920 ms
Average single thread: 1034 ms
Average multi thread: 1393 ms
düzenleme için çıkış: Her bir iplik için yeni rasgele nesne ile Math.random() değiştirdikten sonra performans nedeniyle Math.random (yaşamaya) olan senkronize bir yöntem .. performans (her iplik için rasgele ile Math.random() yerleştirildikten sonra)
yeni kodu için çıkış
geliştirilmişAverage simple: 928 ms
Average single thread: 1046 ms
Average multi thread: 642 ms
Ah Haklısınız! Math.random() 'ın senkronize olduğunu farketmedim. Her bir İşçi için yeni bir Rastgele nesne koyduğumda, performans büyük ölçüde iyileşti – GantengX
Sadece hızlı bir soru, eğer Rastgele nesneyi paylaşmaya çalışırsam, performans hala devam ediyor. Bunun neden böyle olduğunu biliyor musun? Random.nextDouble senkronize edilmez ve AtomicLong.compareAndSet olarak adlandırılan Random.next (int) işlevini çağırır.Bunun neden performansın – GantengX
performansını etkileyeceğini anlamıyorum, çünkü aynı kaynağa tekrar birden çok ileti dizisine sahip olmanız gerekiyor: AtomicLong bu durumda. Tek bir iş parçacığı değerini her seferinde güncelleyebilir ve her çağrı için nextDouble() öğesine iki kez güncellenir. –