diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/BulkOps.java b/test/micro/org/openjdk/bench/java/lang/foreign/BulkOps.java deleted file mode 100644 index 60f36d9f157..00000000000 --- a/test/micro/org/openjdk/bench/java/lang/foreign/BulkOps.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (c) 2020, 2025, 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.foreign; - -import org.openjdk.jmh.annotations.Benchmark; -import org.openjdk.jmh.annotations.BenchmarkMode; -import org.openjdk.jmh.annotations.CompilerControl; -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.Setup; -import org.openjdk.jmh.annotations.State; -import org.openjdk.jmh.annotations.TearDown; -import org.openjdk.jmh.annotations.Warmup; -import jdk.internal.misc.Unsafe; - -import java.lang.foreign.Arena; -import java.lang.foreign.MemorySegment; -import java.nio.ByteBuffer; -import java.nio.IntBuffer; -import java.util.concurrent.TimeUnit; - -import static java.lang.foreign.ValueLayout.JAVA_INT; -import static java.lang.foreign.ValueLayout.JAVA_INT_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, jvmArgs = { "--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED" }) -public class BulkOps { - - static final Unsafe unsafe = Utils.unsafe; - - static final int ELEM_SIZE = 1_000_000; - static final int CARRIER_SIZE = (int)JAVA_INT.byteSize(); - static final int ALLOC_SIZE = ELEM_SIZE * CARRIER_SIZE; - - final Arena arena = Arena.ofShared(); - - final long unsafe_addr = unsafe.allocateMemory(ALLOC_SIZE); - final MemorySegment segment = arena.allocate(ALLOC_SIZE, 1); - - final IntBuffer buffer = IntBuffer.allocate(ELEM_SIZE); - - final int[] ints = new int[ELEM_SIZE]; - final MemorySegment bytesSegment = MemorySegment.ofArray(ints); - final long UNSAFE_INT_OFFSET = unsafe.arrayBaseOffset(int[].class); - - // large(ish) segments/buffers with same content, 0, for mismatch, non-multiple-of-8 sized - static final int SIZE_WITH_TAIL = (1024 * 1024) + 7; - final MemorySegment mismatchSegmentLarge1; - - { - mismatchSegmentLarge1 = arena.allocate(SIZE_WITH_TAIL, 1); - } - - final MemorySegment mismatchSegmentLarge2 = arena.allocate(SIZE_WITH_TAIL, 1); - final ByteBuffer mismatchBufferLarge1 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); - final ByteBuffer mismatchBufferLarge2 = ByteBuffer.allocateDirect(SIZE_WITH_TAIL); - - // mismatch at first byte - final MemorySegment mismatchSegmentSmall1 = arena.allocate(7, 1); - final MemorySegment mismatchSegmentSmall2 = arena.allocate(7, 1); - final ByteBuffer mismatchBufferSmall1 = ByteBuffer.allocateDirect(7); - final ByteBuffer mismatchBufferSmall2 = ByteBuffer.allocateDirect(7); - - @Setup - public void setup() { - mismatchSegmentSmall1.fill((byte) 0xFF); - mismatchBufferSmall1.put((byte) 0xFF).clear(); - // verify expected mismatch indices - long si = mismatchSegmentLarge1.mismatch(mismatchSegmentLarge2); - if (si != -1) - throw new AssertionError("Unexpected mismatch index:" + si); - int bi = mismatchBufferLarge1.mismatch(mismatchBufferLarge2); - if (bi != -1) - throw new AssertionError("Unexpected mismatch index:" + bi); - si = mismatchSegmentSmall1.mismatch(mismatchSegmentSmall2); - if (si != 0) - throw new AssertionError("Unexpected mismatch index:" + si); - bi = mismatchBufferSmall1.mismatch(mismatchBufferSmall2); - if (bi != 0) - throw new AssertionError("Unexpected mismatch index:" + bi); - - for (int i = 0; i < ints.length ; i++) { - ints[i] = i; - } - } - - @TearDown - public void tearDown() { - arena.close(); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public void unsafe_fill() { - unsafe.setMemory(unsafe_addr, ALLOC_SIZE, (byte)42); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public void segment_fill() { - segment.fill((byte)42); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public void unsafe_copy() { - unsafe.copyMemory(ints, UNSAFE_INT_OFFSET, null, unsafe_addr, ALLOC_SIZE); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public void segment_copy() { - segment.copyFrom(bytesSegment); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public void segment_copy_static() { - MemorySegment.copy(ints, 0, segment, JAVA_INT_UNALIGNED, 0, ints.length); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public void segment_copy_static_small() { - MemorySegment.copy(ints, 0, segment, JAVA_INT_UNALIGNED, 0, 10); - } - - @Benchmark - @CompilerControl(CompilerControl.Mode.DONT_INLINE) - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public void segment_copy_static_small_dontinline() { - MemorySegment.copy(ints, 0, segment, JAVA_INT_UNALIGNED, 0, 10); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public void unsafe_copy_small() { - unsafe.copyMemory(ints, UNSAFE_INT_OFFSET, null, unsafe_addr, 10 * CARRIER_SIZE); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public void buffer_copy_small() { - buffer.put(0, ints, 0, 10); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public void buffer_copy() { - buffer.put(0, ints, 0, ints.length); - } - - @Benchmark - @CompilerControl(CompilerControl.Mode.DONT_INLINE) - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public void segment_copy_static_dontinline() { - MemorySegment.copy(ints, 0, segment, JAVA_INT_UNALIGNED, 0, ints.length); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public long mismatch_large_segment() { - return mismatchSegmentLarge1.mismatch(mismatchSegmentLarge2); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public int mismatch_large_bytebuffer() { - return mismatchBufferLarge1.mismatch(mismatchBufferLarge2); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public long mismatch_small_segment() { - return mismatchSegmentSmall1.mismatch(mismatchSegmentSmall2); - } - - @Benchmark - @OutputTimeUnit(TimeUnit.NANOSECONDS) - public int mismatch_small_bytebuffer() { - return mismatchBufferSmall1.mismatch(mismatchBufferSmall2); - } -} diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkCopy.java b/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkCopy.java index ca6f21d20e9..83522bd4f3c 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkCopy.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkCopy.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, 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 @@ -38,9 +38,13 @@ import org.openjdk.jmh.annotations.Warmup; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.nio.ByteBuffer; +import java.util.Random; import java.util.concurrent.TimeUnit; +import static java.lang.foreign.ValueLayout.JAVA_LONG; + @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -49,63 +53,122 @@ import java.util.concurrent.TimeUnit; @Fork(value = 3) public class SegmentBulkCopy { - @Param({"2", "3", "4", "5", "6", "7", "8", "64", "512", - "4096", "32768", "262144", "2097152", "16777216", "134217728"}) - public int ELEM_SIZE; + @Param({"2", "4", "8", "12", "16", "64", "512", "4096", "32768", "262144", "2097152", "16777216", "134217728"}) + public int size; - byte[] srcArray; - byte[] dstArray; - MemorySegment heapSrcSegment; - MemorySegment heapDstSegment; - MemorySegment nativeSrcSegment; - MemorySegment nativeDstSegment; - ByteBuffer srcBuffer; - ByteBuffer dstBuffer; + public static class Array extends SegmentBulkCopy { + + byte[] srcArray; + byte[] dstArray; + + ByteBuffer srcBuffer; + ByteBuffer dstBuffer; + + @Setup + public void setup() { + srcArray = new byte[size]; + var rnd = new Random(42); + rnd.nextBytes(srcArray); + dstArray = new byte[size]; + srcBuffer = ByteBuffer.wrap(srcArray); + dstBuffer = ByteBuffer.wrap(dstArray); + } + + @Benchmark + public void arrayCopy() { + System.arraycopy(srcArray, 0, dstArray, 0, size); + } + + @Benchmark + public void bufferCopy() { + dstBuffer.put(0, srcBuffer, 0, size); + } - @Setup - public void setup() { - srcArray = new byte[ELEM_SIZE]; - dstArray = new byte[ELEM_SIZE]; - heapSrcSegment = MemorySegment.ofArray(srcArray); - heapDstSegment = MemorySegment.ofArray(dstArray); - nativeSrcSegment = Arena.ofAuto().allocate(ELEM_SIZE); - nativeDstSegment = Arena.ofAuto().allocate(ELEM_SIZE); - srcBuffer = ByteBuffer.wrap(srcArray); - dstBuffer = ByteBuffer.wrap(dstArray); } - @Benchmark - public void arrayCopy() { - System.arraycopy(srcArray, 0, dstArray, 0, ELEM_SIZE); - } + public static class Segment extends SegmentBulkCopy { - @Benchmark - public void bufferCopy() { - dstBuffer.put(srcBuffer); - } + enum SegmentType {HEAP, NATIVE} + enum Alignment {ALIGNED, UNALIGNED} - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.copy=31"}) - @Benchmark - public void heapSegmentCopyJava() { - MemorySegment.copy(heapSrcSegment, 0, heapDstSegment, 0, ELEM_SIZE); - } + @Param({"HEAP", "NATIVE"}) + String segmentType; - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.copy=0"}) - @Benchmark - public void heapSegmentCopyUnsafe() { - MemorySegment.copy(heapSrcSegment, 0, heapDstSegment, 0, ELEM_SIZE); - } + @Param({"ALIGNED", "UNALIGNED"}) + String alignment; - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.copy=31"}) - @Benchmark - public void nativeSegmentCopyJava() { - MemorySegment.copy(nativeSrcSegment, 0, nativeDstSegment, 0, ELEM_SIZE); - } + MemorySegment srcSegment; + MemorySegment dstSegment; + + @Setup + public void setup() { + // A long array is likely to be aligned at 8-byte boundaries + long[] baseArray; + + baseArray = new long[size / Long.BYTES + 1]; + var rnd = new Random(42); + for (int i = 0; i < baseArray.length; i++) { + baseArray[i] = rnd.nextLong(); + } + + switch (SegmentType.valueOf(segmentType)) { + case HEAP -> { + srcSegment = MemorySegment.ofArray(baseArray); + dstSegment = MemorySegment.ofArray(baseArray.clone()); + } + case NATIVE -> { + srcSegment = Arena.ofAuto().allocateFrom(JAVA_LONG, baseArray); + dstSegment = Arena.ofAuto().allocateFrom(JAVA_LONG, baseArray); + } + } + switch (Alignment.valueOf(alignment)) { + case ALIGNED -> { + srcSegment = srcSegment.asSlice(0, size); + dstSegment = dstSegment.asSlice(0, size); + } + case UNALIGNED -> { + srcSegment = srcSegment.asSlice(1, size); + dstSegment = dstSegment.asSlice(1, size); + } + } + } + + @Benchmark + @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.copy=31"}) + public void copy() { + MemorySegment.copy(srcSegment, 0, dstSegment, 0, size); + } + + @Benchmark + public void copyLoopIntInt() { + for (int i = 0; i < (int) srcSegment.byteSize(); i++) { + final byte v = srcSegment.get(ValueLayout.JAVA_BYTE, i); + dstSegment.set(ValueLayout.JAVA_BYTE, i, v); + } + } + + @Benchmark + public void copyLoopIntLong() { + for (int i = 0; i < srcSegment.byteSize(); i++) { + final byte v = srcSegment.get(ValueLayout.JAVA_BYTE, i); + dstSegment.set(ValueLayout.JAVA_BYTE, i, v); + } + } + + @Benchmark + public void copyLoopLongLong() { + for (long i = 0; i < srcSegment.byteSize(); i++) { + final byte v = srcSegment.get(ValueLayout.JAVA_BYTE, i); + dstSegment.set(ValueLayout.JAVA_BYTE, i, v); + } + } + + @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.copy=0"}) + @Benchmark + public void copyUnsafe() { + MemorySegment.copy(srcSegment, 0, dstSegment, 0, size); + } - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.copy=0"}) - @Benchmark - public void nativeSegmentCopyUnsafe() { - MemorySegment.copy(nativeSrcSegment, 0, nativeDstSegment, 0, ELEM_SIZE); } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkFill.java b/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkFill.java index 96cf62cc5f6..3c4770731b6 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkFill.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkFill.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, 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 @@ -43,6 +43,8 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.concurrent.TimeUnit; +import static java.lang.foreign.ValueLayout.JAVA_LONG; + @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -51,99 +53,105 @@ import java.util.concurrent.TimeUnit; @Fork(value = 3) public class SegmentBulkFill { - @Param({"2", "3", "4", "5", "6", "7", "8", "64", "512", - "4096", "32768", "262144", "2097152", "16777216", "134217728"}) - public int ELEM_SIZE; + private static final byte ZERO = 0; - byte[] array; - MemorySegment heapSegment; - MemorySegment nativeSegment; - MemorySegment unalignedSegment; - ByteBuffer buffer; + @Param({"2", "4", "8", "12", "16", "64", "512", "4096", "32768", "262144", "2097152", "16777216", "134217728"}) + public int size; - @Setup - public void setup() { - array = new byte[ELEM_SIZE]; - heapSegment = MemorySegment.ofArray(array); - nativeSegment = Arena.ofAuto().allocate(ELEM_SIZE, 8); - unalignedSegment = Arena.ofAuto().allocate(ELEM_SIZE + 1, 8).asSlice(1); - buffer = ByteBuffer.wrap(array); - } + public static class Array extends SegmentBulkFill { - @Benchmark - public void arraysFill() { - Arrays.fill(array, (byte) 0); - } + byte[] array; + ByteBuffer buffer; - @Benchmark - public void arraysFillLoop() { - for (int i = 0; i < array.length; i++) { - array[i] = 0; + @Setup + public void setup() { + array = new byte[size]; + buffer = ByteBuffer.wrap(array); } - } - @Benchmark - public void bufferFillLoop() { - for (int i = 0; i < array.length; i++) { - buffer.put(i, (byte)0); + @Benchmark + public void arraysFill() { + Arrays.fill(array, ZERO); } - } - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.fill=31"}) - @Benchmark - public void heapSegmentFillJava() { - heapSegment.fill((byte) 0); - } - - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.fill=0"}) - @Benchmark - public void heapSegmentFillUnsafe() { - heapSegment.fill((byte) 0); - } - - @Benchmark - public void heapSegmentFillLoop() { - for (long i = 0; i < heapSegment.byteSize(); i++) { - heapSegment.set(ValueLayout.JAVA_BYTE, i, (byte) 0); + @Benchmark + public void arraysFillLoop() { + for (int i = 0; i < array.length; i++) { + array[i] = ZERO; + } } - } - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.fill=31"}) - @Benchmark - public void nativeSegmentFillJava() { - nativeSegment.fill((byte) 0); - } - - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.fill=0"}) - @Benchmark - public void nativeSegmentFillUnsafe() { - nativeSegment.fill((byte) 0); - } - - @Benchmark - public void nativeSegmentFillLoop() { - for (long i = 0; i < nativeSegment.byteSize(); i++) { - nativeSegment.set(ValueLayout.JAVA_BYTE, i, (byte) 0); + @Benchmark + public void bufferFillLoop() { + for (int i = 0; i < array.length; i++) { + buffer.put(i, ZERO); + } } + } - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.fill=31"}) - @Benchmark - public void unalignedSegmentFillJava() { - unalignedSegment.fill((byte) 0); - } + public static class Segment extends SegmentBulkFill { - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.fill=0"}) - @Benchmark - public void unalignedSegmentFillUnsafe() { - unalignedSegment.fill((byte) 0); - } + enum SegmentType {HEAP, NATIVE} + enum Alignment {ALIGNED, UNALIGNED} - @Benchmark - public void unalignedSegmentFillLoop() { - for (long i = 0; i < unalignedSegment.byteSize(); i++) { - unalignedSegment.set(ValueLayout.JAVA_BYTE, i, (byte) 0); + @Param({"HEAP", "NATIVE"}) + String segmentType; + + @Param({"ALIGNED", "UNALIGNED"}) + String alignment; + + MemorySegment segment; + + @Setup + public void setup() { + // A long array is likely to be aligned at 8-byte boundaries + long[] baseArray = new long[size / Long.BYTES + 1]; + var heapSegment = MemorySegment.ofArray(baseArray); + + segment = switch (SegmentType.valueOf(segmentType)) { + case HEAP -> heapSegment; + case NATIVE -> Arena.ofAuto().allocateFrom(JAVA_LONG, baseArray); + }; + segment = switch (Alignment.valueOf(alignment)) { + case ALIGNED -> segment.asSlice(0, size); + case UNALIGNED -> segment.asSlice(1, size); + }; } + + @Benchmark + @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.fill=31"}) + public void fill() { + segment.fill(ZERO); + } + + @Benchmark + public void fillLoopIntInt() { + for (int i = 0; i < (int)segment.byteSize(); i++) { + segment.set(ValueLayout.JAVA_BYTE, i, ZERO); + } + } + + @Benchmark + public void fillLoopIntLong() { + for (int i = 0; i < segment.byteSize(); i++) { + segment.set(ValueLayout.JAVA_BYTE, i, ZERO); + } + } + + @Benchmark + public void fillLoopLongLong() { + for (long i = 0; i < segment.byteSize(); i++) { + segment.set(ValueLayout.JAVA_BYTE, i, ZERO); + } + } + + @Benchmark + @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.fill=0"}) + public void fillUnsafe() { + segment.fill(ZERO); + } + } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkHash.java b/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkHash.java index 927a7d3fb1f..0213916fcd6 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkHash.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkHash.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, 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 @@ -45,6 +45,7 @@ import java.util.Random; import java.util.concurrent.TimeUnit; import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_LONG; @BenchmarkMode(Mode.AverageTime) @Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) @@ -54,47 +55,98 @@ import static java.lang.foreign.ValueLayout.JAVA_BYTE; @Fork(value = 3, jvmArgs = {"--add-exports=java.base/jdk.internal.foreign=ALL-UNNAMED"}) public class SegmentBulkHash { - @Param({"8", "64"}) - public int ELEM_SIZE; + @Param({"2", "4", "8", "12", "16", "64", "512", "4096", "32768", "262144", "2097152", "16777216", "134217728"}) + public int size; - byte[] array; - AbstractMemorySegmentImpl heapSegment; - AbstractMemorySegmentImpl nativeSegment; + public static class Array extends SegmentBulkHash { - @Setup - public void setup() { - // Always use the same alignment regardless of size - nativeSegment = (AbstractMemorySegmentImpl) Arena.ofAuto().allocate(ELEM_SIZE, 16); - var rnd = new Random(42); - for (int i = 0; i < ELEM_SIZE; i++) { - nativeSegment.set(JAVA_BYTE, i, (byte) rnd.nextInt(Byte.MIN_VALUE, Byte.MAX_VALUE)); + byte[] array; + + @Setup + public void setup() { + byte[] randomArray = new byte[size + 1]; + var rnd = new Random(42); + rnd.nextBytes(randomArray); + + array = Arrays.copyOf(randomArray, size); } - array = nativeSegment.toArray(JAVA_BYTE); - heapSegment = (AbstractMemorySegmentImpl) MemorySegment.ofArray(array); - } - @Benchmark - public int array() { - return Arrays.hashCode(array); - } - - @Benchmark - public int heapSegment() { - return SegmentBulkOperations.contentHash(heapSegment, 0, ELEM_SIZE); - } - - @Benchmark - public int nativeSegment() { - return SegmentBulkOperations.contentHash(nativeSegment, 0, ELEM_SIZE); - } - - @Benchmark - public int nativeSegmentJava() { - int result = 1; - for (long i = 0; i < ELEM_SIZE; i++) { - result = 31 * result + nativeSegment.get(JAVA_BYTE, i); + @Benchmark + public int array() { + return Arrays.hashCode(array); } - return result; + + } + + public static class Segment extends SegmentBulkHash { + + enum SegmentType {HEAP, NATIVE} + enum Alignment {ALIGNED, UNALIGNED} + + @Param({"HEAP", "NATIVE"}) + String segmentType; + + @Param({"ALIGNED", "UNALIGNED"}) + String alignment; + + AbstractMemorySegmentImpl segment; + + @Setup + public void setup() { + // A long array is likely to be aligned at 8-byte boundaries + long[] baseArray; + + baseArray = new long[size / Long.BYTES + 1]; + var rnd = new Random(42); + for (int i = 0; i < baseArray.length; i++) { + baseArray[i] = rnd.nextLong(); + } + var heapSegment = MemorySegment.ofArray(baseArray); + + var s = switch (SegmentType.valueOf(segmentType)) { + case HEAP -> heapSegment; + case NATIVE -> Arena.ofAuto().allocateFrom(JAVA_LONG, baseArray); + }; + s = switch (Alignment.valueOf(alignment)) { + case ALIGNED -> s.asSlice(0, size); + case UNALIGNED -> s.asSlice(1, size); + }; + + segment = (AbstractMemorySegmentImpl) s; + } + + @Benchmark + public int hash() { + return SegmentBulkOperations.contentHash(segment, 0, size); + } + + @Benchmark + public int hashLoopIntInt() { + int result = 1; + for (int i = 0; i < (int)segment.byteSize(); i++) { + result = 31 * result + segment.get(JAVA_BYTE, i); + } + return result; + } + + @Benchmark + public int hashLoopIntLong() { + int result = 1; + for (int i = 0; i < segment.byteSize(); i++) { + result = 31 * result + segment.get(JAVA_BYTE, i); + } + return result; + } + + @Benchmark + public int hashLoopLongLong() { + int result = 1; + for (long i = 0; i < segment.byteSize(); i++) { + result = 31 * result + segment.get(JAVA_BYTE, i); + } + return result; + } + } } diff --git a/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkMismatch.java b/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkMismatch.java index 61ceb7b956e..aa4dd0b68f9 100644 --- a/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkMismatch.java +++ b/test/micro/org/openjdk/bench/java/lang/foreign/SegmentBulkMismatch.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2025, 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 @@ -38,6 +38,7 @@ import org.openjdk.jmh.annotations.Warmup; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; import java.util.Arrays; import java.util.Random; import java.util.concurrent.TimeUnit; @@ -52,60 +53,122 @@ import static java.lang.foreign.ValueLayout.*; @Fork(value = 3) public class SegmentBulkMismatch { - @Param({"2", "3", "4", "5", "6", "7", "8", "64", "512", - "4096", "32768", "262144", "2097152", "16777216", "134217728"}) - public int ELEM_SIZE; + @Param({"2", "4", "8", "12", "16", "64", "512", "4096", "32768", "262144", "2097152", "16777216", "134217728"}) + public int size; - MemorySegment srcNative; - MemorySegment dstNative; - byte[] srcArray; - byte[] dstArray; - MemorySegment srcHeap; - MemorySegment dstHeap; + public static class Array extends SegmentBulkMismatch { - @Setup - public void setup() { - // Always use the same alignment regardless of size - srcNative = Arena.ofAuto().allocate(ELEM_SIZE,16); - dstNative = Arena.ofAuto().allocate(ELEM_SIZE, 16); - var rnd = new Random(42); - for (int i = 0; i < ELEM_SIZE; i++) { - srcNative.set(JAVA_BYTE, i, (byte) rnd.nextInt(Byte.MIN_VALUE, Byte.MAX_VALUE)); + byte[] srcArray; + byte[] dstArray; + + @Setup + public void setup() { + srcArray = new byte[size]; + var rnd = new Random(42); + rnd.nextBytes(srcArray); + dstArray = Arrays.copyOf(srcArray, size); } - dstNative.copyFrom(srcNative); - srcArray = srcNative.toArray(JAVA_BYTE); - dstArray = dstNative.toArray(JAVA_BYTE); - srcHeap = MemorySegment.ofArray(srcArray); - dstHeap = MemorySegment.ofArray(dstArray); + + @Benchmark + public long array() { + return Arrays.mismatch(srcArray, dstArray); + } + } - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.mismatch=31"}) - @Benchmark - public long nativeSegmentJava() { - return srcNative.mismatch(dstNative); - } + public static class Segment extends SegmentBulkMismatch { - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.mismatch=31"}) - @Benchmark - public long heapSegmentJava() { - return srcHeap.mismatch(dstHeap); - } + enum SegmentType {HEAP, NATIVE} + enum Alignment {ALIGNED, UNALIGNED} - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.mismatch=0"}) - @Benchmark - public long nativeSegmentUnsafe() { - return srcNative.mismatch(dstNative); - } + @Param({"HEAP", "NATIVE"}) + String segmentType; - @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.mismatch=0"}) - @Benchmark - public long heapSegmentUnsafe() { - return srcHeap.mismatch(dstHeap); - } + @Param({"ALIGNED", "UNALIGNED"}) + String alignment; + + MemorySegment srcSegment; + MemorySegment dstSegment; + + @Setup + public void setup() { + // A long array is likely to be aligned at 8-byte boundaries + long[] baseArray; + + baseArray = new long[size / Long.BYTES + 1]; + var rnd = new Random(42); + for (int i = 0; i < baseArray.length; i++) { + baseArray[i] = rnd.nextLong(); + } + + switch (SegmentType.valueOf(segmentType)) { + case HEAP -> { + srcSegment = MemorySegment.ofArray(baseArray); + dstSegment = MemorySegment.ofArray(baseArray.clone()); + } + case NATIVE -> { + var s = MemorySegment.ofArray(baseArray); + srcSegment = Arena.ofAuto().allocateFrom(JAVA_LONG, baseArray); + dstSegment = Arena.ofAuto().allocateFrom(JAVA_LONG, baseArray); + } + } + switch (Alignment.valueOf(alignment)) { + case ALIGNED -> { + srcSegment = srcSegment.asSlice(0, size); + dstSegment = dstSegment.asSlice(0, size); + } + case UNALIGNED -> { + srcSegment = srcSegment.asSlice(1, size); + dstSegment = dstSegment.asSlice(1, size); + } + } + } + + @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.mismatch=31"}) + @Benchmark + public long mismatch() { + return srcSegment.mismatch(dstSegment); + } + + @Fork(value = 3, jvmArgs = {"-Djava.lang.foreign.native.threshold.power.mismatch=0"}) + @Benchmark + public long mismatchUnsafe() { + return srcSegment.mismatch(dstSegment); + } + + @Benchmark + public long mismatchLoopIntInt() { + // Simplified version that assumes the segments are of equal size + for (int i = 0; i < (int)srcSegment.byteSize(); i++) { + if (srcSegment.get(ValueLayout.JAVA_BYTE, i) != dstSegment.get(ValueLayout.JAVA_BYTE, i)) { + return i; + } + } + return -1; + } + + @Benchmark + public long mismatchLoopIntLong() { + // Simplified version that assumes the segments are of equal size + for (int i = 0; i < srcSegment.byteSize(); i++) { + if (srcSegment.get(ValueLayout.JAVA_BYTE, i) != dstSegment.get(ValueLayout.JAVA_BYTE, i)) { + return i; + } + } + return -1; + } + + @Benchmark + public long mismatchLoopLongLong() { + // Simplified version that assumes the segments are of equal size + for (long i = 0; i < srcSegment.byteSize(); i++) { + if (srcSegment.get(ValueLayout.JAVA_BYTE, i) != dstSegment.get(ValueLayout.JAVA_BYTE, i)) { + return i; + } + } + return -1; + } - @Benchmark - public long array() { - return Arrays.mismatch(srcArray, dstArray); } }