Weird performance behavior involving VarHandles

Per-Ake Minborg per-ake.minborg at oracle.com
Wed Apr 24 07:04:34 UTC 2024


Hi Rémi,

I understand how the code is supposed to work (interesting solution!) but actually seeing how things are inlined/folded in reality is often hard. What you could do is to try and log inlining. Jorn has written some excellent articles about this:

https://jornvernee.github.io/methodhandles/2024/01/19/methodhandle-primer.html#method-handle-inlining
https://jornvernee.github.io/hotspot/jit/2023/08/18/debugging-jit.html#3-printing-inlining-traces

I hope that can give you some additional clues what is going on. If not, I hope someone else with more knowledge on the compiler can point you in the right direction.

Best, Per Minborg

________________________________
From: panama-dev <panama-dev-retn at openjdk.org> on behalf of Remi Forax <forax at univ-mlv.fr>
Sent: Wednesday, April 24, 2024 8:37 AM
To: hotspot compiler <hotspot-compiler-dev at openjdk.java.net>
Cc: panama-dev <panama-dev at openjdk.org>
Subject: Weird performance behavior involving VarHandles

Hello,
i'm trying to build an API on top of the foreign memory API and i've found a performance difference i'm not able to explain.

I'm using a guardWithTest to try to provide a simple way to access a VarHandle on a MemoryLayout without having to declare each VarHandle by hand,
so instead of

  private static final StructLayout LAYOUT = MemoryLayout.structLayout(
      ValueLayout.JAVA_INT.withName("x"),
      ValueLayout.JAVA_INT.withName("y")
  );

  private static final VarHandle HANDLE_X =
      LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("x"));
  private static final VarHandle HANDLE_Y =
      LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("y"));

I want something like

  private static final MethodHandle MH = guardWithTest(
      TEST.bindTo("x"),
      dropArguments(constant(VarHandle.class, HANDLE_X), 0, String.class),
      guardWithTest(
          TEST.bindTo("y"),
          dropArguments(constant(VarHandle.class, HANDLE_Y), 0, String.class),
          BOOM
      ));

  (TEST does an == on the strings and BOOM throws an exception)

which if called with "x" returns the VarHandle for "x" and if called with "y" returns the VarHandle for "y".


Now if I try to benchmark the performance with JMH,

  private final MemorySegment segment = Arena.ofAuto().allocate(LAYOUT);

  @Benchmark
  public int control() {
    var x = (int) HANDLE_X.get(segment, 0L);
    var y = (int) HANDLE_Y.get(segment, 0L);
    return x + y;
  }

  @Benchmark
  public int gwt2_methodhandle() throws Throwable {
    var x = (int) ((VarHandle) MH.invokeExact("x")).get(segment, 0L);
    var y = (int) ((VarHandle) MH.invokeExact("y")).get(segment, 0L);
    return x + y;
  }

I get

Benchmark                               Mode  Cnt  Score   Error  Units
ReproducerBenchmarks.control            avgt    5  1.250 ± 0.024  ns/op
ReproducerBenchmarks.gwt2_methodhandle  avgt    5  1.852 ± 0.024  ns/op

and I don't understand why there is a difference in performance because for c2, the strings "x" and "y" are constant so the corresponding VarHandles should be constant thus optimized the same way.

The full benchmark is available here:
  https://raw.githubusercontent.com/forax/memory-mapper/master/src/main/java/com/github/forax/memorymapper/bench/ReproducerBenchmarks.java

regards,
Rémi

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/panama-dev/attachments/20240424/1ca38cf8/attachment-0001.htm>


More information about the panama-dev mailing list