Skip to content

Commit 043b195

Browse files
committed
FFM-based Iterator benchmark.
1 parent d7ba976 commit 043b195

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

src/main/c++/iterators/NativeIterator.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2626
*/
2727
#include <jni.h>
28+
29+
#include <algorithm>
30+
#include <cstring>
31+
#include <stdexcept>
32+
2833
#include "Iterator.h"
2934

3035
#ifdef __cplusplus
@@ -89,6 +94,32 @@ JNIEXPORT void JNICALL Java_com_evolvedbinary_jnibench_common_iterators_NativeIt
8994
delete iterator;
9095
}
9196

97+
extern "C" void* iterator_create(int num_elements, size_t element_size) {
98+
return new Iterator(num_elements, element_size);
99+
}
100+
101+
extern "C" int iterator_has_next(void* handle) {
102+
auto* iterator = reinterpret_cast<Iterator*>(handle);
103+
return iterator->hasNext() ? 1 : 0;
104+
}
105+
106+
extern "C" int iterator_next(void* handle, char* dest, int dest_len) {
107+
auto* iterator = reinterpret_cast<Iterator*>(handle);
108+
try {
109+
const auto& data = iterator->next();
110+
const int size = std::min(static_cast<int>(data.size()), dest_len);
111+
std::memcpy(dest, data.data(), static_cast<size_t>(size));
112+
return size;
113+
} catch (const std::out_of_range&) {
114+
return -1;
115+
}
116+
}
117+
118+
extern "C" void iterator_dispose(void* handle) {
119+
auto* iterator = reinterpret_cast<Iterator*>(handle);
120+
delete iterator;
121+
}
122+
92123
#ifdef __cplusplus
93124
}
94125
#endif
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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.infra.Blackhole;
12+
13+
public class IteratorBenchmarkJavaFfm extends IteratorBenchmark {
14+
private static final MethodHandle ITERATOR_CREATE_HANDLE;
15+
private static final MethodHandle ITERATOR_HAS_NEXT_HANDLE;
16+
private static final MethodHandle ITERATOR_NEXT_HANDLE;
17+
private static final MethodHandle ITERATOR_DISPOSE_HANDLE;
18+
19+
static {
20+
try {
21+
Linker linker = Linker.nativeLinker();
22+
SymbolLookup symbolLookup = SymbolLookup.loaderLookup();
23+
24+
ITERATOR_CREATE_HANDLE = findHandle(symbolLookup, linker, "iterator_create",
25+
FunctionDescriptor.of(ValueLayout.ADDRESS, ValueLayout.JAVA_INT,
26+
ValueLayout.JAVA_LONG));
27+
ITERATOR_HAS_NEXT_HANDLE = findHandle(symbolLookup, linker, "iterator_has_next",
28+
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS));
29+
ITERATOR_NEXT_HANDLE = findHandle(symbolLookup, linker, "iterator_next",
30+
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.ADDRESS,
31+
ValueLayout.ADDRESS, ValueLayout.JAVA_INT));
32+
ITERATOR_DISPOSE_HANDLE = findHandle(symbolLookup, linker, "iterator_dispose",
33+
FunctionDescriptor.ofVoid(ValueLayout.ADDRESS));
34+
} catch (Exception e) {
35+
System.err.println("FFM Setup failed: " + e.getMessage());
36+
throw new RuntimeException(e);
37+
}
38+
}
39+
40+
private static MethodHandle findHandle(SymbolLookup lookup, Linker linker, String symbol,
41+
FunctionDescriptor descriptor) {
42+
var symbolOpt = lookup.find(symbol)
43+
.or(() -> lookup.find("_" + symbol)); // macOS prefix
44+
return symbolOpt.map(memorySegment -> linker.downcallHandle(memorySegment, descriptor))
45+
.orElse(null);
46+
}
47+
48+
@Benchmark
49+
public void iterateFfm(BenchmarkState benchmarkState, Blackhole blackhole) throws Throwable {
50+
if (ITERATOR_CREATE_HANDLE == null || ITERATOR_HAS_NEXT_HANDLE == null
51+
|| ITERATOR_NEXT_HANDLE == null || ITERATOR_DISPOSE_HANDLE == null) {
52+
throw new RuntimeException("FFM not available");
53+
}
54+
55+
MemorySegment iteratorHandle = null;
56+
try (Arena arena = Arena.ofConfined()) {
57+
iteratorHandle = (MemorySegment) ITERATOR_CREATE_HANDLE.invokeExact(benchmarkState.numElements,
58+
benchmarkState.elementSize);
59+
MemorySegment buffer = arena.allocate(benchmarkState.elementSize);
60+
int bufferSize = Math.toIntExact(buffer.byteSize());
61+
62+
while (((int) ITERATOR_HAS_NEXT_HANDLE.invokeExact(iteratorHandle)) != 0) {
63+
int bytesRead = (int) ITERATOR_NEXT_HANDLE.invokeExact(iteratorHandle, buffer, bufferSize);
64+
if (bytesRead < 0) {
65+
throw new IllegalStateException("Iterator exhausted unexpectedly");
66+
}
67+
byte[] result = new byte[bytesRead];
68+
MemorySegment.copy(buffer, ValueLayout.JAVA_BYTE, 0, result, 0, bytesRead);
69+
blackhole.consume(result);
70+
}
71+
} finally {
72+
if (iteratorHandle != null) {
73+
ITERATOR_DISPOSE_HANDLE.invokeExact(iteratorHandle);
74+
}
75+
}
76+
}
77+
}

0 commit comments

Comments
 (0)