これは何?
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の記述が冗長?