On Thu, 25 Jan 2024 14:48:16 GMT, Maurizio Cimadamore <mcimadam...@openjdk.org> 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