diff --git a/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java b/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java index dc995589472b1..6418b7cff6904 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java +++ b/src/java.base/share/classes/jdk/internal/foreign/LayoutPath.java @@ -235,6 +235,18 @@ public VarHandle dereferenceHandle(boolean adapt) { return handle; } + public VarHandle arrayElementHandle() { + Utils.checkElementAlignment(layout, "Element layout size is not multiple of alignment"); + long[] arrayStrides = new long[strides.length + 1]; + long[] arrayBounds = new long[strides.length + 1]; + System.arraycopy(strides, 0, arrayStrides, 1, strides.length); + System.arraycopy(bounds, 0, arrayBounds, 1, bounds.length); + arrayStrides[0] = layout.byteSize(); + arrayBounds[0] = (Long.MAX_VALUE - 1) / layout.byteSize(); // avoid overflows + LayoutPath arrayPath = nestedPath(layout, offset, arrayStrides, arrayBounds, derefAdapters, enclosing); + return arrayPath.dereferenceHandle(); + } + @ForceInline private static long addScaledOffset(long base, long index, long stride, long bound) { Objects.checkIndex(index, bound); diff --git a/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java index b1821ba1d1afb..4b0d852910793 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java +++ b/src/java.base/share/classes/jdk/internal/foreign/layout/AbstractLayout.java @@ -192,7 +192,8 @@ public VarHandle varHandleInternal(PathElement... elements) { } public VarHandle arrayElementVarHandle(PathElement... elements) { - return MethodHandles.collectCoordinates(varHandle(elements), 1, scaleHandle()); + return computePathOp(LayoutPath.rootPath((MemoryLayout) this), LayoutPath::arrayElementHandle, + Set.of(), elements); } public MethodHandle sliceHandle(PathElement... elements) { diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/Alignment.java b/test/micro/org/openjdk/bench/java/lang/foreign/Alignment.java new file mode 100644 index 0000000000000..7ef4c21ea190b --- /dev/null +++ b/test/micro/org/openjdk/bench/java/lang/foreign/Alignment.java @@ -0,0 +1,107 @@ +package org.openjdk.bench.java.lang.foreign; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.util.concurrent.TimeUnit; + +import static java.lang.foreign.MemoryLayout.sequenceLayout; +import static java.lang.foreign.ValueLayout.JAVA_LONG; +import static java.lang.foreign.ValueLayout.JAVA_LONG_UNALIGNED; + +@BenchmarkMode(Mode.AverageTime) +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(org.openjdk.jmh.annotations.Scope.Thread) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@Fork(value = 3) +public class Alignment { + // base offset = 0 + static final VarHandle ALIGNED = MethodHandles.insertCoordinates(JAVA_LONG.arrayElementVarHandle(), 1, 0).withInvokeExactBehavior(); + static final VarHandle UNALIGNED = MethodHandles.insertCoordinates(JAVA_LONG_UNALIGNED.arrayElementVarHandle(), 1, 0).withInvokeExactBehavior(); + // base offset = 8 + static final VarHandle NEXT_ALIGNED = MethodHandles.insertCoordinates(JAVA_LONG.arrayElementVarHandle(), 1, JAVA_LONG.byteSize()).withInvokeExactBehavior(); + static final VarHandle NEXT_UNALIGNED = MethodHandles.insertCoordinates(JAVA_LONG_UNALIGNED.arrayElementVarHandle(), 1, JAVA_LONG.byteSize()).withInvokeExactBehavior(); + + static final long HAYSTACK = 30_000_000L; + static final long NEEDLE_INDEX = 934L; + static final long NEEDLE = 456L; + + final MemorySegment segment = Arena.global().allocate(sequenceLayout(HAYSTACK, JAVA_LONG)); + + { + ALIGNED.set(segment, NEEDLE_INDEX, NEEDLE); + } + + // 6 different ways to loop over segment[1, size): + + @Benchmark + public void findUnaligned() { + long size = segment.byteSize() / JAVA_LONG.byteSize(); + int numFound = 0; + for (long i = 1; i < size; ++i) { + if ((long) UNALIGNED.get(segment, i) == NEEDLE) ++numFound; + } + if (numFound != 1) throw new IllegalStateException(); + } + + @Benchmark + public void findAligned() { + long size = segment.byteSize() / JAVA_LONG.byteSize(); + int numFound = 0; + for (long i = 1; i < size; ++i) { + if ((long) ALIGNED.get(segment, i) == NEEDLE) ++numFound; + } + if (numFound != 1) throw new IllegalStateException(); + } + + @Benchmark + public void findUnalignedPlusOne() { + long size = segment.byteSize() / JAVA_LONG.byteSize(); + int numFound = 0; + for (long i = 0; i < size - 1; ++i) { + if ((long) UNALIGNED.get(segment, i + 1) == NEEDLE) ++numFound; + } + if (numFound != 1) throw new IllegalStateException(); + } + + @Benchmark + public void findAlignedPlusOne() { + long size = segment.byteSize() / JAVA_LONG.byteSize(); + int numFound = 0; + for (long i = 0; i < size - 1; ++i) { + if ((long) ALIGNED.get(segment, i + 1) == NEEDLE) ++numFound; + } + if (numFound != 1) throw new IllegalStateException(); + } + + @Benchmark + public void findUnalignedNext() { + long size = segment.byteSize() / JAVA_LONG.byteSize(); + int numFound = 0; + for (long i = 0; i < size - 1; ++i) { + if ((long) NEXT_UNALIGNED.get(segment, i) == NEEDLE) ++numFound; + } + if (numFound != 1) throw new IllegalStateException(); + } + + @Benchmark + public void findAlignedNext() { + long size = segment.byteSize() / JAVA_LONG.byteSize(); + int numFound = 0; + for (long i = 0; i < size - 1; ++i) { + if ((long) NEXT_ALIGNED.get(segment, i) == NEEDLE) ++numFound; + } + if (numFound != 1) throw new IllegalStateException(); + } +}