LoginSignup
12
14

More than 5 years have passed since last update.

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

Posted at

前にやった計り方だとあんまりちゃんと計れていないのでマジメに実装してみた.
ついでにAndroidでも計ってみた.

先に結論

synchronizedに比べてAtomicIntegerを使った方がJavaは2倍,Androidは7倍速い

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);
    }
}
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の影響なのか時々大きな外れ値が出るので平均ではなく中央値にしている.
で,実行してみると.

Sync   : 15320.4771
Atomic : 7514.3813

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

Androidの場合

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は同じです.

で,結果は

Sync   : 147324.9675
Atomic : 22768.7228

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

補足

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

次回予告

AtomicFloatとAtomicDoubleを作る.

12
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
14