前にやった計り方だとあんまりちゃんと計れていないのでマジメに実装してみた.
ついでにAndroidでも計ってみた.
先に結論
synchronizedに比べてAtomicIntegerを使った方がJavaは2倍,Androidは7倍速い.
Javaの場合
public class Main {
public static void main(String[] args) throws Throwable {
int num = 100;
double[] syncList = new double[num];
double[] atomicList = new double[num];
for (int i = 0; i < num; i++) {
IntTest it = new IntTest();
double sync = test(()->it.testSync());
double atomic = test(()->it.testAtomic());
syncList[i] = sync;
atomicList[i] = atomic;
System.out.println(i+" "+sync+" "+atomic);
}
Arrays.sort(syncList);
Arrays.sort(atomicList);
System.out.println("Sync : "+syncList[num/2]);
System.out.println("Atomic : "+atomicList[num/2]);
}
private static final double test(Runnable task) throws Throwable{
int ThreadNum = 10;
int TaskNum = 10000;
ExecutorService service = Executors.newFixedThreadPool(ThreadNum);
AtomicLong time = new AtomicLong(0);
Runnable run = () -> {
long start, end;
for (int i = 0; i < TaskNum; i++) {
start = System.nanoTime();
task.run();
end = System.nanoTime() - start;
time.addAndGet(end);
}
};
for (int i = 0; i < ThreadNum; i++) {
service.submit(run);
}
service.shutdown();
service.awaitTermination(10, TimeUnit.SECONDS);
return ((double) time.get() / TaskNum);
}
}
public class IntTest {
private static final Random rand = new Random();
private int syncInt;
private final AtomicInteger atomicInt;
public IntTest(){
syncInt = 0;
atomicInt = new AtomicInteger(0);
}
synchronized public void testSync(){
syncInt += rand.nextInt();
}
public void testAtomic(){
atomicInt.addAndGet(rand.nextInt());
}
}
synchronizedとAtomicIntegerの1回の処理をSystem.nanoTime()
で計測,だいたい1万回ぐらいやった平均値を100回集計して,中央値で評価.
なぜ中央値か,というのは最初の方の最適化がかかってない状態が異様に遅かったりGCの影響なのか時々大きな外れ値が出るので平均ではなく中央値にしている.
で,実行してみると.
Sync : 15320.4771
Atomic : 7514.3813
てことでAtomicの方が2倍速い.
Androidの場合
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int num = 100;
double[] syncList = new double[num];
double[] atomicList = new double[num];
try {
for (int i = 0; i < num; i++) {
final IntTest it = new IntTest();
double sync = test(new Runnable() {
@Override
public void run() {
it.testSync();
}
});
double atomic = test(new Runnable() {
@Override
public void run() {
it.testAtomic();
}
});
syncList[i] = sync;
atomicList[i] = atomic;
System.out.println(i + " " + sync + " " + atomic);
}
Arrays.sort(syncList);
Arrays.sort(atomicList);
System.out.println("Sync : " + syncList[num / 2]);
System.out.println("Atomic : " + atomicList[num / 2]);
} catch(Throwable t){
t.printStackTrace();
}
}
private static final double test(final Runnable task) throws Throwable{
final int ThreadNum = 10;
final int TaskNum = 10000;
ExecutorService service = Executors.newFixedThreadPool(ThreadNum);
final AtomicLong time = new AtomicLong(0);
Runnable run = new Runnable() {
@Override
public void run() {
long start, end;
for (int i = 0; i < TaskNum; i++) {
start = System.nanoTime();
task.run();
end = System.nanoTime() - start;
time.addAndGet(end);
}
}
};
for (int i = 0; i < ThreadNum; i++) {
service.submit(run);
}
service.shutdown();
service.awaitTermination(10, TimeUnit.SECONDS);
return ((double) time.get() / TaskNum);
}
}
基本はJavaと同じですがlambda使ってるとことかfinal修飾子とかが違うところ.
IntTest.javaは同じです.
で,結果は
Sync : 147324.9675
Atomic : 22768.7228
てことでAtomicの圧勝.7倍近く速くなってますね.
ちなみに端末はSO-02FでAndroid 4.4.4です.
補足
Atomic系の方法は頻繁に衝突が起きるようだと性能が劣化するはずなんですが,かなり頻繁に更新をかけてもsynchronizedより速いです.というか負けることはほぼ無いです.
もちろん,使用用途によってはsynchronizedをかけた方がいいです.二つの値を編集する場合とか.まぁ,それもAtomicReference使う方が速いかもしれないけど.
次回予告
AtomicFloatとAtomicDoubleを作る.