LoginSignup
4
3

More than 5 years have passed since last update.

Stringのequalsメソッドの速度を調べてみた

Last updated at Posted at 2016-10-11

これは何?

Java で文字列の一致確認に使う equals() って実際どういう処理をしてるの?って話 のコメントで @sipadan2003 さんが

CPUに、0と比較する専用の命令があり、0と比較するほうが性能が優れています。
CPUに、0と比較する専用の命令があり、0と比較するほうが性能が優れています。
たとえば、 while(i < 100) よりも while(i > 0) のほうが高速なのです。たとえば、 while(i < 100) よりも while(i > 0) のほうが高速なのです。

と書かれていたので、実際どのくらいの違いがあるのか調べてみた。

ソース

package obj;

import java.util.Arrays;

/**
 * String.javaのequalsメソッドを抜き出したコード
 */
public class TestString {

    public TestString() {
        this.value = new char[0];
    }

    public TestString(char value[]) {
        this.value = Arrays.copyOf(value, value.length);
    }

    private final char value[];


    public boolean equalsWhile(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof TestString) {
            TestString anotherString = (TestString) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

    public boolean equalsWhile2(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof TestString) {
            TestString anotherString = (TestString) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                while (n-- != 0) {
                    if (v1[n] != v2[n])
                            return false;
                }
                return true;
            }
        }
        return false;
    }

    public boolean equalsFor(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof TestString) {
            TestString anotherString = (TestString) anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                for (int i = 0; i < n; i++) {
                    if (v1[i] != v2[i])
                        return false;
                }
                return true;
            }
        }
        return false;
    }
}
package jmh18;

import java.util.concurrent.TimeUnit;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import obj.TestString;

/**
 * パフォーマンス測定用コード
 */
public class TestEquals {

    public static void main(String[] args) throws RunnerException {
        System.out.println(value1.equalsFor(value2));
        System.out.println(value1.equalsWhile(value2));

        Options opt = new OptionsBuilder().include(TestEquals.class.getSimpleName()).warmupIterations(10)
                .measurementIterations(5).forks(2).build();

        new Runner(opt).run();

    }

    private static TestString value1 = new TestString(new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'});
    private static TestString value2 = new TestString(new char[]{'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'});


    @Benchmark
    public void testFor() {

        for(int i = 0; i < 10000; i++) {
            boolean b = value1.equalsFor(value2);
        }

    }

    @Benchmark
    public void testWhile() {

        for(int i = 0; i < 10000; i++) {
            boolean b = value1.equalsWhile(value2);
        }

    }

    @Benchmark
    public void testWhile2() {

        for(int i = 0; i < 10000; i++) {
            boolean b = value1.equalsWhile2(value2);
        }

    }



}

処理結果

※コメントにて指摘を戴いたのでアノテーションを外して再テストし、処理結果を修正しました。

Benchmark              Mode  Cnt     Score     Error  Units
TestEquals.testFor    thrpt   10  7638.289 ± 357.359  ops/s
TestEquals.testWhile  thrpt   10  6489.518 ±  77.084  ops/s

whileメソッドの記述についてのツッコミを受けたので修正版でのテスト結果です。
※while2のテスト結果とForのテスト結果はテストの度に速さが変わるのでほぼ同一なのではと。

Benchmark               Mode  Cnt     Score     Error  Units
TestEquals.testFor     thrpt   10  7692.061 ± 333.863  ops/s
TestEquals.testWhile   thrpt   10  6551.353 ±  29.420  ops/s
TestEquals.testWhile2  thrpt   10  7545.835 ± 204.459  ops/s

(処理結果全文はリンクを参照して下さい。)

まとめ

全然違うもんなんですね。
あれ?For文の方が速い…?
勘違いしている可能性があるのでコードの問題点を突っ込んでください。
普通にコーディングしている時は意識しなくてもよさそうだけど、速度をとことん求める時は知っておいた方がよさそう。

追記

どうも本体のStringの記述が冗長?

4
3
18

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
4
3