On Thu, 25 Jan 2024 14:48:16 GMT, Maurizio Cimadamore <[email protected]>
wrote:
> I don't 100% buy the `MethodHandleImpl` analogy. In that case the check is
> not simply used to save a branch, but to spare spinning of a completely new
> lambda form.
Doing this to save a few intructions would not likely to worth the hassle
outside the _really performance critical paths_, but even then it might be
useful for hot JDK code. On larger examples, you can avoid memory accesses,
allocations, etc. by coding up the constant-foldable path that you know
compiler would not be able to extract when propagating constants through the
generic code. For example, giving quantitative substance to my previous example:
diff --git a/src/java.base/share/classes/java/lang/Integer.java
b/src/java.base/share/classes/java/lang/Integer.java
index 1c5b3c414ba..d50748c369e 100644
--- a/src/java.base/share/classes/java/lang/Integer.java
+++ b/src/java.base/share/classes/java/lang/Integer.java
@@ -28,4 +28,5 @@
import jdk.internal.misc.CDS;
import jdk.internal.misc.VM;
+import jdk.internal.vm.ConstantSupport;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
@@ -416,4 +417,7 @@ private static void formatUnsignedIntUTF16(int val, int
shift, byte[] buf, int l
}
+ @Stable
+ static final String[] TO_STRINGS = { "-1", "0", "1" };
+
/**
* Returns a {@code String} object representing the
@@ -428,4 +432,8 @@ private static void formatUnsignedIntUTF16(int val, int
shift, byte[] buf, int l
@IntrinsicCandidate
public static String toString(int i) {
+ if (ConstantSupport.isCompileConstant(i) &&
+ (i >= -1) && (i <= 1)) {
+ return TO_STRINGS[i + 1];
+ }
int size = stringSize(i);
if (COMPACT_STRINGS) {
diff --git a/test/micro/org/openjdk/bench/java/lang/Integers.java
b/test/micro/org/openjdk/bench/java/lang/Integers.java
index 43ceb5d18d2..28248593a73 100644
--- a/test/micro/org/openjdk/bench/java/lang/Integers.java
+++ b/test/micro/org/openjdk/bench/java/lang/Integers.java
@@ -91,4 +91,18 @@ public void decode(Blackhole bh) {
}
+ @Benchmark
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ public String toStringConstYay() {
+ return Integer.toString(0);
+ }
+
+ int v = 0;
+
+ @Benchmark
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ public String toStringConstNope() {
+ return Integer.toString(v);
+ }
+
/** Performs toString on small values, just a couple of digits. */
@Benchmark
Benchmark (size) Mode Cnt Score
Error Units
Integers.toStringConstNope 500 avgt 15 3,599 ?
0,034 ns/op
Integers.toStringConstNope:gc.alloc.rate.norm 500 avgt 15 48,000 ?
0,001 B/op
Integers.toStringConstNope:gc.time 500 avgt 15 223,000
ms
Integers.toStringConstYay 500 avgt 15 0,568 ?
0,046 ns/op
Integers.toStringConstYay:gc.alloc.rate.norm 500 avgt 15 ? 10??
B/op
Think about it as simplifying/avoiding the need for full compiler intrinsics. I
could, in principle, do this by intrinsifying `Integer.toString` completely,
check the same `isCon`, and then either construct the access to some String
constant, or arrange the call to actual toString slow path. That would not be
as simple as doing the similar thing in plain Java, with just a little of
compiler support in form of `ConstantSupport`.
-------------
PR Comment: https://git.openjdk.org/jdk/pull/17527#issuecomment-1910449450