mirror of
https://github.com/MarginaliaSearch/MarginaliaSearch.git
synced 2025-10-05 21:22:39 +02:00
(native) Add fallbacks and configuration options for building on systems lacking liburing
This commit is contained in:
@@ -3,18 +3,20 @@
|
||||
CXXFLAGS=-O3 -march=native -std=c++14 -fPIC `pkg-config --cflags liburing`
|
||||
LDFLAGS=
|
||||
|
||||
# Weird hack to get liburing to link on one particular debian server
|
||||
LIBURING_PATH=`pkg-config liburing --keep-system-libs --libs-only-L | cut -c 3- | tr -d \ `/liburing.so
|
||||
# Weird hack to get liburing to link in prod
|
||||
# don't @ me
|
||||
LIBURING_PATH=`./findliburing.sh`
|
||||
|
||||
CXX=c++
|
||||
|
||||
HEADERS=src/config.h
|
||||
SOURCES=src/sort.cc src/unix.cc src/uring.cc
|
||||
|
||||
all: resources/libcpp.so
|
||||
|
||||
resources/libcpp.so: ${SOURCES} resources/liburing.so
|
||||
resources/libcpp.so: ${SOURCES} ${HEADERS} resources/liburing.so
|
||||
${CXX} -shared ${CXXFLAGS} ${SOURCES} resources/liburing.so -o resources/libcpp.so
|
||||
resources/liburing.so:
|
||||
resources/liburing.so: findliburing.sh
|
||||
cp ${LIBURING_PATH} resources/liburing.so
|
||||
clean:
|
||||
rm -rf resources/{libcpp,liburing}.so
|
@@ -23,7 +23,7 @@ apply from: "$rootProject.projectDir/srcsets.gradle"
|
||||
// with a shellscript as gradle's c++ tasks are kind of insufferable
|
||||
|
||||
tasks.register('compileCpp', Exec) {
|
||||
inputs.files('Makefile', 'src/sort.cc', 'src/unix.cc', 'src/uring.cc')
|
||||
inputs.files('Makefile', 'src/sort.cc', 'src/unix.cc', 'src/uring.cc', 'src/config.h')
|
||||
outputs.files('resources/libcpp.so', 'resources/liburing.so')
|
||||
|
||||
commandLine 'make', 'all'
|
||||
|
10
code/libraries/native/findliburing.sh
Executable file
10
code/libraries/native/findliburing.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
# Compatibility shim with the linux deployment used in production which *will* not link to liburing using
|
||||
# any sort of sane compiler or pkg-config flags
|
||||
|
||||
set -e
|
||||
|
||||
URING_PATH=$(pkg-config liburing --keep-system-libs --libs-only-L | cut -c 3- | tr -d \ )
|
||||
if [ ! -z "${URING_PATH}" ]; then
|
||||
echo ${URING_PATH}/liburing.so
|
||||
fi
|
@@ -21,12 +21,16 @@ import static java.lang.foreign.ValueLayout.*;
|
||||
* isAvailable will be false. This flag must be checked before calling
|
||||
* any of the native functions.
|
||||
* */
|
||||
@SuppressWarnings("preview")
|
||||
public class IoUring {
|
||||
|
||||
private static boolean useIoUring = !Boolean.getBoolean("system.disableIoUring");
|
||||
|
||||
public final MethodHandle uringInit;
|
||||
public final MethodHandle uringClose;
|
||||
private final MethodHandle uringReadBuffered;
|
||||
private final MethodHandle uringReadDirect;
|
||||
private final MethodHandle uringReadSubstitute;
|
||||
|
||||
public final MethodHandle uringReadAndPoll;
|
||||
public final MethodHandle uringJustPoll;
|
||||
|
||||
@@ -40,39 +44,67 @@ public class IoUring {
|
||||
private IoUring(Path libFile) {
|
||||
SymbolLookup libraryLookup = SymbolLookup.libraryLookup(libFile, Arena.global());
|
||||
var nativeLinker = Linker.nativeLinker();
|
||||
MemorySegment handle = libraryLookup.findOrThrow("uring_read_buffered");
|
||||
uringReadBuffered = nativeLinker.downcallHandle(handle, FunctionDescriptor.of(JAVA_INT, JAVA_INT, ADDRESS, JAVA_INT, ADDRESS, ADDRESS, ADDRESS));
|
||||
MemorySegment handle;
|
||||
|
||||
handle = libraryLookup.findOrThrow("uring_read_direct");
|
||||
uringReadDirect = nativeLinker.downcallHandle(handle, FunctionDescriptor.of(JAVA_INT, JAVA_INT, ADDRESS, JAVA_INT, ADDRESS, ADDRESS, ADDRESS));
|
||||
useIoUring = useIoUring && libraryLookup.find("initialize_uring").isPresent();
|
||||
if (useIoUring) {
|
||||
System.err.println("io_uring enabled");
|
||||
}
|
||||
else {
|
||||
System.err.println("io_uring disabled");
|
||||
}
|
||||
|
||||
handle = libraryLookup.findOrThrow("uring_read_submit_and_poll");
|
||||
|
||||
uringReadAndPoll = nativeLinker.downcallHandle(handle, FunctionDescriptor.of(
|
||||
JAVA_INT,
|
||||
ADDRESS, // io_uring* ring
|
||||
ADDRESS, // long* result_ids
|
||||
JAVA_INT, // int in_flight_requests
|
||||
JAVA_INT, // int read_count
|
||||
ADDRESS, // long* read_batch_ids
|
||||
ADDRESS, // int* read_fds
|
||||
ADDRESS, // void** read_buffers
|
||||
ADDRESS, // unsigned int** read_sizes
|
||||
ADDRESS // long* read_offsets
|
||||
));
|
||||
handle = libraryLookup.findOrThrow("uring_poll");
|
||||
if (useIoUring) {
|
||||
|
||||
uringJustPoll = nativeLinker.downcallHandle(handle, FunctionDescriptor.of(
|
||||
JAVA_INT,
|
||||
ADDRESS, // io_uring* ring
|
||||
ADDRESS // long* result_ids
|
||||
));
|
||||
handle = libraryLookup.findOrThrow("uring_read_buffered");
|
||||
uringReadBuffered = nativeLinker.downcallHandle(handle, FunctionDescriptor.of(JAVA_INT, JAVA_INT, ADDRESS, JAVA_INT, ADDRESS, ADDRESS, ADDRESS));
|
||||
|
||||
handle = libraryLookup.findOrThrow("initialize_uring");
|
||||
uringInit = nativeLinker.downcallHandle(handle, FunctionDescriptor.of(ADDRESS, JAVA_INT));
|
||||
handle = libraryLookup.findOrThrow("uring_read_direct");
|
||||
uringReadDirect = nativeLinker.downcallHandle(handle, FunctionDescriptor.of(JAVA_INT, JAVA_INT, ADDRESS, JAVA_INT, ADDRESS, ADDRESS, ADDRESS));
|
||||
|
||||
handle = libraryLookup.findOrThrow("close_uring");
|
||||
uringClose = nativeLinker.downcallHandle(handle, FunctionDescriptor.ofVoid(ADDRESS));
|
||||
handle = libraryLookup.findOrThrow("uring_read_submit_and_poll");
|
||||
|
||||
uringReadAndPoll = nativeLinker.downcallHandle(handle, FunctionDescriptor.of(
|
||||
JAVA_INT,
|
||||
ADDRESS, // io_uring* ring
|
||||
ADDRESS, // long* result_ids
|
||||
JAVA_INT, // int in_flight_requests
|
||||
JAVA_INT, // int read_count
|
||||
ADDRESS, // long* read_batch_ids
|
||||
ADDRESS, // int* read_fds
|
||||
ADDRESS, // void** read_buffers
|
||||
ADDRESS, // unsigned int** read_sizes
|
||||
ADDRESS // long* read_offsets
|
||||
));
|
||||
handle = libraryLookup.findOrThrow("uring_poll");
|
||||
|
||||
uringJustPoll = nativeLinker.downcallHandle(handle, FunctionDescriptor.of(
|
||||
JAVA_INT,
|
||||
ADDRESS, // io_uring* ring
|
||||
ADDRESS // long* result_ids
|
||||
));
|
||||
|
||||
handle = libraryLookup.findOrThrow("initialize_uring");
|
||||
uringInit = nativeLinker.downcallHandle(handle, FunctionDescriptor.of(ADDRESS, JAVA_INT));
|
||||
|
||||
handle = libraryLookup.findOrThrow("close_uring");
|
||||
uringClose = nativeLinker.downcallHandle(handle, FunctionDescriptor.ofVoid(ADDRESS));
|
||||
|
||||
handle = libraryLookup.findOrThrow("substitute_uring_read");
|
||||
uringReadSubstitute = nativeLinker.downcallHandle(handle, FunctionDescriptor.of(JAVA_INT, JAVA_INT, JAVA_INT, ADDRESS, ADDRESS, ADDRESS));
|
||||
}
|
||||
else {
|
||||
uringInit = null;
|
||||
uringClose = null;
|
||||
uringJustPoll = null;
|
||||
uringReadAndPoll = null;
|
||||
uringReadDirect = null;
|
||||
uringReadBuffered = null;
|
||||
|
||||
handle = libraryLookup.findOrThrow("substitute_uring_read");
|
||||
uringReadSubstitute = nativeLinker.downcallHandle(handle, FunctionDescriptor.of(JAVA_INT, JAVA_INT, JAVA_INT, ADDRESS, ADDRESS, ADDRESS));
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
@@ -119,20 +151,25 @@ public class IoUring {
|
||||
}
|
||||
|
||||
public static UringQueue uringOpen(int fd, int queueSize) {
|
||||
try {
|
||||
return new UringQueue((MemorySegment) instance.uringInit.invoke(queueSize), fd);
|
||||
if (useIoUring) {
|
||||
try {
|
||||
return new UringQueue((MemorySegment) instance.uringInit.invoke(queueSize), fd);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Failed to invoke native function", t);
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw new RuntimeException("Failed to invoke native function", t);
|
||||
else {
|
||||
return new UringQueue(null, fd);
|
||||
}
|
||||
}
|
||||
|
||||
public static void uringClose(UringQueue ring) {
|
||||
try {
|
||||
instance.uringClose.invoke(ring.pointer());
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw new RuntimeException("Failed to invoke native function", t);
|
||||
if (useIoUring) {
|
||||
try {
|
||||
instance.uringClose.invoke(ring.pointer());
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException("Failed to invoke native function", t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,11 +177,7 @@ public class IoUring {
|
||||
if (offsets.isEmpty()) {
|
||||
throw new IllegalArgumentException("Empty offset list in uringRead");
|
||||
}
|
||||
if (offsets.size() == 1) {
|
||||
if (LinuxSystemCalls.readAt(fd, dest.getFirst(), offsets.getFirst()) > 0)
|
||||
return 1;
|
||||
else return -1;
|
||||
}
|
||||
|
||||
try {
|
||||
MemorySegment bufferList = Arena.ofAuto().allocate(8L * offsets.size(), 8);
|
||||
MemorySegment sizeList = Arena.ofAuto().allocate(4L * offsets.size(), 8);
|
||||
@@ -160,11 +193,19 @@ public class IoUring {
|
||||
sizeList.setAtIndex(JAVA_INT, i, (int) buffer.byteSize());
|
||||
offsetList.setAtIndex(JAVA_LONG, i, offsets.get(i));
|
||||
}
|
||||
if (direct) {
|
||||
return (Integer) instance.uringReadDirect.invoke(fd, ring.pointer(), dest.size(), bufferList, sizeList, offsetList);
|
||||
|
||||
if (useIoUring
|
||||
&& offsets.size() > 5) // fall back to sequential pread operations if the list is too small
|
||||
{
|
||||
if (direct) {
|
||||
return (Integer) instance.uringReadDirect.invoke(fd, ring.pointer(), dest.size(), bufferList, sizeList, offsetList);
|
||||
} else {
|
||||
return (Integer) instance.uringReadBuffered.invoke(fd, ring.pointer(), dest.size(), bufferList, sizeList, offsetList);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return (Integer) instance.uringReadBuffered.invoke(fd, ring.pointer(), dest.size(), bufferList, sizeList, offsetList);
|
||||
// do a sequential pread operation as a fallback
|
||||
return (Integer) instance.uringReadSubstitute.invoke(fd, dest.size(), bufferList, sizeList, offsetList);
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
|
@@ -18,7 +18,6 @@ import static java.lang.foreign.ValueLayout.*;
|
||||
* isAvailable will be false. This flag must be checked before calling
|
||||
* any of the native functions.
|
||||
* */
|
||||
@SuppressWarnings("preview")
|
||||
public class LinuxSystemCalls {
|
||||
private final MethodHandle openDirect;
|
||||
private final MethodHandle openBuffered;
|
||||
|
@@ -24,7 +24,6 @@ import static java.lang.foreign.ValueLayout.JAVA_LONG;
|
||||
* isAvailable will be false. This flag must be checked before calling
|
||||
* any of the native functions.
|
||||
* */
|
||||
@SuppressWarnings("preview")
|
||||
public class NativeAlgos {
|
||||
private final MethodHandle qsortHandle;
|
||||
private final MethodHandle qsort128Handle;
|
||||
|
4
code/libraries/native/src/config.h
Normal file
4
code/libraries/native/src/config.h
Normal file
@@ -0,0 +1,4 @@
|
||||
// Add a line like this to disable io_uring and fall back to sequential pread operations
|
||||
// for e.g. development on non-Linux OS:es
|
||||
|
||||
// #define NO_IO_URING
|
@@ -1,11 +1,18 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#ifndef NO_IO_URING
|
||||
#include <liburing.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
#ifndef NO_IO_URING
|
||||
|
||||
io_uring* initialize_uring(int queue_size) {
|
||||
io_uring* ring = (io_uring*) malloc(sizeof(io_uring));
|
||||
if (!ring) return NULL;
|
||||
@@ -231,4 +238,23 @@ int uring_read_direct(int fd, io_uring* ring, int n, void** buffers, unsigned in
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
int substitute_uring_read(int fd, int n, void** buffers, unsigned int* sizes, long* offsets) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
int rv = pread(fd, buffers[i], sizes[i], offsets[i]);
|
||||
if (rv == -1) {
|
||||
perror("pread");
|
||||
return -1;
|
||||
}
|
||||
if (rv != sizes[i]) {
|
||||
fprintf(stderr, "Unexpected number of bytes read");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
}
|
@@ -27,13 +27,13 @@ class UringQueueTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSunnyDay() throws IOException {
|
||||
public void testSunnyDay() {
|
||||
int fd = LinuxSystemCalls.openBuffered(file);
|
||||
|
||||
List<MemorySegment> segments = new ArrayList<>();
|
||||
List<Long> offsets = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
segments.add(Arena.ofAuto().allocate(32));
|
||||
offsets.add(32L*i);
|
||||
}
|
||||
|
Reference in New Issue
Block a user