17

私はString.equals()メソッドのより高速なバージョンを作成しようとしていて、それを単にコピーすることから始めました。私が見つけた結果は非常に紛らわしいものでした。コピーして貼り付けたバージョンを実行し、タイミングを合わせてJVMバージョンと比較すると、JVMバージョンの方が高速でした。差は6倍から34倍速くなりました!簡単に言えば、文字列が長いほど、違いは大きくなります。

boolean equals(final char a[], final char b[]) {
    int n = a.length;
    int i = 0;

    while (n-- != 0) {
        if (a[i] != b[i]) return false;
        i++;
    }
    return true;
}

public static void main() throws Exception {
    String a = "blah balh balh";
    String b = "blah balh balb";

    long me = 0, jvm = 0;

    Field value = String.class.getDeclaredField("value");
    value.setAccessible(true);

    final char lhs[] = (char[]) value.get(a);
    final char rhs[] = (char[]) value.get(b);
    for (int i = 0; i < 100; i++) {
        long t = System.nanoTime();
        equals(lhs, rhs);
        t = System.nanoTime() - t;
        me += t;
    }

    for (int i = 0; i < 100; i++) {
        long t = System.nanoTime();
        a.equals(b);
        t = System.nanoTime() - t;
        jvm += t;
    }

    System.out.println("me  = " + me);
    System.out.println("jvm = " + jvm);
}

出力:

me  = 258931
jvm = 14991

私が書いたequals()メソッドは、String.equals()メソッドにあるもののコピー貼り付けバージョンです。JVMバージョンがコピー貼り付けバージョンよりも速いのはなぜですか?事実上同じではないですか?

なぜ私がそのような目に見える違いを見るのか誰かが説明できますか?

PS:大きな違いを見たい場合は、最後に1文字だけ異なる長い(本当に本当に長い)文字列を作成できます。

4

3 に答える 3

17

JVMバージョンがコピー貼り付けバージョンよりも速いのはなぜですか。事実上同じではないですか?

驚いたことに、そうではありません。

文字列の比較は非常にユビキタスな操作であるため、 JITコンパイラに。の組み込み関数があることはほぼ間違いありませんString.equals()。これは、コンパイラが文字列を比較するために特別に作成されたマシンコードを生成する方法を知っていることを意味します。これは、を使用するときに、プログラマーであるあなたに対して透過的に行われますString.equals()

これは、表面的には同じように見えても、なぜString.equals()あなたの方法よりもはるかに速いのかを説明します。

クイック検索では、HotSpotにそのような本質的なものについて言及しているいくつかのバグレポートが見つかります。たとえば、7041100:String.equals組み込みのロードは、nullチェックの前に実行されます

関連するHotSpotソースはここにあります。問題の機能は次のとおりです。

  848 Node* LibraryCallKit::make_string_method_node(int opcode, Node* str1, Node* cnt1, Node* str2, Node* cnt2) {

  943 bool LibraryCallKit::inline_string_equals() {
于 2013-03-23T13:19:06.843 に答える
2

ホットスポットを使用すると、開発者はJava実装に加えてメソッドのネイティブ実装を提供できます。Javaコードは実行時にスワップアウトされ、最適化されたバージョンに置き換えられます。これは、組み込みと呼ばれます。基本クラスの数百のメソッドが組み込み関数によって最適化されています。

OpenJDKのソースコードを見ると、String.equalsのx86_64実装を確認できます。vmSymbolsを調べて、すべての本能のリストを取得することもできます(検索do_intrinsic

于 2013-03-23T13:40:15.707 に答える
-1

ご存知のように、JAVAは本当にコンパイラベースでもインタプリタベースの言語でもありません。つまり、Javaは両方を使用します。つまり、コンパイラを使用してソースコードを中間形式に変換し、実行時に解釈します。

これは、コードのどの部分がより頻繁に実行されるかを知るために実行されるLoC(コード行)をマークします。JAVAは、指定されたしきい値を超えて実行されているコードの一部を見つけるとすぐに、それをホットとマークし、このコードをその場でコンパイラーに送信して、次回の要求時に実行される最適化されたバージョンを作成します。これはJIT(ジャストインタイム)

と呼ばれ ます。両方のコードがまったく同じであるため、JAVA HotSpotは両方のメソッドをまったく同じ、つまり同じ実行時間で最適化する必要があります。残念ながら、そうではありません。

JITは、equals()メソッドがホットで頻繁に呼び出されていると判断するとすぐに、ハードウェアレベルでの特別な最適化を行います。
はい、テキスト処理コードを高速化するためにIntelによって作成されたまったく新しい一連の命令があります。つまり、 Strings.equals()メソッドをコピーして貼り付けたequalsメソッドよりも高速にするための別の命令があります。

ここで問題は、これがどのように発生するかです。ええと、これは簡単です。String.equals()暖かいときはいつでも(つまり、より頻繁に使用されますが、それほど頻繁には使用されません)、コンパイラーはコピー貼り付けメソッドと同じ最適化を行います。しかし、equal()メソッドが熱くなると、JITは新しい命令セットを直接使用して文字列を比較し、実行を高速化します。

于 2018-07-29T18:02:24.393 に答える