8371571: Consolidate and enhance bulk memory segment ops benchmarks

Reviewed-by: jvernee
This commit is contained in:
Per Minborg 2025-11-26 15:11:10 +00:00
parent e3a085581b
commit 1ce2a44e9f
5 changed files with 396 additions and 421 deletions

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}