Blog

Re: Java 7 時代の String#split() 事情

http://blog.k11i.biz/2013/05/java-7-stringsplit.html http://d.hatena.ne.jp/chiheisen/20110801/1312119289

Java 7 以後では String#split(String) で、引数が1文字の場合は最適化が効いて、高速になるという話。 社内でなんか話題になってたので最近の JVM だとどうなのかなーと調べてみた(元エントリは5年前のもの)。 (Pattern#split にもいずれ同様の最適化が入る可能性はあるので、このベンチマークの傾向は今でも一緒なのかなーと気になったため)

https://travis-ci.org/tokuhirom/java-string-splitting-benchmark

元のコードは手でベンチマークコードを実装されたものだったが、最近では JMH で簡単に書けるので、JMH で簡単に書いてみた。 以下は travis-ci で openjdk11 を走らせた結果。相変わらず pattern 1 文字の場合の最適化は有効になっていることはわかる。

Benchmark                                           (raw)   Mode  Cnt        Score         Error  Units
StringSplittingBenchmark.patternSplit                      thrpt    5  1550191.136 ±  475915.190  ops/s
StringSplittingBenchmark.patternSplit                  \s  thrpt    5   986040.695 ±  217942.160  ops/s
StringSplittingBenchmark.patternSplit     [ \t\n\x0B\f\r]  thrpt    5  1491352.737 ± 1025015.919  ops/s
StringSplittingBenchmark.patternSplit   |\t|\n|\x0B|\f|\r  thrpt    5   931442.162 ±  108993.004  ops/s
StringSplittingBenchmark.stringSplit                       thrpt    5  1820229.132 ±  564445.853  ops/s
StringSplittingBenchmark.stringSplit                   \s  thrpt    5   817729.421 ±   71545.171  ops/s
StringSplittingBenchmark.stringSplit      [ \t\n\x0B\f\r]  thrpt    5   774425.988 ±  128507.763  ops/s
StringSplittingBenchmark.stringSplit    |\t|\n|\x0B|\f|\r  thrpt    5   384563.020 ±   81302.831  ops/s

実際に travis-ci でバーっと走らせたときの結果は travis のサイトで見ることができる。 (travis 側の負荷状況によって結果が変わるので、参考程度だけれど) https://travis-ci.org/tokuhirom/java-string-splitting-benchmark


ちなみに今回、初めて JMH で @Param などのアノテーションを利用した。

以下のように記述することができて超便利〜。複数のパラメータを利用したベンチマークの実装が驚くほど簡単にできるのだ〜

    @State(Scope.Benchmark)
    public static class BenchmarkState {
        @Param({" ", "\\s", "[ \\t\\n\\x0B\\f\\r]", " |\\t|\\n|\\x0B|\\f|\\r"})
        private String raw;
        private Pattern pattern;

        @Setup
        public void setup() {
            pattern = Pattern.compile(raw);
        }
    }

    @SuppressWarnings("ResultOfMethodCallIgnored")
    @Benchmark
    public static void patternSplit(BenchmarkState state) {
        state.pattern.split(text);
    }

    @SuppressWarnings("ResultOfMethodCallIgnored")
    @Benchmark
    public static void stringSplit(BenchmarkState state) {
        text.split(state.raw);
    }