8215017: Improve String::equals warmup characteristics
authorredestad
Thu, 11 Apr 2019 14:56:52 +0200
changeset 54597 ab57d6bebed8
parent 54596 86c1da00dd6a
child 54598 a9953a8ccd66
8215017: Improve String::equals warmup characteristics Reviewed-by: jlaskey
src/java.base/share/classes/java/lang/String.java
test/micro/org/openjdk/bench/java/lang/StringEquals.java
--- a/src/java.base/share/classes/java/lang/String.java	Tue Apr 23 18:28:48 2019 +0530
+++ b/src/java.base/share/classes/java/lang/String.java	Thu Apr 11 14:56:52 2019 +0200
@@ -1020,9 +1020,8 @@
         }
         if (anObject instanceof String) {
             String aString = (String)anObject;
-            if (coder() == aString.coder()) {
-                return isLatin1() ? StringLatin1.equals(value, aString.value)
-                                  : StringUTF16.equals(value, aString.value);
+            if (!COMPACT_STRINGS || this.coder == aString.coder) {
+                return StringLatin1.equals(value, aString.value);
             }
         }
         return false;
@@ -1057,7 +1056,8 @@
         }
         byte v1[] = value;
         byte v2[] = sb.getValue();
-        if (coder() == sb.getCoder()) {
+        byte coder = coder();
+        if (coder == sb.getCoder()) {
             int n = v1.length;
             for (int i = 0; i < n; i++) {
                 if (v1[i] != v2[i]) {
@@ -1065,7 +1065,7 @@
                 }
             }
         } else {
-            if (!isLatin1()) {  // utf16 str and latin1 abs can never be "equal"
+            if (coder != LATIN1) {  // utf16 str and latin1 abs can never be "equal"
                 return false;
             }
             return StringUTF16.contentEquals(v1, v2, len);
@@ -1209,12 +1209,13 @@
     public int compareTo(String anotherString) {
         byte v1[] = value;
         byte v2[] = anotherString.value;
-        if (coder() == anotherString.coder()) {
-            return isLatin1() ? StringLatin1.compareTo(v1, v2)
-                              : StringUTF16.compareTo(v1, v2);
+        byte coder = coder();
+        if (coder == anotherString.coder()) {
+            return coder == LATIN1 ? StringLatin1.compareTo(v1, v2)
+                                   : StringUTF16.compareTo(v1, v2);
         }
-        return isLatin1() ? StringLatin1.compareToUTF16(v1, v2)
-                          : StringUTF16.compareToLatin1(v1, v2);
+        return coder == LATIN1 ? StringLatin1.compareToUTF16(v1, v2)
+                               : StringUTF16.compareToLatin1(v1, v2);
      }
 
     /**
@@ -1238,12 +1239,13 @@
         public int compare(String s1, String s2) {
             byte v1[] = s1.value;
             byte v2[] = s2.value;
-            if (s1.coder() == s2.coder()) {
-                return s1.isLatin1() ? StringLatin1.compareToCI(v1, v2)
-                                     : StringUTF16.compareToCI(v1, v2);
+            byte coder = s1.coder();
+            if (coder == s2.coder()) {
+                return coder == LATIN1 ? StringLatin1.compareToCI(v1, v2)
+                                       : StringUTF16.compareToCI(v1, v2);
             }
-            return s1.isLatin1() ? StringLatin1.compareToCI_UTF16(v1, v2)
-                                 : StringUTF16.compareToCI_Latin1(v1, v2);
+            return coder == LATIN1 ? StringLatin1.compareToCI_UTF16(v1, v2)
+                                   : StringUTF16.compareToCI_Latin1(v1, v2);
         }
 
         /** Replaces the de-serialized object. */
@@ -1317,7 +1319,8 @@
              (ooffset > (long)other.length() - len)) {
             return false;
         }
-        if (coder() == other.coder()) {
+        byte coder = coder();
+        if (coder == other.coder()) {
             if (!isLatin1() && (len > 0)) {
                 toffset = toffset << 1;
                 ooffset = ooffset << 1;
@@ -1329,7 +1332,7 @@
                 }
             }
         } else {
-            if (coder() == LATIN1) {
+            if (coder == LATIN1) {
                 while (len-- > 0) {
                     if (StringLatin1.getChar(tv, toffset++) !=
                         StringUTF16.getChar(ov, ooffset++)) {
@@ -1411,12 +1414,13 @@
         }
         byte tv[] = value;
         byte ov[] = other.value;
-        if (coder() == other.coder()) {
-            return isLatin1()
+        byte coder = coder();
+        if (coder == other.coder()) {
+            return coder == LATIN1
               ? StringLatin1.regionMatchesCI(tv, toffset, ov, ooffset, len)
               : StringUTF16.regionMatchesCI(tv, toffset, ov, ooffset, len);
         }
-        return isLatin1()
+        return coder == LATIN1
               ? StringLatin1.regionMatchesCI_UTF16(tv, toffset, ov, ooffset, len)
               : StringUTF16.regionMatchesCI_Latin1(tv, toffset, ov, ooffset, len);
     }
@@ -1447,15 +1451,16 @@
         byte pa[] = prefix.value;
         int po = 0;
         int pc = pa.length;
-        if (coder() == prefix.coder()) {
-            int to = isLatin1() ? toffset : toffset << 1;
+        byte coder = coder();
+        if (coder == prefix.coder()) {
+            int to = (coder == LATIN1) ? toffset : toffset << 1;
             while (po < pc) {
                 if (ta[to++] != pa[po++]) {
                     return false;
                 }
             }
         } else {
-            if (isLatin1()) {  // && pcoder == UTF16
+            if (coder == LATIN1) {  // && pcoder == UTF16
                 return false;
             }
             // coder == UTF16 && pcoder == LATIN1)
@@ -1688,11 +1693,12 @@
      *          or {@code -1} if there is no such occurrence.
      */
     public int indexOf(String str) {
-        if (coder() == str.coder()) {
+        byte coder = coder();
+        if (coder == str.coder()) {
             return isLatin1() ? StringLatin1.indexOf(value, str.value)
                               : StringUTF16.indexOf(value, str.value);
         }
-        if (coder() == LATIN1) {  // str.coder == UTF16
+        if (coder == LATIN1) {  // str.coder == UTF16
             return -1;
         }
         return StringUTF16.indexOfLatin1(value, str.value);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/micro/org/openjdk/bench/java/lang/StringEquals.java	Thu Apr 11 14:56:52 2019 +0200
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.openjdk.bench.java.lang;
+
+import org.openjdk.jmh.annotations.*;
+import java.util.concurrent.TimeUnit;
+
+/*
+ * This benchmark naively explores String::equals performance
+ */
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.NANOSECONDS)
+@State(Scope.Benchmark)
+public class StringEquals {
+
+    public String test = new String("0123456789");
+    public String test2 = new String("tgntogjnrognagronagroangroarngorngaorng");
+    public String test3 = new String(test); // equal to test, but not same
+    public String test4 = new String("0123\u01FF");
+    public String test5 = new String(test4); // equal to test4, but not same
+    public String test6 = new String("0123456780");
+    public String test7 = new String("0123\u01FE");
+
+    @Benchmark
+    public boolean different() {
+        return test.equals(test2);
+    }
+
+    @Benchmark
+    public boolean equal() {
+        return test.equals(test3);
+    }
+
+    @Benchmark
+    public boolean almostEqual() {
+        return test.equals(test6);
+    }
+
+    @Benchmark
+    public boolean almostEqualUTF16() {
+        return test4.equals(test7);
+    }
+
+    @Benchmark
+    public boolean differentCoders() {
+        return test.equals(test4);
+    }
+
+    @Benchmark
+    public boolean equalsUTF16() {
+        return test5.equals(test4);
+    }
+}
+