Skip to content

Commit 9a0eeac

Browse files
committed
ArrayToNative Java 21/25
1 parent 2ede033 commit 9a0eeac

5 files changed

Lines changed: 299 additions & 0 deletions

File tree

jmh_tiny_array_to_native.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"benchmark": "ByteArrayToNativeBenchmark",
3+
"jvmargs": ["Xmx4G", "XX:ErrorFile=./results/hs_err_pid%p.log", "XX:+HeapDumpOnOutOfMemoryError"],
4+
"params": {
5+
"valueSize": [50, 1024, 4096, 16384],
6+
"cacheMB": [1],
7+
"checksum": ["none", "copyout"]
8+
},
9+
"options": {
10+
"batchsize": 1,
11+
"warmupiterations": 5,
12+
"warmuptime": "10ms",
13+
"iterations": 5,
14+
"time": "50ms"
15+
},
16+
"result.path": "./results",
17+
"java.library.path": "target/jni-benchmarks-1.0.1-SNAPSHOT-application/jni-benchmarks-1.0.1-SNAPSHOT/lib",
18+
"jar": "target/jni-benchmarks-1.0.1-SNAPSHOT-benchmarks.nar"
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"benchmark": "ByteArrayToNativeBenchmarkJava21",
3+
"jvmargs": ["Xmx4G", "XX:ErrorFile=./results/hs_err_pid%p.log", "XX:+HeapDumpOnOutOfMemoryError", "-enable-preview"],
4+
"params": {
5+
"valueSize": [50, 1024, 4096, 16384],
6+
"cacheMB": [1],
7+
"checksum": ["none", "copyout"]
8+
},
9+
"options": {
10+
"batchsize": 1,
11+
"warmupiterations": 5,
12+
"warmuptime": "10ms",
13+
"iterations": 5,
14+
"time": "50ms"
15+
},
16+
"result.path": "./results",
17+
"java.library.path": "target/jni-benchmarks-1.0.1-SNAPSHOT-application/jni-benchmarks-1.0.1-SNAPSHOT/lib",
18+
"jar": "target/jni-benchmarks-1.0.1-SNAPSHOT-benchmarks.nar"
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"benchmark": "ByteArrayToNativeBenchmarkJava25",
3+
"jvmargs": ["Xmx4G", "XX:ErrorFile=./results/hs_err_pid%p.log", "XX:+HeapDumpOnOutOfMemoryError", "-enable-preview"],
4+
"params": {
5+
"valueSize": [50, 1024, 4096, 16384],
6+
"cacheMB": [1],
7+
"checksum": ["none", "copyout"]
8+
},
9+
"options": {
10+
"batchsize": 1,
11+
"warmupiterations": 5,
12+
"warmuptime": "10ms",
13+
"iterations": 5,
14+
"time": "50ms"
15+
},
16+
"result.path": "./results",
17+
"java.library.path": "target/jni-benchmarks-1.0.1-SNAPSHOT-application/jni-benchmarks-1.0.1-SNAPSHOT/lib",
18+
"jar": "target/jni-benchmarks-1.0.1-SNAPSHOT-benchmarks.nar"
19+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package com.evolvedbinary.jnibench.jmhbench;
2+
3+
import java.lang.foreign.Arena;
4+
import java.lang.foreign.FunctionDescriptor;
5+
import java.lang.foreign.Linker;
6+
import java.lang.foreign.MemorySegment;
7+
import java.lang.foreign.SymbolLookup;
8+
import java.lang.foreign.ValueLayout;
9+
import java.lang.invoke.MethodHandle;
10+
import org.openjdk.jmh.annotations.Benchmark;
11+
import org.openjdk.jmh.annotations.Scope;
12+
import org.openjdk.jmh.annotations.Setup;
13+
import org.openjdk.jmh.annotations.State;
14+
import org.openjdk.jmh.annotations.TearDown;
15+
16+
public class ByteArrayToNativeBenchmarkJava21 extends ByteArrayToNativeBenchmark {
17+
private static final MethodHandle ffmGetByteArrayHandle;
18+
19+
static {
20+
try {
21+
Linker linker = Linker.nativeLinker();
22+
SymbolLookup symbolLookup = SymbolLookup.loaderLookup();
23+
24+
var symbolOpt = symbolLookup.find("GetByteArray_getFFM")
25+
.or(() -> symbolLookup.find("_GetByteArray_getFFM")) // macOS prefix
26+
.or(() -> symbolLookup.find("GetByteArray_getFFM@28")); // Windows mangled name
27+
28+
if (symbolOpt.isPresent()) {
29+
FunctionDescriptor ffmDescriptor = FunctionDescriptor.of(
30+
ValueLayout.JAVA_INT,
31+
ValueLayout.ADDRESS,
32+
ValueLayout.JAVA_INT,
33+
ValueLayout.JAVA_INT,
34+
ValueLayout.ADDRESS,
35+
ValueLayout.JAVA_INT,
36+
ValueLayout.JAVA_INT
37+
);
38+
39+
ffmGetByteArrayHandle = linker.downcallHandle(
40+
symbolOpt.get(),
41+
ffmDescriptor
42+
);
43+
} else {
44+
ffmGetByteArrayHandle = null;
45+
}
46+
} catch (Exception e) {
47+
System.err.println("FFM Setup failed: " + e.getMessage());
48+
throw new RuntimeException(e);
49+
}
50+
}
51+
52+
@State(Scope.Benchmark)
53+
public static class BenchmarkStateJava21 extends BenchmarkState {
54+
public MemorySegment ffmKeySegment;
55+
public MemorySegment ffmValueSegment;
56+
private Arena benchmarkArena;
57+
58+
@Setup
59+
public void setup() {
60+
super.setup();
61+
benchmarkArena = Arena.ofShared();
62+
ffmKeySegment = benchmarkArena.allocateArray(ValueLayout.JAVA_BYTE, keyBytes);
63+
ffmValueSegment = benchmarkArena.allocate(keySize);
64+
}
65+
66+
@TearDown
67+
public void tearDown() {
68+
super.tearDown();
69+
if (benchmarkArena != null) {
70+
benchmarkArena.close();
71+
}
72+
}
73+
}
74+
75+
@Benchmark
76+
public byte[] passKeyAsFFM(BenchmarkStateJava21 benchmarkState) throws Throwable {
77+
if (ffmGetByteArrayHandle == null) {
78+
throw new RuntimeException("FFM not available");
79+
}
80+
81+
try (Arena arena = Arena.ofConfined()) {
82+
final var keySegment = arena.allocateArray(ValueLayout.JAVA_BYTE, benchmarkState.keyBytes);
83+
final var valueSegment = arena.allocate(benchmarkState.keySize);
84+
85+
int bytesRead = (int) ffmGetByteArrayHandle.invoke(
86+
keySegment,
87+
0,
88+
benchmarkState.keyBytes.length,
89+
valueSegment,
90+
0,
91+
benchmarkState.keySize
92+
);
93+
94+
byte[] result = new byte[bytesRead];
95+
MemorySegment.copy(valueSegment, ValueLayout.JAVA_BYTE, 0, result, 0, bytesRead);
96+
97+
return result;
98+
}
99+
}
100+
101+
@Benchmark
102+
public byte[] passKeyAsFFMPreallocated(BenchmarkStateJava21 benchmarkState) throws Throwable {
103+
if (ffmGetByteArrayHandle == null) {
104+
throw new RuntimeException("FFM not available");
105+
}
106+
107+
int bytesRead = (int) ffmGetByteArrayHandle.invoke(
108+
benchmarkState.ffmKeySegment,
109+
0,
110+
benchmarkState.keyBytes.length,
111+
benchmarkState.ffmValueSegment,
112+
0,
113+
benchmarkState.keySize
114+
);
115+
116+
byte[] result = new byte[bytesRead];
117+
MemorySegment.copy(benchmarkState.ffmValueSegment, ValueLayout.JAVA_BYTE, 0, result, 0, bytesRead);
118+
119+
return result;
120+
}
121+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package com.evolvedbinary.jnibench.jmhbench;
2+
3+
import java.lang.foreign.Arena;
4+
import java.lang.foreign.FunctionDescriptor;
5+
import java.lang.foreign.Linker;
6+
import java.lang.foreign.MemorySegment;
7+
import java.lang.foreign.SymbolLookup;
8+
import java.lang.foreign.ValueLayout;
9+
import java.lang.invoke.MethodHandle;
10+
import org.openjdk.jmh.annotations.Benchmark;
11+
import org.openjdk.jmh.annotations.Scope;
12+
import org.openjdk.jmh.annotations.Setup;
13+
import org.openjdk.jmh.annotations.State;
14+
import org.openjdk.jmh.annotations.TearDown;
15+
16+
public class ByteArrayToNativeBenchmarkJava25 extends ByteArrayToNativeBenchmark {
17+
private static final MethodHandle ffmGetByteArrayHandle;
18+
19+
static {
20+
try {
21+
Linker linker = Linker.nativeLinker();
22+
SymbolLookup symbolLookup = SymbolLookup.loaderLookup();
23+
24+
var symbolOpt = symbolLookup.find("GetByteArray_getFFM")
25+
.or(() -> symbolLookup.find("_GetByteArray_getFFM")) // macOS prefix
26+
.or(() -> symbolLookup.find("GetByteArray_getFFM@28")); // Windows mangled name
27+
28+
if (symbolOpt.isPresent()) {
29+
FunctionDescriptor ffmDescriptor = FunctionDescriptor.of(
30+
ValueLayout.JAVA_INT,
31+
ValueLayout.ADDRESS,
32+
ValueLayout.JAVA_INT,
33+
ValueLayout.JAVA_INT,
34+
ValueLayout.ADDRESS,
35+
ValueLayout.JAVA_INT,
36+
ValueLayout.JAVA_INT
37+
);
38+
39+
ffmGetByteArrayHandle = linker.downcallHandle(
40+
symbolOpt.get(),
41+
ffmDescriptor
42+
);
43+
} else {
44+
ffmGetByteArrayHandle = null;
45+
}
46+
} catch (Exception e) {
47+
System.err.println("FFM Setup failed: " + e.getMessage());
48+
throw new RuntimeException(e);
49+
}
50+
}
51+
52+
@State(Scope.Benchmark)
53+
public static class BenchmarkStateJava25 extends BenchmarkState {
54+
public MemorySegment ffmKeySegment;
55+
public MemorySegment ffmValueSegment;
56+
private Arena benchmarkArena;
57+
58+
@Setup
59+
public void setup() {
60+
super.setup();
61+
benchmarkArena = Arena.ofShared();
62+
ffmKeySegment = benchmarkArena.allocateFrom(ValueLayout.JAVA_BYTE, keyBytes);
63+
ffmValueSegment = benchmarkArena.allocate(keySize);
64+
}
65+
66+
@TearDown
67+
public void tearDown() {
68+
super.tearDown();
69+
if (benchmarkArena != null) {
70+
benchmarkArena.close();
71+
}
72+
}
73+
}
74+
75+
@Benchmark
76+
public byte[] passKeyAsFFM(BenchmarkStateJava25 benchmarkState) throws Throwable {
77+
if (ffmGetByteArrayHandle == null) {
78+
throw new RuntimeException("FFM not available");
79+
}
80+
81+
try (Arena arena = Arena.ofConfined()) {
82+
final var keySegment = arena.allocateFrom(ValueLayout.JAVA_BYTE, benchmarkState.keyBytes);
83+
final var valueSegment = arena.allocate(benchmarkState.keySize);
84+
85+
int bytesRead = (int) ffmGetByteArrayHandle.invoke(
86+
keySegment,
87+
0,
88+
benchmarkState.keyBytes.length,
89+
valueSegment,
90+
0,
91+
benchmarkState.keySize
92+
);
93+
94+
byte[] result = new byte[bytesRead];
95+
MemorySegment.copy(valueSegment, ValueLayout.JAVA_BYTE, 0, result, 0, bytesRead);
96+
97+
return result;
98+
}
99+
}
100+
101+
@Benchmark
102+
public byte[] passKeyAsFFMPreallocated(BenchmarkStateJava25 benchmarkState) throws Throwable {
103+
if (ffmGetByteArrayHandle == null) {
104+
throw new RuntimeException("FFM not available");
105+
}
106+
107+
int bytesRead = (int) ffmGetByteArrayHandle.invoke(
108+
benchmarkState.ffmKeySegment,
109+
0,
110+
benchmarkState.keyBytes.length,
111+
benchmarkState.ffmValueSegment,
112+
0,
113+
benchmarkState.keySize
114+
);
115+
116+
byte[] result = new byte[bytesRead];
117+
MemorySegment.copy(benchmarkState.ffmValueSegment, ValueLayout.JAVA_BYTE, 0, result, 0, bytesRead);
118+
119+
return result;
120+
}
121+
}

0 commit comments

Comments
 (0)