mirror of
https://github.com/MarginaliaSearch/MarginaliaSearch.git
synced 2025-02-23 13:09:00 +00:00
(array, experimental) Call C++ helper methods to do some low level stuff a bit faster than is possible with Java
This commit is contained in:
parent
c837321df1
commit
55a7c1db00
@ -9,6 +9,7 @@ java {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
apply from: "$rootProject.projectDir/srcsets.gradle"
|
apply from: "$rootProject.projectDir/srcsets.gradle"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -20,6 +21,8 @@ dependencies {
|
|||||||
implementation libs.lz4
|
implementation libs.lz4
|
||||||
implementation libs.guava
|
implementation libs.guava
|
||||||
|
|
||||||
|
implementation project(':code:libraries:array:cpp')
|
||||||
|
|
||||||
testImplementation libs.bundles.slf4j.test
|
testImplementation libs.bundles.slf4j.test
|
||||||
testImplementation libs.bundles.junit
|
testImplementation libs.bundles.junit
|
||||||
testImplementation libs.mockito
|
testImplementation libs.mockito
|
||||||
|
26
code/libraries/array/cpp/build.gradle
Normal file
26
code/libraries/array/cpp/build.gradle
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion.set(JavaLanguageVersion.of(rootProject.ext.jvmVersion))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation libs.bundles.slf4j
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$rootProject.projectDir/srcsets.gradle"
|
||||||
|
|
||||||
|
// We use a custom task to compile the C++ code into a shared library
|
||||||
|
// with a shellscript as gradle's c++ tasks are kind of insufferable
|
||||||
|
|
||||||
|
tasks.register('compileCpp', Exec) {
|
||||||
|
inputs.files('src/main/cpp/cpphelpers.cpp', 'src/main/public/cpphelpers.h')
|
||||||
|
outputs.file 'resources/libcpp.so'
|
||||||
|
commandLine 'sh', 'compile.sh'
|
||||||
|
}
|
||||||
|
|
||||||
|
processResources.dependsOn('compileCpp')
|
8
code/libraries/array/cpp/compile.sh
Normal file
8
code/libraries/array/cpp/compile.sh
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
if ! which g++ > /dev/null; then
|
||||||
|
echo "g++ not found, skipping compilation"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
c++ -O3 -march=native -shared -fPIC -Isrc/main/public src/main/cpp/*.cpp -o resources/libcpp.so
|
133
code/libraries/array/cpp/java/nu/marginalia/NativeAlgos.java
Normal file
133
code/libraries/array/cpp/java/nu/marginalia/NativeAlgos.java
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package nu.marginalia;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.lang.foreign.*;
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
|
||||||
|
import static java.lang.foreign.ValueLayout.ADDRESS;
|
||||||
|
import static java.lang.foreign.ValueLayout.JAVA_LONG;
|
||||||
|
|
||||||
|
public class NativeAlgos {
|
||||||
|
private final MethodHandle qsortHandle;
|
||||||
|
private final MethodHandle qsort128Handle;
|
||||||
|
private final MethodHandle linearSearch64Handle;
|
||||||
|
private final MethodHandle linearSearch128Handle;
|
||||||
|
private final MethodHandle binarySearch128Handle;
|
||||||
|
private final MethodHandle binarySearch64UpperHandle;
|
||||||
|
|
||||||
|
public static final NativeAlgos instance;
|
||||||
|
public static final boolean isAvailable;
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(NativeAlgos.class);
|
||||||
|
|
||||||
|
private NativeAlgos(Path libFile) throws Exception {
|
||||||
|
var libraryLookup = SymbolLookup.libraryLookup(libFile, Arena.global());
|
||||||
|
var nativeLinker = Linker.nativeLinker();
|
||||||
|
|
||||||
|
var handle = libraryLookup.find("ms_sort_64").get();
|
||||||
|
qsortHandle = nativeLinker.downcallHandle(handle, FunctionDescriptor.ofVoid(ADDRESS, JAVA_LONG, JAVA_LONG));
|
||||||
|
|
||||||
|
handle = libraryLookup.find("ms_sort_128").get();
|
||||||
|
qsort128Handle = nativeLinker.downcallHandle(handle,
|
||||||
|
FunctionDescriptor.ofVoid(ADDRESS, JAVA_LONG, JAVA_LONG));
|
||||||
|
|
||||||
|
handle = libraryLookup.find("ms_linear_search_64").get();
|
||||||
|
linearSearch64Handle = nativeLinker.downcallHandle(handle,
|
||||||
|
FunctionDescriptor.of(JAVA_LONG, JAVA_LONG, ADDRESS, JAVA_LONG, JAVA_LONG));
|
||||||
|
|
||||||
|
handle = libraryLookup.find("ms_linear_search_128").get();
|
||||||
|
linearSearch128Handle = nativeLinker.downcallHandle(handle,
|
||||||
|
FunctionDescriptor.of(JAVA_LONG, JAVA_LONG, ADDRESS, JAVA_LONG, JAVA_LONG));
|
||||||
|
|
||||||
|
handle = libraryLookup.find("ms_binary_search_128").get();
|
||||||
|
binarySearch128Handle = nativeLinker.downcallHandle(handle,
|
||||||
|
FunctionDescriptor.of(JAVA_LONG, JAVA_LONG, ADDRESS, JAVA_LONG, JAVA_LONG));
|
||||||
|
|
||||||
|
handle = libraryLookup.find("ms_binary_search_64upper").get();
|
||||||
|
binarySearch64UpperHandle = nativeLinker.downcallHandle(handle,
|
||||||
|
FunctionDescriptor.of(JAVA_LONG, JAVA_LONG, ADDRESS, JAVA_LONG, JAVA_LONG));
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
Path libFile;
|
||||||
|
NativeAlgos nativeAlgosI = null;
|
||||||
|
// copy resource to temp file
|
||||||
|
try (var is = NativeAlgos.class.getClassLoader().getResourceAsStream("libcpp.so")) {
|
||||||
|
var tempFile = File.createTempFile("libcpp", ".so");
|
||||||
|
tempFile.deleteOnExit();
|
||||||
|
|
||||||
|
try (var os = new FileOutputStream(tempFile)) {
|
||||||
|
is.transferTo(os);
|
||||||
|
}
|
||||||
|
|
||||||
|
libFile = tempFile.toPath();
|
||||||
|
nativeAlgosI = new NativeAlgos(libFile);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = nativeAlgosI;
|
||||||
|
isAvailable = instance != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void sort(MemorySegment ms, long start, long end) {
|
||||||
|
try {
|
||||||
|
instance.qsortHandle.invoke(ms, start, end);
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
throw new RuntimeException("Failed to invoke native function", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sort128(MemorySegment ms, long start, long end) {
|
||||||
|
try {
|
||||||
|
instance.qsort128Handle.invoke(ms, start, end);
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
throw new RuntimeException("Failed to invoke native function", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long linearSearch64(long key, MemorySegment ms, long start, long end) {
|
||||||
|
try {
|
||||||
|
return (long) instance.linearSearch64Handle.invoke(key, ms, start, end);
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
throw new RuntimeException("Failed to invoke native function", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long linearSearch128(long key, MemorySegment ms, long start, long end) {
|
||||||
|
try {
|
||||||
|
return (long) instance.linearSearch128Handle.invoke(key, ms, start, end);
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
throw new RuntimeException("Failed to invoke native function", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long binarySearch128(long key, MemorySegment ms, long start, long end) {
|
||||||
|
try {
|
||||||
|
return (long) instance.binarySearch128Handle.invoke(key, ms, start, end);
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
throw new RuntimeException("Failed to invoke native function", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long binarySearch64Upper(long key, MemorySegment ms, long start, long end) {
|
||||||
|
try {
|
||||||
|
return (long) instance.binarySearch64UpperHandle.invoke(key, ms, start, end);
|
||||||
|
}
|
||||||
|
catch (Throwable t) {
|
||||||
|
throw new RuntimeException("Failed to invoke native function", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
code/libraries/array/cpp/resources/.gitignore
vendored
Normal file
1
code/libraries/array/cpp/resources/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
../../resources/cpp/libcpp.so
|
136
code/libraries/array/cpp/src/main/cpp/cpphelpers.cpp
Normal file
136
code/libraries/array/cpp/src/main/cpp/cpphelpers.cpp
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#include "cpphelpers.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
struct p2 {
|
||||||
|
int64_t a;
|
||||||
|
int64_t b;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ms_sort_64(int64_t* area, uint64_t start, uint64_t end) {
|
||||||
|
std::sort(&area[start], &area[end]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ms_sort_128(int64_t* area, uint64_t start, uint64_t end) {
|
||||||
|
struct p2 *startp = (struct p2 *) &area[start];
|
||||||
|
struct p2 *endp = (struct p2 *) &area[end];
|
||||||
|
|
||||||
|
// sort based on the first element of the pair
|
||||||
|
std::sort(startp, endp, [](struct p2& fst, struct p2& snd) {
|
||||||
|
return fst.a < snd.a;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t encodeSearchMiss64(int64_t value) {
|
||||||
|
return -1 - std::max(int64_t(0), value);
|
||||||
|
}
|
||||||
|
int64_t encodeSearchMiss128(int64_t value) {
|
||||||
|
return -2 - std::max(int64_t(0), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t decodeSearchMiss64(long value) {
|
||||||
|
return -value - 1;
|
||||||
|
}
|
||||||
|
int64_t decodeSearchMiss128(long value) {
|
||||||
|
return -value - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ms_linear_search_64(int64_t key, int64_t* area, uint64_t fromIndex, uint64_t toIndex) {
|
||||||
|
uint64_t pos = fromIndex;
|
||||||
|
for (; pos < toIndex; pos++) {
|
||||||
|
int64_t val = area[pos];
|
||||||
|
|
||||||
|
if (val == key) return pos;
|
||||||
|
if (val > key) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeSearchMiss64(pos - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ms_linear_search_128(int64_t key, int64_t* area, uint64_t fromIndex, uint64_t toIndex) {
|
||||||
|
uint64_t pos = fromIndex;
|
||||||
|
|
||||||
|
for (; pos < toIndex; pos+=2) {
|
||||||
|
int64_t val = area[pos];
|
||||||
|
|
||||||
|
if (val == key) return pos;
|
||||||
|
if (val > key) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeSearchMiss128(pos - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** long low = 0;
|
||||||
|
long high = (toIndex - fromIndex)/sz - 1;
|
||||||
|
|
||||||
|
while (high - low >= LINEAR_SEARCH_CUTOFF) {
|
||||||
|
long mid = (low + high) >>> 1;
|
||||||
|
long midVal = get(fromIndex + sz*mid);
|
||||||
|
|
||||||
|
if (midVal < key)
|
||||||
|
low = mid + 1;
|
||||||
|
else if (midVal > key)
|
||||||
|
high = mid - 1;
|
||||||
|
else
|
||||||
|
return fromIndex + sz*mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (fromIndex += low*sz; fromIndex < toIndex; fromIndex+=sz) {
|
||||||
|
long val = get(fromIndex);
|
||||||
|
|
||||||
|
if (val == key) return fromIndex;
|
||||||
|
if (val > key) return encodeSearchMiss(sz, fromIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeSearchMiss(sz, toIndex - sz); */
|
||||||
|
|
||||||
|
int64_t ms_binary_search_128(int64_t key, int64_t* area, uint64_t fromIndex, uint64_t toIndex) {
|
||||||
|
int64_t low = 0;
|
||||||
|
int64_t high = (toIndex - fromIndex) / 2 - 1;
|
||||||
|
|
||||||
|
while (high - low >= 32) {
|
||||||
|
int64_t mid = low + (high - low) / 2;
|
||||||
|
int64_t midVal = area[fromIndex + mid * 2];
|
||||||
|
|
||||||
|
if (midVal < key) {
|
||||||
|
low = mid + 1;
|
||||||
|
} else if (midVal > key) {
|
||||||
|
high = mid - 1;
|
||||||
|
} else {
|
||||||
|
return fromIndex + mid * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (fromIndex += low * 2; fromIndex < toIndex; fromIndex+=2) {
|
||||||
|
int64_t val = area[fromIndex];
|
||||||
|
|
||||||
|
if (val == key) return fromIndex;
|
||||||
|
if (val > key) return encodeSearchMiss128(fromIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeSearchMiss128(toIndex - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ms_binary_search_64upper(int64_t key, int64_t* area, uint64_t fromIndex, uint64_t toIndex) {
|
||||||
|
int64_t low = 0;
|
||||||
|
int64_t high = toIndex - fromIndex - 1;
|
||||||
|
|
||||||
|
while (high - low > 32) {
|
||||||
|
int64_t mid = low + (high - low) / 2;
|
||||||
|
int64_t midVal = area[fromIndex + mid];
|
||||||
|
|
||||||
|
if (midVal < key) {
|
||||||
|
low = mid + 1;
|
||||||
|
} else if (midVal > key) {
|
||||||
|
high = mid - 1;
|
||||||
|
} else {
|
||||||
|
return fromIndex + mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (fromIndex += low; fromIndex < toIndex; fromIndex++) {
|
||||||
|
if (area[fromIndex] >= key) return fromIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return toIndex;
|
||||||
|
}
|
14
code/libraries/array/cpp/src/main/public/cpphelpers.hpp
Normal file
14
code/libraries/array/cpp/src/main/public/cpphelpers.hpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void ms_sort_64(int64_t* area, uint64_t start, uint64_t end);
|
||||||
|
void ms_sort_128(int64_t* area, uint64_t start, uint64_t end);
|
||||||
|
|
||||||
|
int64_t ms_linear_search_64(int64_t key, int64_t* area, uint64_t fromIndex, uint64_t toIndex);
|
||||||
|
int64_t ms_linear_search_128(int64_t key, int64_t* area, uint64_t fromIndex, uint64_t toIndex);
|
||||||
|
|
||||||
|
int64_t ms_binary_search_128(int64_t key, int64_t* area, uint64_t fromIndex, uint64_t toIndex);
|
||||||
|
int64_t ms_binary_search_64upper(int64_t key, int64_t* area, uint64_t fromIndex, uint64_t toIndex);
|
||||||
|
}
|
@ -12,6 +12,7 @@ import java.lang.foreign.Arena;
|
|||||||
|
|
||||||
public interface LongArray extends LongArrayBase, LongArrayTransformations, LongArraySearch, LongArraySort, AutoCloseable {
|
public interface LongArray extends LongArrayBase, LongArrayTransformations, LongArraySearch, LongArraySort, AutoCloseable {
|
||||||
int WORD_SIZE = 8;
|
int WORD_SIZE = 8;
|
||||||
|
int foo = 3;
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
static LongArray allocate(long size) {
|
static LongArray allocate(long size) {
|
||||||
|
@ -8,6 +8,7 @@ import java.nio.channels.FileChannel;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public interface LongArrayBase extends BulkTransferArray<LongBuffer> {
|
public interface LongArrayBase extends BulkTransferArray<LongBuffer> {
|
||||||
|
|
||||||
long get(long pos);
|
long get(long pos);
|
||||||
|
|
||||||
void set(long pos, long value);
|
void set(long pos, long value);
|
||||||
@ -26,6 +27,12 @@ public interface LongArrayBase extends BulkTransferArray<LongBuffer> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void quickSortNative(long start, long end);
|
||||||
|
void quickSortNative128(long start, long end);
|
||||||
|
long linearSearchNative(long key, long start, long end);
|
||||||
|
long linearSearchNative128(long key, long start, long end);
|
||||||
|
long binarySearchNativeUB(long key, long start, long end);
|
||||||
|
long binarySearchNative128(long key, long start, long end);
|
||||||
default void increment(long pos) {
|
default void increment(long pos) {
|
||||||
set(pos, get(pos) + 1);
|
set(pos, get(pos) + 1);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package nu.marginalia.array.algo;
|
package nu.marginalia.array.algo;
|
||||||
|
|
||||||
|
import nu.marginalia.NativeAlgos;
|
||||||
import nu.marginalia.array.buffer.LongQueryBuffer;
|
import nu.marginalia.array.buffer.LongQueryBuffer;
|
||||||
|
|
||||||
public interface LongArraySearch extends LongArrayBase {
|
public interface LongArraySearch extends LongArrayBase {
|
||||||
@ -7,6 +8,38 @@ public interface LongArraySearch extends LongArrayBase {
|
|||||||
int LINEAR_SEARCH_CUTOFF = 32;
|
int LINEAR_SEARCH_CUTOFF = 32;
|
||||||
|
|
||||||
default long linearSearch(long key, long fromIndex, long toIndex) {
|
default long linearSearch(long key, long fromIndex, long toIndex) {
|
||||||
|
if (NativeAlgos.isAvailable) {
|
||||||
|
return linearSearchNative(key, fromIndex, toIndex);
|
||||||
|
} else {
|
||||||
|
return linearSearchJava(key, fromIndex, toIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default long linearSearchN(int sz, long key, long fromIndex, long toIndex) {
|
||||||
|
if (NativeAlgos.isAvailable && sz == 2) {
|
||||||
|
return linearSearchNative128(key, fromIndex, toIndex);
|
||||||
|
} else {
|
||||||
|
return linearSearchNJava(sz, key, fromIndex, toIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default long binarySearchUpperBound(long key, long fromIndex, long toIndex) {
|
||||||
|
if (NativeAlgos.isAvailable) {
|
||||||
|
return binarySearchNativeUB(key, fromIndex, toIndex);
|
||||||
|
} else {
|
||||||
|
return binarySearchUpperBoundJava(key, fromIndex, toIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default long binarySearchN(int sz, long key, long fromIndex, long toIndex) {
|
||||||
|
if (NativeAlgos.isAvailable && sz == 2) {
|
||||||
|
return binarySearchNative128(key, fromIndex, toIndex);
|
||||||
|
} else {
|
||||||
|
return binarySearchNJava(sz, key, fromIndex, toIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default long linearSearchJava(long key, long fromIndex, long toIndex) {
|
||||||
long pos;
|
long pos;
|
||||||
|
|
||||||
for (pos = fromIndex; pos < toIndex; pos++) {
|
for (pos = fromIndex; pos < toIndex; pos++) {
|
||||||
@ -19,16 +52,7 @@ public interface LongArraySearch extends LongArrayBase {
|
|||||||
return encodeSearchMiss(1, pos - 1);
|
return encodeSearchMiss(1, pos - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
default long linearSearchUpperBound(long key, long fromIndex, long toIndex) {
|
default long linearSearchNJava(int sz, long key, long fromIndex, long toIndex) {
|
||||||
|
|
||||||
for (long pos = fromIndex; pos < toIndex; pos++) {
|
|
||||||
if (get(pos) >= key) return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
return toIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
default long linearSearchN(int sz, long key, long fromIndex, long toIndex) {
|
|
||||||
long pos;
|
long pos;
|
||||||
|
|
||||||
for (pos = fromIndex; pos < toIndex; pos+=sz) {
|
for (pos = fromIndex; pos < toIndex; pos+=sz) {
|
||||||
@ -60,7 +84,7 @@ public interface LongArraySearch extends LongArrayBase {
|
|||||||
return linearSearch(key, fromIndex + low, fromIndex + high + 1);
|
return linearSearch(key, fromIndex + low, fromIndex + high + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
default long binarySearchN(int sz, long key, long fromIndex, long toIndex) {
|
default long binarySearchNJava(int sz, long key, long fromIndex, long toIndex) {
|
||||||
long low = 0;
|
long low = 0;
|
||||||
long high = (toIndex - fromIndex)/sz - 1;
|
long high = (toIndex - fromIndex)/sz - 1;
|
||||||
|
|
||||||
@ -87,7 +111,7 @@ public interface LongArraySearch extends LongArrayBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
default long binarySearchUpperBound(long key, long fromIndex, long toIndex) {
|
default long binarySearchUpperBoundJava(long key, long fromIndex, long toIndex) {
|
||||||
long low = 0;
|
long low = 0;
|
||||||
long high = (toIndex - fromIndex) - 1;
|
long high = (toIndex - fromIndex) - 1;
|
||||||
|
|
||||||
@ -110,28 +134,6 @@ public interface LongArraySearch extends LongArrayBase {
|
|||||||
return toIndex;
|
return toIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
default long binarySearchUpperBoundN(int sz, long key, long fromIndex, long toIndex) {
|
|
||||||
long low = 0;
|
|
||||||
long high = (toIndex - fromIndex)/sz - 1;
|
|
||||||
|
|
||||||
while (high - low >= LINEAR_SEARCH_CUTOFF) {
|
|
||||||
long mid = (low + high) >>> 1;
|
|
||||||
long midVal = get(fromIndex + sz*mid);
|
|
||||||
|
|
||||||
if (midVal < key)
|
|
||||||
low = mid + 1;
|
|
||||||
else if (midVal > key)
|
|
||||||
high = mid - 1;
|
|
||||||
else
|
|
||||||
return fromIndex + sz*mid;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (fromIndex += low; fromIndex < toIndex; fromIndex+=sz) {
|
|
||||||
if (get(fromIndex) >= key) return fromIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return toIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
default void retain(LongQueryBuffer buffer, long boundary, long searchStart, long searchEnd) {
|
default void retain(LongQueryBuffer buffer, long boundary, long searchStart, long searchEnd) {
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package nu.marginalia.array.algo;
|
package nu.marginalia.array.algo;
|
||||||
|
|
||||||
|
import nu.marginalia.NativeAlgos;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.channels.FileChannel;
|
import java.nio.channels.FileChannel;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@ -68,33 +70,6 @@ public interface LongArraySort extends LongArrayBase {
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
default void sortLargeSpan(SortingContext ctx, long start, long end) throws IOException {
|
|
||||||
long size = end - start;
|
|
||||||
|
|
||||||
if (size < ctx.memorySortLimit()) {
|
|
||||||
quickSort(start, end);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mergeSort(start, end, ctx.tempDir());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default void sortLargeSpanN(SortingContext ctx, int sz, long start, long end) throws IOException {
|
|
||||||
if (sz == 1) {
|
|
||||||
sortLargeSpan(ctx, start, end);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
long size = end - start;
|
|
||||||
|
|
||||||
if (size < ctx.memorySortLimit()) {
|
|
||||||
quickSortN(sz, start, end);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mergeSortN(sz, start, end, ctx.tempDir());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default boolean isSortedN(int wordSize, long start, long end) {
|
default boolean isSortedN(int wordSize, long start, long end) {
|
||||||
if (start == end) return true;
|
if (start == end) return true;
|
||||||
|
|
||||||
@ -109,8 +84,6 @@ public interface LongArraySort extends LongArrayBase {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
default void insertionSort(long start, long end) {
|
default void insertionSort(long start, long end) {
|
||||||
SortAlgoInsertionSort._insertionSort(this, start, end);
|
SortAlgoInsertionSort._insertionSort(this, start, end);
|
||||||
}
|
}
|
||||||
@ -119,11 +92,13 @@ public interface LongArraySort extends LongArrayBase {
|
|||||||
SortAlgoInsertionSort._insertionSortN(this, sz, start, end);
|
SortAlgoInsertionSort._insertionSortN(this, sz, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
default void quickSort(long start, long end) {
|
default void quickSort(long start, long end) {
|
||||||
if (end - start < 64) {
|
if (end - start < 64) {
|
||||||
insertionSort(start, end);
|
insertionSort(start, end);
|
||||||
}
|
}
|
||||||
|
else if (NativeAlgos.isAvailable) {
|
||||||
|
quickSortNative(start, end);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
SortAlgoQuickSort._quickSortLH(this, start, end - 1);
|
SortAlgoQuickSort._quickSortLH(this, start, end - 1);
|
||||||
}
|
}
|
||||||
@ -135,8 +110,13 @@ public interface LongArraySort extends LongArrayBase {
|
|||||||
if (end == start)
|
if (end == start)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (NativeAlgos.isAvailable && wordSize == 2) {
|
||||||
|
quickSortNative128(start, end);
|
||||||
|
}
|
||||||
|
else {
|
||||||
SortAlgoQuickSort._quickSortLHN(this, wordSize, start, end - wordSize);
|
SortAlgoQuickSort._quickSortLHN(this, wordSize, start, end - wordSize);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default void mergeSortN(int wordSize, long start, long end, Path tmpDir) throws IOException {
|
default void mergeSortN(int wordSize, long start, long end, Path tmpDir) throws IOException {
|
||||||
int length = (int) (end - start);
|
int length = (int) (end - start);
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
package nu.marginalia.array.delegate;
|
|
||||||
|
|
||||||
import nu.marginalia.array.ArrayRangeReference;
|
|
||||||
import nu.marginalia.array.IntArray;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public class ReferenceImplIntArrayDelegate implements IntArray {
|
|
||||||
|
|
||||||
private final IntArray delegate;
|
|
||||||
|
|
||||||
public ReferenceImplIntArrayDelegate(IntArray delegate) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int get(long pos) {
|
|
||||||
return delegate.get(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(long pos, int value) {
|
|
||||||
delegate.set(pos, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long size() {
|
|
||||||
return delegate.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(Path file) throws IOException {
|
|
||||||
delegate.write(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void transferFrom(FileChannel source, long sourceStart, long arrayStart, long arrayEnd) throws IOException {
|
|
||||||
delegate.transferFrom(source, sourceStart, arrayStart, arrayEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ArrayRangeReference<IntArray> directRangeIfPossible(long start, long end) {
|
|
||||||
return delegate.directRangeIfPossible(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void force() {
|
|
||||||
delegate.force();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package nu.marginalia.array.delegate;
|
|
||||||
|
|
||||||
import nu.marginalia.array.ArrayRangeReference;
|
|
||||||
import nu.marginalia.array.LongArray;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
public class ReferenceImplLongArrayDelegate implements LongArray {
|
|
||||||
|
|
||||||
private final LongArray delegate;
|
|
||||||
|
|
||||||
public ReferenceImplLongArrayDelegate(LongArray delegate) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long get(long pos) {
|
|
||||||
return delegate.get(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(long pos, long value) {
|
|
||||||
delegate.set(pos, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long size() {
|
|
||||||
return delegate.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(Path file) throws IOException {
|
|
||||||
delegate.write(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void transferFrom(FileChannel source, long sourceStart, long arrayStart, long arrayEnd) throws IOException {
|
|
||||||
delegate.transferFrom(source, sourceStart, arrayStart, arrayEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ArrayRangeReference<LongArray> directRangeIfPossible(long start, long end) {
|
|
||||||
return delegate.directRangeIfPossible(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void force() {
|
|
||||||
delegate.force();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
delegate.close();
|
|
||||||
}
|
|
||||||
}
|
|
@ -75,6 +75,35 @@ public class ShiftedLongArray implements LongArray {
|
|||||||
delegate.fill(start + shift, end + shift, val);
|
delegate.fill(start + shift, end + shift, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void quickSortNative(long start, long end) {
|
||||||
|
delegate.quickSortNative(start + shift, end + shift);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void quickSortNative128(long start, long end) {
|
||||||
|
delegate.quickSortNative128(start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long linearSearchNative(long key, long start, long end) {
|
||||||
|
return delegate.linearSearchNative(key, start + shift, end + shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long linearSearchNative128(long key, long start, long end) {
|
||||||
|
return delegate.linearSearchNative128(key, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long binarySearchNativeUB(long key, long start, long end) {
|
||||||
|
return delegate.binarySearchNativeUB(key, start + shift, end + shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long binarySearchNative128(long key, long start, long end) {
|
||||||
|
return delegate.binarySearchNative128(key, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long size() {
|
public long size() {
|
||||||
return size;
|
return size;
|
||||||
@ -132,13 +161,6 @@ public class ShiftedLongArray implements LongArray {
|
|||||||
return delegate.isSortedN(sz, shift + start, shift + end);
|
return delegate.isSortedN(sz, shift + start, shift + end);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sortLargeSpanN(SortingContext ctx, int sz, long start, long end) throws IOException {
|
|
||||||
delegate.sortLargeSpanN(ctx, sz, start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sortLargeSpan(SortingContext ctx, long start, long end) throws IOException {
|
|
||||||
delegate.sortLargeSpan(ctx, start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long searchN(int sz, long key) {
|
public long searchN(int sz, long key) {
|
||||||
if (size < 128) {
|
if (size < 128) {
|
||||||
@ -216,14 +238,7 @@ public class ShiftedLongArray implements LongArray {
|
|||||||
public long binarySearchUpperBound(long key, long fromIndex, long toIndex) {
|
public long binarySearchUpperBound(long key, long fromIndex, long toIndex) {
|
||||||
return translateSearchResult(1, delegate.binarySearchUpperBound(key, fromIndex + shift, toIndex+shift));
|
return translateSearchResult(1, delegate.binarySearchUpperBound(key, fromIndex + shift, toIndex+shift));
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
public long linearSearchUpperBound(long key, long fromIndex, long toIndex) {
|
|
||||||
return translateSearchResult(1, delegate.linearSearchUpperBound(key, fromIndex + shift, toIndex+shift));
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public long binarySearchUpperBoundN(int sz, long key, long fromIndex, long toIndex) {
|
|
||||||
return translateSearchResult(sz, delegate.binarySearchUpperBoundN(sz, key, fromIndex + shift, toIndex+shift));
|
|
||||||
}
|
|
||||||
private long translateSearchResult(int sz, long delegatedIdx) {
|
private long translateSearchResult(int sz, long delegatedIdx) {
|
||||||
long ret;
|
long ret;
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package nu.marginalia.array.page;
|
package nu.marginalia.array.page;
|
||||||
|
|
||||||
|
import nu.marginalia.NativeAlgos;
|
||||||
import nu.marginalia.array.ArrayRangeReference;
|
import nu.marginalia.array.ArrayRangeReference;
|
||||||
import nu.marginalia.array.LongArray;
|
import nu.marginalia.array.LongArray;
|
||||||
|
|
||||||
@ -125,6 +126,36 @@ public class SegmentLongArray implements PartitionPage, LongArray {
|
|||||||
return segment.byteSize() / JAVA_LONG.byteSize();
|
return segment.byteSize() / JAVA_LONG.byteSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void quickSortNative(long start, long end) {
|
||||||
|
NativeAlgos.sort(segment, start, end);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void quickSortNative128(long start, long end) {
|
||||||
|
NativeAlgos.sort128(segment, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long linearSearchNative(long key, long start, long end) {
|
||||||
|
return NativeAlgos.linearSearch64(key, segment, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long linearSearchNative128(long key, long start, long end) {
|
||||||
|
return NativeAlgos.linearSearch128(key, segment, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long binarySearchNativeUB(long key, long start, long end) {
|
||||||
|
return NativeAlgos.binarySearch64Upper(key, segment, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long binarySearchNative128(long key, long start, long end) {
|
||||||
|
return NativeAlgos.binarySearch128(key, segment, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer getByteBuffer() {
|
public ByteBuffer getByteBuffer() {
|
||||||
return segment.asByteBuffer();
|
return segment.asByteBuffer();
|
||||||
@ -147,6 +178,7 @@ public class SegmentLongArray implements PartitionPage, LongArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ArrayRangeReference<LongArray> directRangeIfPossible(long start, long end) {
|
public ArrayRangeReference<LongArray> directRangeIfPossible(long start, long end) {
|
||||||
return new ArrayRangeReference<>(this, start, end);
|
return new ArrayRangeReference<>(this, start, end);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package nu.marginalia.array.page;
|
package nu.marginalia.array.page;
|
||||||
|
|
||||||
|
import nu.marginalia.NativeAlgos;
|
||||||
import nu.marginalia.array.ArrayRangeReference;
|
import nu.marginalia.array.ArrayRangeReference;
|
||||||
import nu.marginalia.array.LongArray;
|
import nu.marginalia.array.LongArray;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -273,4 +274,34 @@ public class UnsafeLongArray implements PartitionPage, LongArray {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void quickSortNative(long start, long end) {
|
||||||
|
NativeAlgos.sort(segment, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void quickSortNative128(long start, long end) {
|
||||||
|
NativeAlgos.sort128(segment, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long linearSearchNative(long key, long start, long end) {
|
||||||
|
return NativeAlgos.linearSearch64(key, segment, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long linearSearchNative128(long key, long start, long end) {
|
||||||
|
return NativeAlgos.linearSearch128(key, segment, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long binarySearchNativeUB(long key, long start, long end) {
|
||||||
|
return NativeAlgos.binarySearch64Upper(key, segment, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long binarySearchNative128(long key, long start, long end) {
|
||||||
|
return NativeAlgos.binarySearch128(key, segment, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
package nu.marginalia.array;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.LongBuffer;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
/** This is a benchmark comparison implementation of LongArray that
|
|
||||||
* does not include the optimizations in PagingLongArray */
|
|
||||||
public class SimulatedNaiveArray implements LongArray {
|
|
||||||
final LongBuffer[] buffers;
|
|
||||||
final int bufferSize;
|
|
||||||
|
|
||||||
public SimulatedNaiveArray(int size, int bufferSize) {
|
|
||||||
this.bufferSize = bufferSize;
|
|
||||||
buffers = new LongBuffer[size / bufferSize];
|
|
||||||
for (int i = 0 ; i < buffers.length; i++) {
|
|
||||||
buffers[i] = LongBuffer.allocate(bufferSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ArrayRangeReference<LongArray> directRangeIfPossible(long start, long end) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void force() {}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long get(long pos) {
|
|
||||||
return buffers[(int) pos/bufferSize].get((int) pos%bufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void set(long pos, long value) {
|
|
||||||
buffers[(int) pos/bufferSize].put((int) pos%bufferSize, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long size() {
|
|
||||||
return 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(Path file) throws IOException {}
|
|
||||||
@Override
|
|
||||||
public void transferFrom(FileChannel source, long sourceStart, long arrayStart, long arrayEnd) throws IOException {}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
package nu.marginalia.array.page;
|
|
||||||
|
|
||||||
import nu.marginalia.array.LongArray;
|
|
||||||
import nu.marginalia.array.SimulatedNaiveArray;
|
|
||||||
import org.openjdk.jmh.annotations.*;
|
|
||||||
|
|
||||||
import java.lang.foreign.Arena;
|
|
||||||
|
|
||||||
public class FoldBenchmark {
|
|
||||||
|
|
||||||
@State(Scope.Benchmark)
|
|
||||||
public static class BenchState {
|
|
||||||
|
|
||||||
@Setup(Level.Trial)
|
|
||||||
public void doSetup() {
|
|
||||||
array.transformEach(0, size, (pos,old) -> ~pos);
|
|
||||||
array2.transformEach(0, size, (pos,old) -> ~pos);
|
|
||||||
array3.transformEach(0, size, (pos,old) -> ~pos);
|
|
||||||
simulateNaiveApproach.transformEach(0, size, (pos,old) -> ~pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
int size = 100*1024*1024;
|
|
||||||
int pageSize = 10*1024;
|
|
||||||
LongArray array = LongArray.allocate(size);
|
|
||||||
LongArray array2 = SegmentLongArray.onHeap(Arena.ofShared(), size);
|
|
||||||
LongArray array3 = SegmentLongArray.onHeap(Arena.ofConfined(), size);
|
|
||||||
LongArray simulateNaiveApproach = new SimulatedNaiveArray(size, pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Fork(value = 1, warmups = 1)
|
|
||||||
@Warmup(iterations = 1)
|
|
||||||
@Benchmark
|
|
||||||
@BenchmarkMode(Mode.Throughput)
|
|
||||||
public long benchArrayFoldGoldStandard(BenchState state) {
|
|
||||||
return state.array.fold(0, 0, state.size, Long::sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Fork(value = 1, warmups = 1)
|
|
||||||
@Warmup(iterations = 1)
|
|
||||||
@Benchmark
|
|
||||||
@BenchmarkMode(Mode.Throughput)
|
|
||||||
public long benchArrayFoldGoldStandard2(BenchState state) {
|
|
||||||
return state.array2.fold(0, 0, state.size, Long::sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Fork(value = 1, warmups = 1)
|
|
||||||
@Warmup(iterations = 1)
|
|
||||||
@Benchmark
|
|
||||||
@BenchmarkMode(Mode.Throughput)
|
|
||||||
public long benchArrayFoldGoldStandard3(BenchState state) {
|
|
||||||
return state.array3.fold(0, 0, state.size, Long::sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Fork(value = 1, warmups = 1)
|
|
||||||
@Warmup(iterations = 1)
|
|
||||||
@Benchmark
|
|
||||||
@BenchmarkMode(Mode.Throughput)
|
|
||||||
public long benchArrayFoldNaive(BenchState state) {
|
|
||||||
return state.simulateNaiveApproach.fold(0, 0, state.size, Long::sum);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,83 +1,46 @@
|
|||||||
package nu.marginalia.array.page;
|
package nu.marginalia.array.page;
|
||||||
|
|
||||||
import nu.marginalia.array.LongArray;
|
import nu.marginalia.array.LongArray;
|
||||||
import nu.marginalia.array.SimulatedNaiveArray;
|
import nu.marginalia.array.LongArrayFactory;
|
||||||
import org.openjdk.jmh.annotations.*;
|
import org.openjdk.jmh.annotations.*;
|
||||||
|
|
||||||
import java.lang.foreign.Arena;
|
|
||||||
|
|
||||||
/** This benchmark simulates the sorting in index creation */
|
/** This benchmark simulates the sorting in index creation */
|
||||||
public class QuicksortBenchmark {
|
public class QuicksortBenchmark {
|
||||||
|
|
||||||
@State(Scope.Benchmark)
|
@State(Scope.Benchmark)
|
||||||
public static class BenchState {
|
public static class BenchState {
|
||||||
|
|
||||||
@Setup(Level.Trial)
|
@Setup(Level.Invocation)
|
||||||
public void doSetup() {
|
public void doSetup() {
|
||||||
array.transformEach(0, size, (pos,old) -> ~pos);
|
array.transformEach(0, size, (pos,old) -> ~pos);
|
||||||
array2.transformEach(0, size, (pos,old) -> ~pos);
|
|
||||||
array3.transformEach(0, size, (pos,old) -> ~pos);
|
|
||||||
simulateNaiveApproach.transformEach(0, size, (pos,old) -> ~pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int size = 100*1024*1024;
|
int size = 1024*1024;
|
||||||
int pageSize = 10*1024;
|
int pageSize = 10*1024;
|
||||||
LongArray array = LongArray.allocate(size);
|
LongArray array = LongArrayFactory.onHeapShared(size);
|
||||||
LongArray array2 = SegmentLongArray.onHeap(Arena.ofShared(), size);
|
|
||||||
LongArray array3 = SegmentLongArray.onHeap(Arena.ofConfined(), size);
|
|
||||||
LongArray simulateNaiveApproach = new SimulatedNaiveArray(size, pageSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Fork(value = 1, warmups = 1)
|
@Fork(value = 2, warmups = 5)
|
||||||
@Warmup(iterations = 1)
|
@Warmup(iterations = 1)
|
||||||
@Benchmark
|
@Benchmark
|
||||||
@BenchmarkMode(Mode.Throughput)
|
@BenchmarkMode(Mode.Throughput)
|
||||||
public LongArray benchArrayFoldGoldStandard(BenchState state) {
|
public LongArray javaSort(BenchState state) {
|
||||||
var array = state.array;
|
var array = state.array;
|
||||||
|
|
||||||
for (int i = 0; i + 100 < state.size; i+=100) {
|
array.quickSortN(2, 0, array.size());
|
||||||
array.quickSort(i, i + 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
@Fork(value = 1, warmups = 1)
|
|
||||||
|
@Fork(value = 2, warmups = 5)
|
||||||
@Warmup(iterations = 1)
|
@Warmup(iterations = 1)
|
||||||
@Benchmark
|
@Benchmark
|
||||||
@BenchmarkMode(Mode.Throughput)
|
@BenchmarkMode(Mode.Throughput)
|
||||||
public LongArray benchArrayFoldGoldStandard2(BenchState state) {
|
public LongArray cppSort(BenchState state) {
|
||||||
var array = state.array2;
|
|
||||||
|
|
||||||
for (int i = 0; i + 100 < state.size; i+=100) {
|
var array = state.array;
|
||||||
array.quickSort(i, i + 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array;
|
array.quickSortNative128(0, array.size());
|
||||||
}
|
|
||||||
@Fork(value = 1, warmups = 1)
|
|
||||||
@Warmup(iterations = 1)
|
|
||||||
@Benchmark
|
|
||||||
@BenchmarkMode(Mode.Throughput)
|
|
||||||
public LongArray benchArrayFoldGoldStandard3(BenchState state) {
|
|
||||||
var array = state.array3;
|
|
||||||
|
|
||||||
for (int i = 0; i + 100 < state.size; i+=100) {
|
|
||||||
array.quickSort(i, i + 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Fork(value = 1, warmups = 1)
|
|
||||||
@Warmup(iterations = 1)
|
|
||||||
@Benchmark
|
|
||||||
@BenchmarkMode(Mode.Throughput)
|
|
||||||
public LongArray benchArrayFoldNaive(BenchState state) {
|
|
||||||
var array = state.simulateNaiveApproach;
|
|
||||||
|
|
||||||
for (int i = 0; i + 100 < state.size; i+=100) {
|
|
||||||
array.quickSort(i, i + 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
@ -47,20 +47,19 @@ class LongArraySearchTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void linearSearchUpperBound() {
|
void binarySearchUpperBoundNative() {
|
||||||
linearSearchUpperBoundTester(basicArray);
|
binarySearchUpperBoundNativeTester(basicArray);
|
||||||
linearSearchUpperBoundTester(shiftedArray);
|
binarySearchUpperBoundNativeTester(shiftedArray);
|
||||||
linearSearchUpperBoundTester(segmentArray);
|
binarySearchUpperBoundNativeTester(segmentArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEmptyRange() {
|
public void testEmptyRange() {
|
||||||
assertTrue(segmentArray.binarySearchN(2, 0, 0, 0) < 0);
|
assertTrue(segmentArray.binarySearchN(2, 0, 0, 0) < 0);
|
||||||
assertTrue(segmentArray.linearSearchN(2, 0, 0, 0) < 0);
|
assertTrue(segmentArray.linearSearchN(2, 0, 0, 0) < 0);
|
||||||
assertTrue(segmentArray.binarySearch(0, 0, 0) < 0);
|
assertTrue(segmentArray.binarySearch(0, 0, 0) < 0);
|
||||||
assertTrue(segmentArray.linearSearch(0, 0, 0) < 0);
|
assertTrue(segmentArray.linearSearch(0, 0, 0) < 0);
|
||||||
|
|
||||||
assertEquals(0, segmentArray.linearSearchUpperBound(0, 0, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void linearSearchTester(LongArray array) {
|
void linearSearchTester(LongArray array) {
|
||||||
@ -112,12 +111,10 @@ class LongArraySearchTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void linearSearchUpperBoundTester(LongArray array) {
|
|
||||||
for (int i = 0; i < array.size() * 3; i++) {
|
|
||||||
long ret = array.linearSearchUpperBound(i, 0, array.size());
|
|
||||||
long ret2 = array.binarySearchUpperBound(i, 0, array.size());
|
|
||||||
|
|
||||||
assertEquals(ret, ret2);
|
void binarySearchUpperBoundNativeTester(LongArray array) {
|
||||||
|
for (int i = 0; i < array.size() * 3; i++) {
|
||||||
|
long ret = array.binarySearchNativeUB(i, 0, array.size());
|
||||||
|
|
||||||
if ((i % 3) == 0) {
|
if ((i % 3) == 0) {
|
||||||
assertTrue(ret >= 0);
|
assertTrue(ret >= 0);
|
||||||
@ -125,13 +122,11 @@ class LongArraySearchTest {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (i > 0 && ret > 0 && ret < array.size()) {
|
if (i > 0 && ret > 0 && ret < array.size()) {
|
||||||
System.out.println(ret);
|
|
||||||
assertTrue(array.get(ret-1) < i);
|
assertTrue(array.get(ret-1) < i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void retain() {
|
void retain() {
|
||||||
long[] vals = new long[128];
|
long[] vals = new long[128];
|
||||||
|
@ -61,12 +61,40 @@ class LongArraySortTest {
|
|||||||
void sort(LongArray array, long start, long end) throws IOException;
|
void sort(LongArray array, long start, long end) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNative() {
|
||||||
|
LongArray array = LongArray.allocate(4);
|
||||||
|
array.set(0, 1);
|
||||||
|
array.set(1, 4);
|
||||||
|
array.set(2, 3);
|
||||||
|
array.set(3, 2);
|
||||||
|
array.quickSortNative(0, 4);
|
||||||
|
assertTrue(array.isSorted(0, 4));
|
||||||
|
assertEquals(1, array.get(0));
|
||||||
|
assertEquals(2, array.get(1));
|
||||||
|
assertEquals(3, array.get(2));
|
||||||
|
assertEquals(4, array.get(3));
|
||||||
|
|
||||||
|
array.set(2, 5);
|
||||||
|
array.quickSortNative(2, 4);
|
||||||
|
|
||||||
|
assertEquals(4, array.get(2));
|
||||||
|
assertEquals(5, array.get(3));
|
||||||
|
|
||||||
|
assertTrue(array.isSorted(2, 4));
|
||||||
|
}
|
||||||
@Test
|
@Test
|
||||||
public void quickSortStressTest() throws IOException {
|
public void quickSortStressTest() throws IOException {
|
||||||
LongArray array = LongArray.allocate(65536);
|
LongArray array = LongArray.allocate(65536);
|
||||||
sortAlgorithmTester(array, LongArraySort::quickSort);
|
sortAlgorithmTester(array, LongArraySort::quickSort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nativeSortTest() throws IOException {
|
||||||
|
LongArray array = LongArray.allocate(65536);
|
||||||
|
sortAlgorithmTester(array, LongArraySort::quickSortNative);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void insertionSortStressTest() throws IOException {
|
public void insertionSortStressTest() throws IOException {
|
||||||
|
@ -36,6 +36,7 @@ include 'code:index:index-forward'
|
|||||||
include 'code:index:index-reverse'
|
include 'code:index:index-reverse'
|
||||||
|
|
||||||
include 'code:libraries:array'
|
include 'code:libraries:array'
|
||||||
|
include 'code:libraries:array:cpp'
|
||||||
include 'code:libraries:geo-ip'
|
include 'code:libraries:geo-ip'
|
||||||
include 'code:libraries:btree'
|
include 'code:libraries:btree'
|
||||||
include 'code:libraries:easy-lsh'
|
include 'code:libraries:easy-lsh'
|
||||||
|
Loading…
Reference in New Issue
Block a user