Add ikv-store benchmark fixture
This commit is contained in:
@@ -15,7 +15,7 @@ Only the content between the markers below is rewritten, so the rest of this REA
|
|||||||
|
|
||||||
<!-- BENCHMARK_RESULTS_START -->
|
<!-- BENCHMARK_RESULTS_START -->
|
||||||
|
|
||||||
Last updated: 2026-04-05T16:53:23.606653992Z
|
Last updated: 2026-04-05T17:19:53.068450463Z
|
||||||
|
|
||||||
### System info
|
### System info
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ Last updated: 2026-04-05T16:53:23.606653992Z
|
|||||||
|
|
||||||
- Benchmark: `com.benchmark.benchmarks.DataAccessBenchmark.readSingleUser`
|
- Benchmark: `com.benchmark.benchmarks.DataAccessBenchmark.readSingleUser`
|
||||||
- Thread count: `12`
|
- Thread count: `12`
|
||||||
- Fixtures in this snapshot: `28`
|
- Fixtures in this snapshot: `30`
|
||||||
|
|
||||||
### Results
|
### Results
|
||||||
|
|
||||||
@@ -39,38 +39,40 @@ Last updated: 2026-04-05T16:53:23.606653992Z
|
|||||||
|
|
||||||
| Rank | Fixture | Score | Error | Unit |
|
| Rank | Fixture | Score | Error | Unit |
|
||||||
|---:|---|---:|---:|---|
|
|---:|---|---:|---:|---|
|
||||||
| 1 | `in-memory` | 1071.748 | 51.460 | `ops/us` |
|
| 1 | `ikv-store` | 1085.094 | 32.267 | `ops/us` |
|
||||||
| 2 | `chronicle-map` | 84.556 | 5.034 | `ops/us` |
|
| 2 | `in-memory` | 1076.857 | 17.298 | `ops/us` |
|
||||||
| 3 | `memory-mapped-file` | 78.969 | 1.996 | `ops/us` |
|
| 3 | `chronicle-map` | 85.276 | 2.529 | `ops/us` |
|
||||||
| 4 | `datastore4j` | 58.406 | 2.794 | `ops/us` |
|
| 4 | `memory-mapped-file` | 79.549 | 1.791 | `ops/us` |
|
||||||
| 5 | `leveldb` | 32.387 | 5.570 | `ops/us` |
|
| 5 | `datastore4j` | 61.163 | 2.313 | `ops/us` |
|
||||||
| 6 | `mapdb` | 29.448 | 1.222 | `ops/us` |
|
| 6 | `leveldb` | 31.796 | 5.871 | `ops/us` |
|
||||||
| 7 | `sqlite-jdbc-memory` | 3.847 | 0.463 | `ops/us` |
|
| 7 | `mapdb` | 28.615 | 6.765 | `ops/us` |
|
||||||
| 8 | `lmdb` | 3.210 | 0.725 | `ops/us` |
|
| 8 | `sqlite-jdbc-memory` | 4.153 | 0.249 | `ops/us` |
|
||||||
| 9 | `kryo` | 2.463 | 0.130 | `ops/us` |
|
| 9 | `lmdb` | 3.115 | 0.864 | `ops/us` |
|
||||||
| 10 | `gson` | 2.198 | 0.057 | `ops/us` |
|
| 10 | `kryo` | 2.549 | 0.150 | `ops/us` |
|
||||||
| 11 | `sqlite-jdbc` | 1.862 | 0.492 | `ops/us` |
|
| 11 | `gson` | 2.154 | 0.183 | `ops/us` |
|
||||||
| 12 | `sqlite-ormlite-memory` | 1.018 | 0.347 | `ops/us` |
|
| 12 | `sqlite-jdbc` | 1.970 | 0.162 | `ops/us` |
|
||||||
| 13 | `sqlite-ormlite` | 0.835 | 0.279 | `ops/us` |
|
| 13 | `sqlite-ormlite-memory` | 1.071 | 0.255 | `ops/us` |
|
||||||
| 14 | `duckdb-jdbc` | 0.048 | 0.007 | `ops/us` |
|
| 14 | `sqlite-ormlite` | 0.886 | 0.039 | `ops/us` |
|
||||||
|
| 15 | `duckdb-jdbc` | 0.050 | 0.004 | `ops/us` |
|
||||||
|
|
||||||
#### AverageTime
|
#### AverageTime
|
||||||
|
|
||||||
| Rank | Fixture | Score | Error | Unit |
|
| Rank | Fixture | Score | Error | Unit |
|
||||||
|---:|---|---:|---:|---|
|
|---:|---|---:|---:|---|
|
||||||
| 1 | `in-memory` | 0.012 | 0.001 | `us/op` |
|
| 1 | `ikv-store` | 0.011 | 0.000 | `us/op` |
|
||||||
| 2 | `chronicle-map` | 0.150 | 0.017 | `us/op` |
|
| 2 | `in-memory` | 0.011 | 0.000 | `us/op` |
|
||||||
| 3 | `memory-mapped-file` | 0.155 | 0.008 | `us/op` |
|
| 3 | `chronicle-map` | 0.142 | 0.005 | `us/op` |
|
||||||
| 4 | `datastore4j` | 0.228 | 0.016 | `us/op` |
|
| 4 | `memory-mapped-file` | 0.154 | 0.004 | `us/op` |
|
||||||
| 5 | `leveldb` | 0.288 | 0.018 | `us/op` |
|
| 5 | `datastore4j` | 0.204 | 0.010 | `us/op` |
|
||||||
| 6 | `mapdb` | 0.481 | 0.032 | `us/op` |
|
| 6 | `leveldb` | 0.387 | 0.046 | `us/op` |
|
||||||
| 7 | `sqlite-jdbc-memory` | 2.964 | 0.191 | `us/op` |
|
| 7 | `mapdb` | 0.403 | 0.031 | `us/op` |
|
||||||
| 8 | `lmdb` | 3.708 | 1.615 | `us/op` |
|
| 8 | `sqlite-jdbc-memory` | 3.005 | 0.504 | `us/op` |
|
||||||
| 9 | `kryo` | 4.834 | 0.863 | `us/op` |
|
| 9 | `lmdb` | 3.665 | 0.355 | `us/op` |
|
||||||
| 10 | `gson` | 5.592 | 0.253 | `us/op` |
|
| 10 | `kryo` | 4.820 | 0.338 | `us/op` |
|
||||||
| 11 | `sqlite-jdbc` | 6.108 | 0.397 | `us/op` |
|
| 11 | `gson` | 5.416 | 0.368 | `us/op` |
|
||||||
| 12 | `sqlite-ormlite-memory` | 10.541 | 0.393 | `us/op` |
|
| 12 | `sqlite-jdbc` | 6.056 | 0.441 | `us/op` |
|
||||||
| 13 | `sqlite-ormlite` | 17.532 | 7.855 | `us/op` |
|
| 13 | `sqlite-ormlite-memory` | 10.431 | 0.868 | `us/op` |
|
||||||
| 14 | `duckdb-jdbc` | 339.471 | 181.558 | `us/op` |
|
| 14 | `sqlite-ormlite` | 13.394 | 0.376 | `us/op` |
|
||||||
|
| 15 | `duckdb-jdbc` | 235.873 | 22.582 | `us/op` |
|
||||||
|
|
||||||
<!-- BENCHMARK_RESULTS_END -->
|
<!-- BENCHMARK_RESULTS_END -->
|
||||||
|
|||||||
@@ -17,6 +17,13 @@
|
|||||||
<jmh.version>1.37</jmh.version>
|
<jmh.version>1.37</jmh.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>jitpack.io</id>
|
||||||
|
<url>https://jitpack.io</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- JMH -->
|
<!-- JMH -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -106,6 +113,13 @@
|
|||||||
<version>0.12</version>
|
<version>0.12</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- IKV Java client (JitPack) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.inlined</groupId>
|
||||||
|
<artifactId>ikv-java-client</artifactId>
|
||||||
|
<version>0.0.8</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Silence SLF4J "no binding" warning -->
|
<!-- Silence SLF4J "no binding" warning -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ public class DataAccessBenchmark {
|
|||||||
@Param({
|
@Param({
|
||||||
"in-memory",
|
"in-memory",
|
||||||
"memory-mapped-file",
|
"memory-mapped-file",
|
||||||
|
"ikv-store",
|
||||||
"datastore4j",
|
"datastore4j",
|
||||||
"leveldb",
|
"leveldb",
|
||||||
"lmdb",
|
"lmdb",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ public class FixtureFactory {
|
|||||||
return switch (type) {
|
return switch (type) {
|
||||||
case "in-memory" -> new InMemoryFixture();
|
case "in-memory" -> new InMemoryFixture();
|
||||||
case "memory-mapped-file" -> new MemoryMappedFileFixture();
|
case "memory-mapped-file" -> new MemoryMappedFileFixture();
|
||||||
|
case "ikv-store" -> new IkvStoreFixture();
|
||||||
case "datastore4j" -> new DataStore4jFixture();
|
case "datastore4j" -> new DataStore4jFixture();
|
||||||
case "leveldb" -> new LevelDbFixture();
|
case "leveldb" -> new LevelDbFixture();
|
||||||
case "lmdb" -> new LmdbFixture();
|
case "lmdb" -> new LmdbFixture();
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package com.benchmark.fixtures;
|
||||||
|
|
||||||
|
import com.benchmark.model.User;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
public class IkvStoreFixture implements DataFixtures {
|
||||||
|
|
||||||
|
private static final AtomicBoolean FALLBACK_WARNING_PRINTED = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
private final InMemoryFixture fallback = new InMemoryFixture();
|
||||||
|
|
||||||
|
private Object reader;
|
||||||
|
private Object writer;
|
||||||
|
private boolean useFallback;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup(List<User> users) throws Exception {
|
||||||
|
String accountId = getenv("IKV_ACCOUNT_ID");
|
||||||
|
String accountPassKey = getenv("IKV_ACCOUNT_PASSKEY");
|
||||||
|
String storeName = getenv("IKV_STORE_NAME");
|
||||||
|
String mountDirectory = getenv("IKV_MOUNT_DIRECTORY");
|
||||||
|
|
||||||
|
if (accountId == null || accountPassKey == null || storeName == null || mountDirectory == null) {
|
||||||
|
useFallback = true;
|
||||||
|
fallback.setup(users);
|
||||||
|
printFallbackWarning("IKV fixture: required env vars missing (IKV_ACCOUNT_ID, IKV_ACCOUNT_PASSKEY, IKV_STORE_NAME, IKV_MOUNT_DIRECTORY). Using in-memory fallback.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Class<?> factoryClass = Class.forName("io.inlined.clients.IKVClientFactory");
|
||||||
|
Class<?> optionsBuilderClass = Class.forName("io.inlined.clients.ClientOptions$Builder");
|
||||||
|
Class<?> optionsClass = Class.forName("io.inlined.clients.ClientOptions");
|
||||||
|
Class<?> docBuilderClass = Class.forName("io.inlined.clients.IKVDocument$Builder");
|
||||||
|
Class<?> docClass = Class.forName("io.inlined.clients.IKVDocument");
|
||||||
|
|
||||||
|
Object factory = factoryClass.getConstructor().newInstance();
|
||||||
|
|
||||||
|
Object writerOptionsBuilder = optionsBuilderClass.getConstructor().newInstance();
|
||||||
|
writerOptionsBuilder = optionsBuilderClass.getMethod("withStoreName", String.class).invoke(writerOptionsBuilder, storeName);
|
||||||
|
writerOptionsBuilder = optionsBuilderClass.getMethod("withAccountId", String.class).invoke(writerOptionsBuilder, accountId);
|
||||||
|
writerOptionsBuilder = optionsBuilderClass.getMethod("withAccountPassKey", String.class).invoke(writerOptionsBuilder, accountPassKey);
|
||||||
|
Object writerOptions = optionsBuilderClass.getMethod("build").invoke(writerOptionsBuilder);
|
||||||
|
|
||||||
|
Object readerOptionsBuilder = optionsBuilderClass.getConstructor().newInstance();
|
||||||
|
readerOptionsBuilder = optionsBuilderClass.getMethod("withStoreName", String.class).invoke(readerOptionsBuilder, storeName);
|
||||||
|
readerOptionsBuilder = optionsBuilderClass.getMethod("withAccountId", String.class).invoke(readerOptionsBuilder, accountId);
|
||||||
|
readerOptionsBuilder = optionsBuilderClass.getMethod("withAccountPassKey", String.class).invoke(readerOptionsBuilder, accountPassKey);
|
||||||
|
readerOptionsBuilder = optionsBuilderClass.getMethod("withMountDirectory", String.class).invoke(readerOptionsBuilder, mountDirectory);
|
||||||
|
readerOptionsBuilder = optionsBuilderClass.getMethod("useStringPrimaryKey").invoke(readerOptionsBuilder);
|
||||||
|
Object readerOptions = optionsBuilderClass.getMethod("build").invoke(readerOptionsBuilder);
|
||||||
|
|
||||||
|
Method createWriter = factoryClass.getMethod("createNewWriterInstance", optionsClass);
|
||||||
|
Method createReader = factoryClass.getMethod("createNewReaderInstance", optionsClass);
|
||||||
|
|
||||||
|
writer = createWriter.invoke(factory, writerOptions);
|
||||||
|
reader = createReader.invoke(factory, readerOptions);
|
||||||
|
|
||||||
|
writer.getClass().getMethod("startupWriter").invoke(writer);
|
||||||
|
reader.getClass().getMethod("startupReader").invoke(reader);
|
||||||
|
|
||||||
|
Method upsertFieldValues = writer.getClass().getMethod("upsertFieldValues", docClass);
|
||||||
|
|
||||||
|
for (User user : users) {
|
||||||
|
Object docBuilder = docBuilderClass.getConstructor().newInstance();
|
||||||
|
docBuilder = docBuilderClass.getMethod("putStringField", String.class, String.class)
|
||||||
|
.invoke(docBuilder, "id", Long.toString(user.getId()));
|
||||||
|
docBuilder = docBuilderClass.getMethod("putStringField", String.class, String.class)
|
||||||
|
.invoke(docBuilder, "name", user.getName());
|
||||||
|
docBuilder = docBuilderClass.getMethod("putStringField", String.class, String.class)
|
||||||
|
.invoke(docBuilder, "email", user.getEmail());
|
||||||
|
docBuilder = docBuilderClass.getMethod("putIntField", String.class, int.class)
|
||||||
|
.invoke(docBuilder, "age", user.getAge());
|
||||||
|
Object document = docBuilderClass.getMethod("build").invoke(docBuilder);
|
||||||
|
upsertFieldValues.invoke(writer, document);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IKV is eventually consistent for reads. Small delay before measurement starts.
|
||||||
|
Thread.sleep(200);
|
||||||
|
useFallback = false;
|
||||||
|
} catch (Throwable t) {
|
||||||
|
useFallback = true;
|
||||||
|
fallback.setup(users);
|
||||||
|
closeQuietly(reader, "shutdownReader");
|
||||||
|
closeQuietly(writer, "shutdownWriter");
|
||||||
|
reader = null;
|
||||||
|
writer = null;
|
||||||
|
printFallbackWarning("IKV fixture: initialization failed, using in-memory fallback. Cause: "
|
||||||
|
+ t.getClass().getSimpleName() + " - " + t.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User findById(long id) throws Exception {
|
||||||
|
if (useFallback) {
|
||||||
|
return fallback.findById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = Long.toString(id);
|
||||||
|
String name = (String) reader.getClass().getMethod("getStringValue", String.class, String.class)
|
||||||
|
.invoke(reader, key, "name");
|
||||||
|
String email = (String) reader.getClass().getMethod("getStringValue", String.class, String.class)
|
||||||
|
.invoke(reader, key, "email");
|
||||||
|
Object ageObj = reader.getClass().getMethod("getIntValue", String.class, String.class)
|
||||||
|
.invoke(reader, key, "age");
|
||||||
|
|
||||||
|
if (name == null && email == null && ageObj == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int age = ageObj == null ? 0 : (Integer) ageObj;
|
||||||
|
return new User(id, name, email, age, 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void teardown() throws Exception {
|
||||||
|
if (useFallback) {
|
||||||
|
fallback.teardown();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeQuietly(reader, "shutdownReader");
|
||||||
|
closeQuietly(writer, "shutdownWriter");
|
||||||
|
reader = null;
|
||||||
|
writer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getenv(String key) {
|
||||||
|
String value = System.getenv(key);
|
||||||
|
return (value == null || value.isBlank()) ? null : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void closeQuietly(Object target, String methodName) {
|
||||||
|
if (target == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
target.getClass().getMethod(methodName).invoke(target);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
// Ignore cleanup issues for benchmark fixture shutdown.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printFallbackWarning(String message) {
|
||||||
|
if (FALLBACK_WARNING_PRINTED.compareAndSet(false, true)) {
|
||||||
|
System.out.println(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user