AndroidとJavaでAtomicIntegerの性能をちゃんと計る

[前にやった計り方](http://qiita.com/shinido/items/b8e69091da5ddb4bd9ad)だとあんまりちゃんと計れていないのでマジメに実装してみた．
ついでにAndroidでも計ってみた．

# 先に結論
synchronizedに比べてAtomicIntegerを使った方が**Javaは2倍，Androidは7倍速い**．

# Javaの場合

```java:Main.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);
    }
}
```

```java:IntTest.java
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の影響なのか時々大きな外れ値が出るので平均ではなく中央値にしている．
で，実行してみると．

```zsh
Sync   : 15320.4771
Atomic : 7514.3813
```

てことでAtomicの方が2倍速い．

# Androidの場合

```java:MainActivity.java
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は同じです．

で，結果は

```zsh
Sync   : 147324.9675
Atomic : 22768.7228
```

てことでAtomicの圧勝．7倍近く速くなってますね．
ちなみに端末はSO-02FでAndroid 4.4.4です．

# 補足
Atomic系の方法は頻繁に衝突が起きるようだと性能が劣化するはずなんですが，かなり頻繁に更新をかけてもsynchronizedより速いです．というか負けることはほぼ無いです．
もちろん，使用用途によってはsynchronizedをかけた方がいいです．二つの値を編集する場合とか．まぁ，それもAtomicReference使う方が速いかもしれないけど．

# 次回予告
AtomicFloatとAtomicDoubleを作る．