mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8371571: Consolidate and enhance bulk memory segment ops benchmarks
Reviewed-by: jvernee
This commit is contained in:
parent
e3a085581b
commit
1ce2a44e9f
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user