Why is Arrays.equals(char[], char[]) 8 times faster than all the other versions?

前端 未结 3 495
失恋的感觉
失恋的感觉 2021-02-02 10:31

Short Story

Based on my tests with a few different Oracle and OpenJDK implementations, it seems that Arrays.equals(char[], char[]) is somehow about 8 times fas

相关标签:
3条回答
  • 2021-02-02 10:58

    Because for chars, SSE3 and 4.1/4.2 are both extremely good at checking state change. The JVM-generated code for char manipulation code is more tuned because that's what Java gets used a lot for in web apps and such. Java is awful at optimizing for other kinds of data. That's just the nature of the beast.

    This same behaviour is observable in Scala and GoSu as well. The bulk of information in transit these days is in text form, so unless you modify your JVM, it's tuned for text. And, as Marco mentioned, it is an intrinsic C function underneath, meaning it directly maps to high-performance vectorized instructions like SSE4.x or even AVX2 if the standard JVM has been improved that much.

    http://blog.synopse.info/post/2015/06/30/Faster-String-process-using-SSE-4.2-Text-Processing-Instructions-STTNI

    http://www.tomshardware.com/reviews/Intel-i7-nehalem-cpu,2041-7.html

    Seriously, SSE4.x does not treat chars and bytes as equivalent data types, which is why text analysis is faster. Furthermore, for 8-bit integral, comparisons the instructions didn't exist until AVX2.

    0 讨论(0)
  • 2021-02-02 11:03

    I might go out on a limb when suggesting that this is the answer, but according to http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/9d15b81d5d1b/src/share/vm/classfile/vmSymbols.hpp#l756, the Arrays#equals(char[], char[]) method is implemented as an intrinsic.

    Most likely because it is highly performance critical in all string comparisons. <- This was wrong, at least. Surprisingly, String does not use Arrays.equals for the comparison. But regardless of why it is an intrinsic, this may still be the reason for the performance difference.

    0 讨论(0)
  • 2021-02-02 11:23

    @Marco13 guess was right. HotSpot JVM has an intrinsic (i.e. special hand-coded implementation) forArrays.equals(char[], char[]), but not for other Arrays.equals methods.

    The following JMH benchmark proves that disabling this intrinsic makes char[] array comparsion as slow as short[] array comparison.

    @State(Scope.Benchmark)
    public class ArrayEquals {
        @Param("100")
        int length;
    
        short[] s1, s2;
        char[] c1, c2;
    
        @Setup
        public void setup() {
            s1 = new short[length];
            s2 = new short[length];
            c1 = new char[length];
            c2 = new char[length];
        }
    
        @Benchmark
        public boolean chars() {
            return Arrays.equals(c1, c2);
        }
    
        @Benchmark
        @Fork(jvmArgsAppend = {"-XX:+UnlockDiagnosticVMOptions", "-XX:DisableIntrinsic=_equalsC"})
        public boolean charsNoIntrinsic() {
            return Arrays.equals(c1, c2);
        }
    
        @Benchmark
        public boolean shorts() {
            return Arrays.equals(s1, s2);
        }
    }
    

    Results:

    Benchmark                     (length)  Mode  Cnt   Score   Error  Units
    ArrayEquals.chars                  100  avgt   10  19,012 ± 1,204  ns/op
    ArrayEquals.charsNoIntrinsic       100  avgt   10  49,495 ± 0,682  ns/op
    ArrayEquals.shorts                 100  avgt   10  49,566 ± 0,815  ns/op
    

    This intrinsic was added long ago in 2008 in the times of aggressive JVM competition. JDK 6 included a special alt-string.jar library which was enabled by -XX:+UseStringCache. I've found a number of calls to Arrays.equals(char[], char[]) from one of these special classes - StringValue.StringCache. The intrinsic was an essential part of this "optimization". In modern JDK there is no more alt-string.jar, but JVM intrinsic is still there (not playing its original role though).

    Update

    I've tested the same with JDK 9-ea+148, and it appears that _equalsC intrinsic makes very little performance difference.

    Benchmark                     (length)  Mode  Cnt   Score   Error  Units
    ArrayEquals.chars                  100  avgt   10  18,931 ± 0,061  ns/op
    ArrayEquals.charsNoIntrinsic       100  avgt   10  19,616 ± 0,063  ns/op
    ArrayEquals.shorts                 100  avgt   10  19,753 ± 0,080  ns/op
    

    Arrays.equals implementation has changed in JDK 9.

    Now it calls ArraysSupport.vectorizedMismatch helper method for all types of non-object arrays. Furthermore, vectorizedMismatch is also a HotSpot intrinsic which has hand-written assembly implementation that uses AVX.

    0 讨论(0)
提交回复
热议问题