-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add some version-store microbenchmarks
This can serve as a tool to later optimize these parts in the Nessie code base: * version store implementation * database specific `Persist` implementations * storate logic implementations So far, the results look good enough, no concerning outliers w/ in-memory, mongo + cassandra. Microbenchmarks use `BackendTestFactory` to locate and initialize a `Backend` (and in turn `Persist`) implementation. Microbenchmarks are intended to inspect the Nessie code down up to the database level, but not intended to optimize configurations for a particular database. In other words: only the Nessie code is interesting, but the performance of the database itself is not (that much). The `BackendTestFactory` interface got a new function `getName()` and the corresponding service definitions have been added. Needed to use a new Gradle project, because the microbenchmarks need the Java `ServiceLoader` for various reasons, so it the JMH uber-jar needs the _shadow_ plugin, but its presence would make `nessie-services.jar` an uber-jar as well - so it's easier to use a separate Gradle project.
- Loading branch information
Showing
24 changed files
with
677 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Nessie VersionStore micro benchmarks | ||
|
||
Building: | ||
```bash | ||
./gradlew :nessie-services-bench:jmhJar | ||
``` | ||
|
||
Running: | ||
```bash | ||
java -jar servers/services-bench/build/libs/nessie-services-bench-*-jmh.jar | ||
``` | ||
|
||
## Async-profiler | ||
|
||
See the [Async Profiler repo](https://github.com/async-profiler/async-profiler) for a pre-built library or how to build | ||
it from source. | ||
|
||
Running (Linux): | ||
```bash | ||
LD_LIBRARY_PATH=(PATH-TO-LIBRARY)/ java \ | ||
-jar servers/services-bench/build/libs/nessie-services-bench-*-jmh.jar \ | ||
-prof async | ||
``` | ||
|
||
## Linux Perf tools | ||
|
||
Install the appropriate `linux-tools` package for your distribution, or `make` it from the Linux sources in `tools/perf` | ||
for your running Linux kernel version. `perf` needs to be in `PATH`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* Copyright (C) 2022 Dremio | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar | ||
|
||
plugins { | ||
`java-library` | ||
`nessie-conventions` | ||
id("com.github.johnrengelman.shadow") | ||
alias(libs.plugins.jmh) | ||
} | ||
|
||
extra["maven.name"] = "Nessie - Services - Microbenchmarks" | ||
|
||
dependencies { | ||
implementation(project(":nessie-model")) | ||
implementation(project(":nessie-versioned-spi")) | ||
implementation(libs.slf4j.api) | ||
|
||
jmhRuntimeOnly(project(":nessie-server-store")) | ||
|
||
implementation(project(":nessie-versioned-storage-common")) | ||
implementation(project(":nessie-versioned-storage-store")) | ||
implementation(project(":nessie-versioned-storage-testextension")) | ||
|
||
jmhImplementation(libs.jmh.core) | ||
jmhAnnotationProcessor(libs.jmh.generator.annprocess) | ||
jmhCompileOnly(libs.microprofile.openapi) | ||
jmhRuntimeOnly(project(":nessie-versioned-storage-inmemory")) | ||
jmhRuntimeOnly(project(":nessie-versioned-storage-cassandra")) | ||
jmhRuntimeOnly(project(":nessie-versioned-storage-rocksdb")) | ||
jmhRuntimeOnly(project(":nessie-versioned-storage-mongodb")) | ||
jmhRuntimeOnly(project(":nessie-versioned-storage-dynamodb")) | ||
jmhRuntimeOnly(project(":nessie-versioned-storage-jdbc")) | ||
jmhRuntimeOnly(libs.testcontainers.testcontainers) | ||
jmhRuntimeOnly(libs.testcontainers.cassandra) | ||
jmhRuntimeOnly(libs.testcontainers.mongodb) | ||
jmhRuntimeOnly(libs.docker.java.api) | ||
jmhRuntimeOnly(libs.agroal.pool) | ||
jmhRuntimeOnly(libs.h2) | ||
} | ||
|
||
jmh { jmhVersion.set(libs.versions.jmh.get()) } | ||
|
||
tasks.named<ShadowJar>("jmhJar") { mergeServiceFiles() } |
79 changes: 79 additions & 0 deletions
79
servers/services-bench/src/jmh/java/org/projectnessie/services/BaseParams.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* Copyright (C) 2023 Dremio | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.projectnessie.services; | ||
|
||
import static org.projectnessie.versioned.storage.common.logic.Logics.repositoryLogic; | ||
|
||
import java.util.HashSet; | ||
import java.util.ServiceLoader; | ||
import java.util.Set; | ||
import org.projectnessie.versioned.VersionStore; | ||
import org.projectnessie.versioned.storage.common.config.StoreConfig; | ||
import org.projectnessie.versioned.storage.common.persist.Backend; | ||
import org.projectnessie.versioned.storage.common.persist.Persist; | ||
import org.projectnessie.versioned.storage.common.persist.PersistFactory; | ||
import org.projectnessie.versioned.storage.testextension.BackendTestFactory; | ||
import org.projectnessie.versioned.storage.versionstore.VersionStoreImpl; | ||
|
||
abstract class BaseParams { | ||
public static final String DEFAULT_BRANCH_NAME = "main"; | ||
|
||
Backend backend; | ||
VersionStore versionStore; | ||
BackendTestFactory backendTestFactory; | ||
|
||
protected void init(String backendName) throws Exception { | ||
Set<String> known = new HashSet<>(); | ||
for (BackendTestFactory candidate : ServiceLoader.load(BackendTestFactory.class)) { | ||
String name = candidate.getName(); | ||
known.add(name); | ||
if (backendName.equals(name)) { | ||
backendTestFactory = candidate; | ||
break; | ||
} | ||
} | ||
if (backendTestFactory == null) { | ||
throw new IllegalArgumentException( | ||
"Could not find backend named " + backendName + ", known backends: " + known); | ||
} | ||
|
||
backendTestFactory.start(); | ||
|
||
backend = backendTestFactory.createNewBackend(); | ||
backend.setupSchema(); | ||
PersistFactory factory = backend.createFactory(); | ||
Persist persist = factory.newPersist(StoreConfig.Adjustable.empty()); | ||
repositoryLogic(persist).initialize(DEFAULT_BRANCH_NAME); | ||
versionStore = new VersionStoreImpl(persist); | ||
} | ||
|
||
protected void tearDown() throws Exception { | ||
if (backend != null) { | ||
try { | ||
backend.close(); | ||
} finally { | ||
backend = null; | ||
} | ||
} | ||
if (backendTestFactory != null) { | ||
try { | ||
backendTestFactory.stop(); | ||
} finally { | ||
backendTestFactory = null; | ||
} | ||
} | ||
} | ||
} |
140 changes: 140 additions & 0 deletions
140
servers/services-bench/src/jmh/java/org/projectnessie/services/ContentOpsBench.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
/* | ||
* Copyright (C) 2023 Dremio | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.projectnessie.services; | ||
|
||
import static java.util.concurrent.TimeUnit.MICROSECONDS; | ||
import static java.util.concurrent.TimeUnit.MILLISECONDS; | ||
import static org.projectnessie.model.CommitMeta.fromMessage; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.concurrent.ThreadLocalRandom; | ||
import org.openjdk.jmh.annotations.Benchmark; | ||
import org.openjdk.jmh.annotations.BenchmarkMode; | ||
import org.openjdk.jmh.annotations.Fork; | ||
import org.openjdk.jmh.annotations.Measurement; | ||
import org.openjdk.jmh.annotations.Mode; | ||
import org.openjdk.jmh.annotations.OutputTimeUnit; | ||
import org.openjdk.jmh.annotations.Param; | ||
import org.openjdk.jmh.annotations.Scope; | ||
import org.openjdk.jmh.annotations.Setup; | ||
import org.openjdk.jmh.annotations.State; | ||
import org.openjdk.jmh.annotations.TearDown; | ||
import org.openjdk.jmh.annotations.Threads; | ||
import org.openjdk.jmh.annotations.Warmup; | ||
import org.openjdk.jmh.infra.Blackhole; | ||
import org.projectnessie.model.ContentKey; | ||
import org.projectnessie.model.IcebergTable; | ||
import org.projectnessie.model.Namespace; | ||
import org.projectnessie.versioned.BranchName; | ||
import org.projectnessie.versioned.ContentResult; | ||
import org.projectnessie.versioned.KeyEntry; | ||
import org.projectnessie.versioned.Operation; | ||
import org.projectnessie.versioned.Put; | ||
import org.projectnessie.versioned.ReferenceCreatedResult; | ||
import org.projectnessie.versioned.paging.PaginationIterator; | ||
|
||
@Warmup(iterations = 2, time = 2000, timeUnit = MILLISECONDS) | ||
@Measurement(iterations = 3, time = 1000, timeUnit = MILLISECONDS) | ||
@Fork( | ||
value = 1, | ||
jvmArgs = {"-Xms8g", "-Xmx8g"}) | ||
@Threads(1) | ||
@BenchmarkMode(Mode.AverageTime) | ||
@OutputTimeUnit(MICROSECONDS) | ||
public class ContentOpsBench { | ||
|
||
@State(Scope.Benchmark) | ||
public static class BenchmarkParam extends BaseParams { | ||
|
||
@Param({"100", "1000", "10000"}) | ||
public int contents; | ||
|
||
@Param({"In-Memory"}) | ||
public String backendName; | ||
|
||
ReferenceCreatedResult ref; | ||
List<ContentKey> keys = new ArrayList<>(); | ||
|
||
@Setup | ||
public void setup() throws Exception { | ||
super.init(backendName); | ||
|
||
Namespace ns = Namespace.of("my-namespace"); | ||
|
||
BranchName branchName = BranchName.of("branch"); | ||
ref = versionStore.create(branchName, Optional.empty()); | ||
|
||
versionStore.commit( | ||
branchName, | ||
Optional.empty(), | ||
fromMessage("initial"), | ||
Collections.singletonList(Put.of(ns.toContentKey(), ns))); | ||
List<Operation> commitOps = new ArrayList<>(); | ||
for (int j = 0; j < contents; j++) { | ||
ContentKey key = ContentKey.of(ns, "table-" + j); | ||
keys.add(key); | ||
commitOps.add(Put.of(key, IcebergTable.of("meta-" + j, j, j, j, j))); | ||
if (commitOps.size() == 500) { | ||
versionStore.commit(branchName, Optional.empty(), fromMessage("x"), commitOps); | ||
commitOps.clear(); | ||
} | ||
} | ||
if (!commitOps.isEmpty()) { | ||
versionStore.commit(branchName, Optional.empty(), fromMessage("x"), commitOps); | ||
} | ||
} | ||
|
||
@Override | ||
@TearDown | ||
public void tearDown() throws Exception { | ||
super.tearDown(); | ||
} | ||
|
||
public ContentKey randomKey() { | ||
return keys.get(ThreadLocalRandom.current().nextInt(contents)); | ||
} | ||
} | ||
|
||
@Benchmark | ||
public void getKeys(BenchmarkParam param, Blackhole bh) throws Exception { | ||
PaginationIterator<KeyEntry> iter = | ||
param.versionStore.getKeys(param.ref.getNamedRef(), null, false, null, null, null, null); | ||
while (iter.hasNext()) { | ||
bh.consume(iter.next()); | ||
} | ||
} | ||
|
||
@Benchmark | ||
public ContentResult getValue(BenchmarkParam param) throws Exception { | ||
ContentKey key = param.randomKey(); | ||
return param.versionStore.getValue(param.ref.getNamedRef(), key); | ||
} | ||
|
||
@Benchmark | ||
public Map<ContentKey, ContentResult> getTenValues(BenchmarkParam param) throws Exception { | ||
Set<ContentKey> k = new HashSet<>(); | ||
while (k.size() < 10) { | ||
k.add(param.randomKey()); | ||
} | ||
return param.versionStore.getValues(param.ref.getNamedRef(), k); | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
servers/services-bench/src/jmh/java/org/projectnessie/services/CreateReferencesBench.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* | ||
* Copyright (C) 2023 Dremio | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.projectnessie.services; | ||
|
||
import static java.util.concurrent.TimeUnit.MICROSECONDS; | ||
import static java.util.concurrent.TimeUnit.MILLISECONDS; | ||
|
||
import java.util.Optional; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
import org.openjdk.jmh.annotations.Benchmark; | ||
import org.openjdk.jmh.annotations.BenchmarkMode; | ||
import org.openjdk.jmh.annotations.Fork; | ||
import org.openjdk.jmh.annotations.Measurement; | ||
import org.openjdk.jmh.annotations.Mode; | ||
import org.openjdk.jmh.annotations.OutputTimeUnit; | ||
import org.openjdk.jmh.annotations.Param; | ||
import org.openjdk.jmh.annotations.Scope; | ||
import org.openjdk.jmh.annotations.Setup; | ||
import org.openjdk.jmh.annotations.State; | ||
import org.openjdk.jmh.annotations.TearDown; | ||
import org.openjdk.jmh.annotations.Threads; | ||
import org.openjdk.jmh.annotations.Warmup; | ||
import org.projectnessie.versioned.BranchName; | ||
import org.projectnessie.versioned.ReferenceCreatedResult; | ||
|
||
@Warmup(iterations = 2, time = 2000, timeUnit = MILLISECONDS) | ||
@Measurement(iterations = 3, time = 1000, timeUnit = MILLISECONDS) | ||
@Fork(1) | ||
@Threads(1) | ||
@BenchmarkMode(Mode.AverageTime) | ||
@OutputTimeUnit(MICROSECONDS) | ||
public class CreateReferencesBench { | ||
|
||
@State(Scope.Benchmark) | ||
public static class BenchmarkParam extends BaseParams { | ||
|
||
@Param({"In-Memory"}) | ||
public String backendName; | ||
|
||
@Setup | ||
public void setup() throws Exception { | ||
super.init(backendName); | ||
} | ||
|
||
@Override | ||
@TearDown | ||
public void tearDown() throws Exception { | ||
super.tearDown(); | ||
} | ||
|
||
AtomicInteger createReferences = new AtomicInteger(); | ||
} | ||
|
||
@Benchmark | ||
public ReferenceCreatedResult createBranch(BenchmarkParam param) throws Exception { | ||
return param.versionStore.create( | ||
BranchName.of("branch-" + param.createReferences.getAndIncrement()), Optional.empty()); | ||
} | ||
} |
Oops, something went wrong.