JMH microbenchmarks for RocksJava (#6241)
Summary: This is the start of some JMH microbenchmarks for RocksJava. Such benchmarks can help us decide on performance improvements of the Java API. At the moment, I have only added benchmarks for various Comparator options, as that is one of the first areas where I want to improve performance. I plan to expand this to many more tests. Details of how to compile and run the benchmarks are in the `README.md`. A run of these on a XEON 3.5 GHz 4vCPU (QEMU Virtual CPU version 2.5+) / 8GB RAM KVM with Ubuntu 18.04, OpenJDK 1.8.0_232, and gcc 8.3.0 produced the following: ``` # Run complete. Total time: 01:43:17 REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial experiments, perform baseline and negative tests that provide experimental control, make sure the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts. Do not assume the numbers tell you what you want them to tell. Benchmark (comparatorName) Mode Cnt Score Error Units ComparatorBenchmarks.put native_bytewise thrpt 25 122373.920 ± 2200.538 ops/s ComparatorBenchmarks.put java_bytewise_adaptive_mutex thrpt 25 17388.201 ± 1444.006 ops/s ComparatorBenchmarks.put java_bytewise_non-adaptive_mutex thrpt 25 16887.150 ± 1632.204 ops/s ComparatorBenchmarks.put java_direct_bytewise_adaptive_mutex thrpt 25 15644.572 ± 1791.189 ops/s ComparatorBenchmarks.put java_direct_bytewise_non-adaptive_mutex thrpt 25 14869.601 ± 2252.135 ops/s ComparatorBenchmarks.put native_reverse_bytewise thrpt 25 116528.735 ± 4168.797 ops/s ComparatorBenchmarks.put java_reverse_bytewise_adaptive_mutex thrpt 25 10651.975 ± 545.998 ops/s ComparatorBenchmarks.put java_reverse_bytewise_non-adaptive_mutex thrpt 25 10514.224 ± 930.069 ops/s ``` Indicating a ~7x difference between comparators implemented natively (C++) and those implemented in Java. Let's see if we can't improve on that in the near future... Pull Request resolved: https://github.com/facebook/rocksdb/pull/6241 Differential Revision: D19290410 Pulled By: pdillinger fbshipit-source-id: 25d44bf3a31de265502ed0c5d8a28cf4c7cb9c0bmain
parent
5709e97a74
commit
6477075f2c
@ -0,0 +1,5 @@ |
|||||||
|
Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
||||||
|
This source code is licensed under both the GPLv2 (found in the |
||||||
|
COPYING file in the root directory) and Apache 2.0 License |
||||||
|
(found in the LICENSE.Apache file in the root directory). |
||||||
|
|
@ -0,0 +1,18 @@ |
|||||||
|
# JMH Benchmarks for RocksJava |
||||||
|
|
||||||
|
These are micro-benchmarks for RocksJava functionality, using [JMH (Java Microbenchmark Harness)](https://openjdk.java.net/projects/code-tools/jmh/). |
||||||
|
|
||||||
|
## Compiling |
||||||
|
|
||||||
|
**Note**: This uses a specific build of RocksDB that is set in the `<version>` element of the `dependencies` section of the `pom.xml` file. If you are testing local changes you should build and install a SNAPSHOT version of rocksdbjni, and update the `pom.xml` of rocksdbjni-jmh file to test with this. |
||||||
|
|
||||||
|
```bash |
||||||
|
$ mvn package |
||||||
|
``` |
||||||
|
|
||||||
|
## Running |
||||||
|
```bash |
||||||
|
$ java -jar target/rocksdbjni-jmh-1.0-SNAPSHOT-benchmarks.jar |
||||||
|
``` |
||||||
|
|
||||||
|
NOTE: you can append `-help` to the command above to see all of the JMH runtime options. |
@ -0,0 +1,138 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||||
|
<modelVersion>4.0.0</modelVersion> |
||||||
|
|
||||||
|
<groupId>org.rocksdb</groupId> |
||||||
|
<artifactId>rocksdbjni-jmh</artifactId> |
||||||
|
<version>1.0-SNAPSHOT</version> |
||||||
|
|
||||||
|
<url>http://rocksdb.org/</url> |
||||||
|
|
||||||
|
<name>rocksdbjni-jmh</name> |
||||||
|
<description>JMH Benchmarks for RocksDB Java API</description> |
||||||
|
|
||||||
|
<organization> |
||||||
|
<name>Facebook, Inc.</name> |
||||||
|
<url>https://www.facebook.com</url> |
||||||
|
</organization> |
||||||
|
|
||||||
|
<licenses> |
||||||
|
<license> |
||||||
|
<name>Apache License 2.0</name> |
||||||
|
<url>http://www.apache.org/licenses/LICENSE-2.0.html</url> |
||||||
|
<distribution>repo</distribution> |
||||||
|
</license> |
||||||
|
<license> |
||||||
|
<name>GNU General Public License, version 2</name> |
||||||
|
<url>http://www.gnu.org/licenses/gpl-2.0.html</url> |
||||||
|
<distribution>repo</distribution> |
||||||
|
</license> |
||||||
|
</licenses> |
||||||
|
|
||||||
|
<scm> |
||||||
|
<connection>scm:git:git://github.com/facebook/rocksdb.git</connection> |
||||||
|
<developerConnection>scm:git:git@github.com:facebook/rocksdb.git</developerConnection> |
||||||
|
<url>http://github.com/facebook/rocksdb/</url> |
||||||
|
</scm> |
||||||
|
|
||||||
|
<properties> |
||||||
|
<project.build.source>1.7</project.build.source> |
||||||
|
<project.build.target>1.7</project.build.target> |
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||||
|
|
||||||
|
<jmh.version>1.22</jmh.version> |
||||||
|
<uberjar.name>benchmarks</uberjar.name> |
||||||
|
</properties> |
||||||
|
|
||||||
|
<dependencies> |
||||||
|
<dependency> |
||||||
|
<groupId>org.rocksdb</groupId> |
||||||
|
<artifactId>rocksdbjni</artifactId> |
||||||
|
<version>6.4.6</version> |
||||||
|
</dependency> |
||||||
|
|
||||||
|
<dependency> |
||||||
|
<groupId>org.openjdk.jmh</groupId> |
||||||
|
<artifactId>jmh-core</artifactId> |
||||||
|
<version>${jmh.version}</version> |
||||||
|
</dependency> |
||||||
|
<dependency> |
||||||
|
<groupId>org.openjdk.jmh</groupId> |
||||||
|
<artifactId>jmh-generator-annprocess</artifactId> |
||||||
|
<version>${jmh.version}</version> |
||||||
|
<scope>provided</scope> |
||||||
|
</dependency> |
||||||
|
</dependencies> |
||||||
|
|
||||||
|
<build> |
||||||
|
<plugins> |
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||||
|
<version>3.8.1</version> |
||||||
|
<configuration> |
||||||
|
<source>${project.build.source}</source> |
||||||
|
<target>${project.build.target}</target> |
||||||
|
<encoding>${project.build.sourceEncoding}</encoding> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>com.mycila</groupId> |
||||||
|
<artifactId>license-maven-plugin</artifactId> |
||||||
|
<version>3.0</version> |
||||||
|
<inherited>true</inherited> |
||||||
|
<configuration> |
||||||
|
<header>LICENSE-HEADER.txt</header> |
||||||
|
<failIfMissing>true</failIfMissing> |
||||||
|
<aggregate>true</aggregate> |
||||||
|
<strictCheck>true</strictCheck> |
||||||
|
<excludes> |
||||||
|
<exclude>pom.xml</exclude> |
||||||
|
</excludes> |
||||||
|
<encoding>${project.build.sourceEncoding}</encoding> |
||||||
|
</configuration> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
<plugin> |
||||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||||
|
<artifactId>maven-shade-plugin</artifactId> |
||||||
|
<version>3.2.1</version> |
||||||
|
<executions> |
||||||
|
<execution> |
||||||
|
<phase>package</phase> |
||||||
|
<goals> |
||||||
|
<goal>shade</goal> |
||||||
|
</goals> |
||||||
|
<configuration> |
||||||
|
<finalName>${project.artifactId}-${project.version}-${uberjar.name}</finalName> |
||||||
|
<transformers> |
||||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> |
||||||
|
<mainClass>org.openjdk.jmh.Main</mainClass> |
||||||
|
</transformer> |
||||||
|
</transformers> |
||||||
|
<filters> |
||||||
|
<filter> |
||||||
|
<!-- |
||||||
|
Shading signed JARs will fail without this. |
||||||
|
http://stackoverflow.com/questions/999489/invalid-signature-file-when-attempting-to-run-a-jar |
||||||
|
--> |
||||||
|
<artifact>*:*</artifact> |
||||||
|
<excludes> |
||||||
|
<exclude>META-INF/*.SF</exclude> |
||||||
|
<exclude>META-INF/*.DSA</exclude> |
||||||
|
<exclude>META-INF/*.RSA</exclude> |
||||||
|
</excludes> |
||||||
|
</filter> |
||||||
|
</filters> |
||||||
|
</configuration> |
||||||
|
</execution> |
||||||
|
</executions> |
||||||
|
</plugin> |
||||||
|
|
||||||
|
</plugins> |
||||||
|
</build> |
||||||
|
|
||||||
|
</project> |
@ -0,0 +1,122 @@ |
|||||||
|
/** |
||||||
|
* Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
||||||
|
* This source code is licensed under both the GPLv2 (found in the |
||||||
|
* COPYING file in the root directory) and Apache 2.0 License |
||||||
|
* (found in the LICENSE.Apache file in the root directory). |
||||||
|
*/ |
||||||
|
package org.rocksdb.jmh; |
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.*; |
||||||
|
import org.rocksdb.*; |
||||||
|
import org.rocksdb.util.BytewiseComparator; |
||||||
|
import org.rocksdb.util.DirectBytewiseComparator; |
||||||
|
import org.rocksdb.util.FileUtils; |
||||||
|
import org.rocksdb.util.ReverseBytewiseComparator; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||||
|
|
||||||
|
import static org.rocksdb.util.KVUtils.ba; |
||||||
|
|
||||||
|
@State(Scope.Benchmark) |
||||||
|
public class ComparatorBenchmarks { |
||||||
|
|
||||||
|
@Param({ |
||||||
|
"native_bytewise", |
||||||
|
"native_reverse_bytewise", |
||||||
|
"java_bytewise_adaptive_mutex", |
||||||
|
"java_bytewise_non-adaptive_mutex", |
||||||
|
"java_reverse_bytewise_adaptive_mutex", |
||||||
|
"java_reverse_bytewise_non-adaptive_mutex", |
||||||
|
"java_direct_bytewise_adaptive_mutex", |
||||||
|
"java_direct_bytewise_non-adaptive_mutex" |
||||||
|
|
||||||
|
}) |
||||||
|
public String comparatorName; |
||||||
|
|
||||||
|
Path dbDir; |
||||||
|
ComparatorOptions comparatorOptions; |
||||||
|
AbstractComparator comparator; |
||||||
|
Options options; |
||||||
|
RocksDB db; |
||||||
|
|
||||||
|
@Setup(Level.Trial) |
||||||
|
public void setup() throws IOException, RocksDBException { |
||||||
|
RocksDB.loadLibrary(); |
||||||
|
|
||||||
|
dbDir = Files.createTempDirectory("rocksjava-comparator-benchmarks"); |
||||||
|
|
||||||
|
options = new Options() |
||||||
|
.setCreateIfMissing(true); |
||||||
|
if (comparatorName == null || "native_bytewise".equals(comparatorName)) { |
||||||
|
options.setComparator(BuiltinComparator.BYTEWISE_COMPARATOR); |
||||||
|
} else if ("native_reverse_bytewise".equals(comparatorName)) { |
||||||
|
options.setComparator(BuiltinComparator.REVERSE_BYTEWISE_COMPARATOR); |
||||||
|
} else if ("java_bytewise_adaptive_mutex".equals(comparatorName)) { |
||||||
|
comparatorOptions = new ComparatorOptions() |
||||||
|
.setUseAdaptiveMutex(true); |
||||||
|
comparator = new BytewiseComparator(comparatorOptions); |
||||||
|
options.setComparator(comparator); |
||||||
|
} else if ("java_bytewise_non-adaptive_mutex".equals(comparatorName)) { |
||||||
|
comparatorOptions = new ComparatorOptions() |
||||||
|
.setUseAdaptiveMutex(false); |
||||||
|
comparator = new BytewiseComparator(comparatorOptions); |
||||||
|
options.setComparator(comparator); |
||||||
|
} else if ("java_reverse_bytewise_adaptive_mutex".equals(comparatorName)) { |
||||||
|
comparatorOptions = new ComparatorOptions() |
||||||
|
.setUseAdaptiveMutex(true); |
||||||
|
comparator = new ReverseBytewiseComparator(comparatorOptions); |
||||||
|
options.setComparator(comparator); |
||||||
|
} else if ("java_reverse_bytewise_non-adaptive_mutex".equals(comparatorName)) { |
||||||
|
comparatorOptions = new ComparatorOptions() |
||||||
|
.setUseAdaptiveMutex(false); |
||||||
|
comparator = new ReverseBytewiseComparator(comparatorOptions); |
||||||
|
options.setComparator(comparator); |
||||||
|
} else if ("java_direct_bytewise_adaptive_mutex".equals(comparatorName)) { |
||||||
|
comparatorOptions = new ComparatorOptions() |
||||||
|
.setUseAdaptiveMutex(true); |
||||||
|
comparator = new DirectBytewiseComparator(comparatorOptions); |
||||||
|
options.setComparator(comparator); |
||||||
|
} else if ("java_direct_bytewise_non-adaptive_mutex".equals(comparatorName)) { |
||||||
|
comparatorOptions = new ComparatorOptions() |
||||||
|
.setUseAdaptiveMutex(false); |
||||||
|
comparator = new DirectBytewiseComparator(comparatorOptions); |
||||||
|
options.setComparator(comparator); |
||||||
|
} else { |
||||||
|
throw new IllegalArgumentException("Unknown comparator name: " + comparatorName); |
||||||
|
} |
||||||
|
|
||||||
|
db = RocksDB.open(options, dbDir.toAbsolutePath().toString()); |
||||||
|
} |
||||||
|
|
||||||
|
@TearDown(Level.Trial) |
||||||
|
public void cleanup() throws IOException { |
||||||
|
db.close(); |
||||||
|
if (comparator != null) { |
||||||
|
comparator.close(); |
||||||
|
} |
||||||
|
if (comparatorOptions != null) { |
||||||
|
comparatorOptions.close(); |
||||||
|
} |
||||||
|
options.close(); |
||||||
|
FileUtils.delete(dbDir); |
||||||
|
} |
||||||
|
|
||||||
|
@State(Scope.Benchmark) |
||||||
|
public static class Counter { |
||||||
|
private final AtomicInteger count = new AtomicInteger(); |
||||||
|
|
||||||
|
public int next() { |
||||||
|
return count.getAndIncrement(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Benchmark |
||||||
|
public void put(final Counter counter) throws RocksDBException { |
||||||
|
final int i = counter.next(); |
||||||
|
db.put(ba("key" + i), ba("value" + i)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,139 @@ |
|||||||
|
/** |
||||||
|
* Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
||||||
|
* This source code is licensed under both the GPLv2 (found in the |
||||||
|
* COPYING file in the root directory) and Apache 2.0 License |
||||||
|
* (found in the LICENSE.Apache file in the root directory). |
||||||
|
*/ |
||||||
|
package org.rocksdb.jmh; |
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.*; |
||||||
|
import org.rocksdb.*; |
||||||
|
import org.rocksdb.util.FileUtils; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||||
|
|
||||||
|
import static org.rocksdb.util.KVUtils.ba; |
||||||
|
|
||||||
|
@State(Scope.Benchmark) |
||||||
|
public class GetBenchmarks { |
||||||
|
|
||||||
|
@Param({ |
||||||
|
"no_column_family", |
||||||
|
"1_column_family", |
||||||
|
"20_column_families", |
||||||
|
"100_column_families" |
||||||
|
}) |
||||||
|
String columnFamilyTestType; |
||||||
|
|
||||||
|
@Param("100000") |
||||||
|
int keyCount; |
||||||
|
|
||||||
|
Path dbDir; |
||||||
|
DBOptions options; |
||||||
|
int cfs = 0; // number of column families
|
||||||
|
private AtomicInteger cfHandlesIdx; |
||||||
|
ColumnFamilyHandle[] cfHandles; |
||||||
|
RocksDB db; |
||||||
|
private final AtomicInteger keyIndex = new AtomicInteger(); |
||||||
|
|
||||||
|
@Setup(Level.Trial) |
||||||
|
public void setup() throws IOException, RocksDBException { |
||||||
|
RocksDB.loadLibrary(); |
||||||
|
|
||||||
|
dbDir = Files.createTempDirectory("rocksjava-get-benchmarks"); |
||||||
|
|
||||||
|
options = new DBOptions() |
||||||
|
.setCreateIfMissing(true) |
||||||
|
.setCreateMissingColumnFamilies(true); |
||||||
|
|
||||||
|
final List<ColumnFamilyDescriptor> cfDescriptors = new ArrayList<>(); |
||||||
|
cfDescriptors.add(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY)); |
||||||
|
|
||||||
|
if ("1_column_family".equals(columnFamilyTestType)) { |
||||||
|
cfs = 1; |
||||||
|
} else if ("20_column_families".equals(columnFamilyTestType)) { |
||||||
|
cfs = 20; |
||||||
|
} else if ("100_column_families".equals(columnFamilyTestType)) { |
||||||
|
cfs = 100; |
||||||
|
} |
||||||
|
|
||||||
|
if (cfs > 0) { |
||||||
|
cfHandlesIdx = new AtomicInteger(1); |
||||||
|
for (int i = 1; i <= cfs; i++) { |
||||||
|
cfDescriptors.add(new ColumnFamilyDescriptor(ba("cf" + i))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final List<ColumnFamilyHandle> cfHandlesList = new ArrayList<>(cfDescriptors.size()); |
||||||
|
db = RocksDB.open(options, dbDir.toAbsolutePath().toString(), cfDescriptors, cfHandlesList); |
||||||
|
cfHandles = cfHandlesList.toArray(new ColumnFamilyHandle[0]); |
||||||
|
|
||||||
|
// store initial data for retrieving via get
|
||||||
|
for (int i = 0; i < cfs; i++) { |
||||||
|
for (int j = 0; j < keyCount; j++) { |
||||||
|
db.put(cfHandles[i], ba("key" + j), ba("value" + j)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
try (final FlushOptions flushOptions = new FlushOptions() |
||||||
|
.setWaitForFlush(true)) { |
||||||
|
db.flush(flushOptions); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@TearDown(Level.Trial) |
||||||
|
public void cleanup() throws IOException { |
||||||
|
for (final ColumnFamilyHandle cfHandle : cfHandles) { |
||||||
|
cfHandle.close(); |
||||||
|
} |
||||||
|
db.close(); |
||||||
|
options.close(); |
||||||
|
FileUtils.delete(dbDir); |
||||||
|
} |
||||||
|
|
||||||
|
private ColumnFamilyHandle getColumnFamily() { |
||||||
|
if (cfs == 0) { |
||||||
|
return cfHandles[0]; |
||||||
|
} else if (cfs == 1) { |
||||||
|
return cfHandles[1]; |
||||||
|
} else { |
||||||
|
int idx = cfHandlesIdx.getAndIncrement(); |
||||||
|
if (idx > cfs) { |
||||||
|
cfHandlesIdx.set(1); // doesn't ensure a perfect distribution, but it's ok
|
||||||
|
idx = 0; |
||||||
|
} |
||||||
|
return cfHandles[idx]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Takes the next position in the index. |
||||||
|
*/ |
||||||
|
private int next() { |
||||||
|
int idx; |
||||||
|
int nextIdx; |
||||||
|
while (true) { |
||||||
|
idx = keyIndex.get(); |
||||||
|
nextIdx = idx + 1; |
||||||
|
if (nextIdx >= keyCount) { |
||||||
|
nextIdx = 0; |
||||||
|
} |
||||||
|
|
||||||
|
if (keyIndex.compareAndSet(idx, nextIdx)) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return idx; |
||||||
|
} |
||||||
|
|
||||||
|
@Benchmark |
||||||
|
public byte[] get() throws RocksDBException { |
||||||
|
final int keyIdx = next(); |
||||||
|
return db.get(getColumnFamily(), ba("key" + keyIdx)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,158 @@ |
|||||||
|
/** |
||||||
|
* Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
||||||
|
* This source code is licensed under both the GPLv2 (found in the |
||||||
|
* COPYING file in the root directory) and Apache 2.0 License |
||||||
|
* (found in the LICENSE.Apache file in the root directory). |
||||||
|
*/ |
||||||
|
package org.rocksdb.jmh; |
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.*; |
||||||
|
import org.rocksdb.*; |
||||||
|
import org.rocksdb.util.FileUtils; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||||
|
|
||||||
|
import static org.rocksdb.util.KVUtils.ba; |
||||||
|
import static org.rocksdb.util.KVUtils.keys; |
||||||
|
|
||||||
|
@State(Scope.Benchmark) |
||||||
|
public class MultiGetBenchmarks { |
||||||
|
|
||||||
|
@Param({ |
||||||
|
"no_column_family", |
||||||
|
"1_column_family", |
||||||
|
"20_column_families", |
||||||
|
"100_column_families" |
||||||
|
}) |
||||||
|
String columnFamilyTestType; |
||||||
|
|
||||||
|
@Param("100000") |
||||||
|
int keyCount; |
||||||
|
|
||||||
|
@Param({ |
||||||
|
"10", |
||||||
|
"100", |
||||||
|
"1000", |
||||||
|
"10000", |
||||||
|
}) |
||||||
|
int multiGetSize; |
||||||
|
|
||||||
|
Path dbDir; |
||||||
|
DBOptions options; |
||||||
|
int cfs = 0; // number of column families
|
||||||
|
private AtomicInteger cfHandlesIdx; |
||||||
|
ColumnFamilyHandle[] cfHandles; |
||||||
|
RocksDB db; |
||||||
|
private final AtomicInteger keyIndex = new AtomicInteger(); |
||||||
|
|
||||||
|
@Setup(Level.Trial) |
||||||
|
public void setup() throws IOException, RocksDBException { |
||||||
|
RocksDB.loadLibrary(); |
||||||
|
|
||||||
|
dbDir = Files.createTempDirectory("rocksjava-multiget-benchmarks"); |
||||||
|
|
||||||
|
options = new DBOptions() |
||||||
|
.setCreateIfMissing(true) |
||||||
|
.setCreateMissingColumnFamilies(true); |
||||||
|
|
||||||
|
final List<ColumnFamilyDescriptor> cfDescriptors = new ArrayList<>(); |
||||||
|
cfDescriptors.add(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY)); |
||||||
|
|
||||||
|
if ("1_column_family".equals(columnFamilyTestType)) { |
||||||
|
cfs = 1; |
||||||
|
} else if ("20_column_families".equals(columnFamilyTestType)) { |
||||||
|
cfs = 20; |
||||||
|
} else if ("100_column_families".equals(columnFamilyTestType)) { |
||||||
|
cfs = 100; |
||||||
|
} |
||||||
|
|
||||||
|
if (cfs > 0) { |
||||||
|
cfHandlesIdx = new AtomicInteger(1); |
||||||
|
for (int i = 1; i <= cfs; i++) { |
||||||
|
cfDescriptors.add(new ColumnFamilyDescriptor(ba("cf" + i))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final List<ColumnFamilyHandle> cfHandlesList = new ArrayList<>(cfDescriptors.size()); |
||||||
|
db = RocksDB.open(options, dbDir.toAbsolutePath().toString(), cfDescriptors, cfHandlesList); |
||||||
|
cfHandles = cfHandlesList.toArray(new ColumnFamilyHandle[0]); |
||||||
|
|
||||||
|
// store initial data for retrieving via get
|
||||||
|
for (int i = 0; i < cfs; i++) { |
||||||
|
for (int j = 0; j < keyCount; j++) { |
||||||
|
db.put(cfHandles[i], ba("key" + j), ba("value" + j)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
try (final FlushOptions flushOptions = new FlushOptions() |
||||||
|
.setWaitForFlush(true)) { |
||||||
|
db.flush(flushOptions); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@TearDown(Level.Trial) |
||||||
|
public void cleanup() throws IOException { |
||||||
|
for (final ColumnFamilyHandle cfHandle : cfHandles) { |
||||||
|
cfHandle.close(); |
||||||
|
} |
||||||
|
db.close(); |
||||||
|
options.close(); |
||||||
|
FileUtils.delete(dbDir); |
||||||
|
} |
||||||
|
|
||||||
|
private ColumnFamilyHandle getColumnFamily() { |
||||||
|
if (cfs == 0) { |
||||||
|
return cfHandles[0]; |
||||||
|
} else if (cfs == 1) { |
||||||
|
return cfHandles[1]; |
||||||
|
} else { |
||||||
|
int idx = cfHandlesIdx.getAndIncrement(); |
||||||
|
if (idx > cfs) { |
||||||
|
cfHandlesIdx.set(1); // doesn't ensure a perfect distribution, but it's ok
|
||||||
|
idx = 0; |
||||||
|
} |
||||||
|
return cfHandles[idx]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reserves the next {@inc} positions in the index. |
||||||
|
* |
||||||
|
* @param inc the number by which to increment the index |
||||||
|
* @param limit the limit for the index |
||||||
|
* @return the index before {@code inc} is added |
||||||
|
*/ |
||||||
|
private int next(final int inc, final int limit) { |
||||||
|
int idx; |
||||||
|
int nextIdx; |
||||||
|
while (true) { |
||||||
|
idx = keyIndex.get(); |
||||||
|
nextIdx = idx + inc; |
||||||
|
if (nextIdx >= limit) { |
||||||
|
nextIdx = inc; |
||||||
|
} |
||||||
|
|
||||||
|
if (keyIndex.compareAndSet(idx, nextIdx)) { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (nextIdx >= limit) { |
||||||
|
return -1; |
||||||
|
} else { |
||||||
|
return idx; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Benchmark |
||||||
|
public List<byte[]> multiGet10() throws RocksDBException { |
||||||
|
final int fromKeyIdx = next(multiGetSize, keyCount); |
||||||
|
final List<byte[]> keys = keys(fromKeyIdx, fromKeyIdx + multiGetSize); |
||||||
|
return db.multiGetAsList(keys); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
/** |
||||||
|
* Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
||||||
|
* This source code is licensed under both the GPLv2 (found in the |
||||||
|
* COPYING file in the root directory) and Apache 2.0 License |
||||||
|
* (found in the LICENSE.Apache file in the root directory). |
||||||
|
*/ |
||||||
|
package org.rocksdb.jmh; |
||||||
|
|
||||||
|
import org.openjdk.jmh.annotations.*; |
||||||
|
import org.rocksdb.*; |
||||||
|
import org.rocksdb.util.FileUtils; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||||
|
|
||||||
|
import static org.rocksdb.util.KVUtils.ba; |
||||||
|
|
||||||
|
@State(Scope.Benchmark) |
||||||
|
public class PutBenchmarks { |
||||||
|
|
||||||
|
@Param({ |
||||||
|
"no_column_family", |
||||||
|
"1_column_family", |
||||||
|
"20_column_families", |
||||||
|
"100_column_families" |
||||||
|
}) |
||||||
|
String columnFamilyTestType; |
||||||
|
|
||||||
|
Path dbDir; |
||||||
|
DBOptions options; |
||||||
|
int cfs = 0; // number of column families
|
||||||
|
private AtomicInteger cfHandlesIdx; |
||||||
|
ColumnFamilyHandle[] cfHandles; |
||||||
|
RocksDB db; |
||||||
|
|
||||||
|
@Setup(Level.Trial) |
||||||
|
public void setup() throws IOException, RocksDBException { |
||||||
|
RocksDB.loadLibrary(); |
||||||
|
|
||||||
|
dbDir = Files.createTempDirectory("rocksjava-put-benchmarks"); |
||||||
|
|
||||||
|
options = new DBOptions() |
||||||
|
.setCreateIfMissing(true) |
||||||
|
.setCreateMissingColumnFamilies(true); |
||||||
|
|
||||||
|
final List<ColumnFamilyDescriptor> cfDescriptors = new ArrayList<>(); |
||||||
|
cfDescriptors.add(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY)); |
||||||
|
|
||||||
|
if ("1_column_family".equals(columnFamilyTestType)) { |
||||||
|
cfs = 1; |
||||||
|
} else if ("20_column_families".equals(columnFamilyTestType)) { |
||||||
|
cfs = 20; |
||||||
|
} else if ("100_column_families".equals(columnFamilyTestType)) { |
||||||
|
cfs = 100; |
||||||
|
} |
||||||
|
|
||||||
|
if (cfs > 0) { |
||||||
|
cfHandlesIdx = new AtomicInteger(1); |
||||||
|
for (int i = 1; i <= cfs; i++) { |
||||||
|
cfDescriptors.add(new ColumnFamilyDescriptor(ba("cf" + i))); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final List<ColumnFamilyHandle> cfHandlesList = new ArrayList<>(cfDescriptors.size()); |
||||||
|
db = RocksDB.open(options, dbDir.toAbsolutePath().toString(), cfDescriptors, cfHandlesList); |
||||||
|
cfHandles = cfHandlesList.toArray(new ColumnFamilyHandle[0]); |
||||||
|
} |
||||||
|
|
||||||
|
@TearDown(Level.Trial) |
||||||
|
public void cleanup() throws IOException { |
||||||
|
for (final ColumnFamilyHandle cfHandle : cfHandles) { |
||||||
|
cfHandle.close(); |
||||||
|
} |
||||||
|
db.close(); |
||||||
|
options.close(); |
||||||
|
FileUtils.delete(dbDir); |
||||||
|
} |
||||||
|
|
||||||
|
private ColumnFamilyHandle getColumnFamily() { |
||||||
|
if (cfs == 0) { |
||||||
|
return cfHandles[0]; |
||||||
|
} else if (cfs == 1) { |
||||||
|
return cfHandles[1]; |
||||||
|
} else { |
||||||
|
int idx = cfHandlesIdx.getAndIncrement(); |
||||||
|
if (idx > cfs) { |
||||||
|
cfHandlesIdx.set(1); // doesn't ensure a perfect distribution, but it's ok
|
||||||
|
idx = 0; |
||||||
|
} |
||||||
|
return cfHandles[idx]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@State(Scope.Benchmark) |
||||||
|
public static class Counter { |
||||||
|
private final AtomicInteger count = new AtomicInteger(); |
||||||
|
|
||||||
|
public int next() { |
||||||
|
return count.getAndIncrement(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Benchmark |
||||||
|
public void put(final ComparatorBenchmarks.Counter counter) throws RocksDBException { |
||||||
|
final int i = counter.next(); |
||||||
|
db.put(getColumnFamily(), ba("key" + i), ba("value" + i)); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
/** |
||||||
|
* Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
||||||
|
* This source code is licensed under both the GPLv2 (found in the |
||||||
|
* COPYING file in the root directory) and Apache 2.0 License |
||||||
|
* (found in the LICENSE.Apache file in the root directory). |
||||||
|
*/ |
||||||
|
package org.rocksdb.util; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.file.FileVisitResult; |
||||||
|
import java.nio.file.Files; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.nio.file.SimpleFileVisitor; |
||||||
|
import java.nio.file.attribute.BasicFileAttributes; |
||||||
|
|
||||||
|
public final class FileUtils { |
||||||
|
private static final SimpleFileVisitor<Path> DELETE_DIR_VISITOR = new DeleteDirVisitor(); |
||||||
|
|
||||||
|
/** |
||||||
|
* Deletes a path from the filesystem |
||||||
|
* |
||||||
|
* If the path is a directory its contents |
||||||
|
* will be recursively deleted before it itself |
||||||
|
* is deleted. |
||||||
|
* |
||||||
|
* Note that removal of a directory is not an atomic-operation |
||||||
|
* and so if an error occurs during removal, some of the directories |
||||||
|
* descendants may have already been removed |
||||||
|
* |
||||||
|
* @param path the path to delete. |
||||||
|
* |
||||||
|
* @throws IOException if an error occurs whilst removing a file or directory |
||||||
|
*/ |
||||||
|
public static void delete(final Path path) throws IOException { |
||||||
|
if (!Files.isDirectory(path)) { |
||||||
|
Files.deleteIfExists(path); |
||||||
|
} else { |
||||||
|
Files.walkFileTree(path, DELETE_DIR_VISITOR); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static class DeleteDirVisitor extends SimpleFileVisitor<Path> { |
||||||
|
@Override |
||||||
|
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { |
||||||
|
Files.deleteIfExists(file); |
||||||
|
return FileVisitResult.CONTINUE; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException { |
||||||
|
if (exc != null) { |
||||||
|
throw exc; |
||||||
|
} |
||||||
|
|
||||||
|
Files.deleteIfExists(dir); |
||||||
|
return FileVisitResult.CONTINUE; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,58 @@ |
|||||||
|
/** |
||||||
|
* Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
||||||
|
* This source code is licensed under both the GPLv2 (found in the |
||||||
|
* COPYING file in the root directory) and Apache 2.0 License |
||||||
|
* (found in the LICENSE.Apache file in the root directory). |
||||||
|
*/ |
||||||
|
package org.rocksdb.util; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import static java.nio.charset.StandardCharsets.UTF_8; |
||||||
|
|
||||||
|
public final class KVUtils { |
||||||
|
|
||||||
|
/** |
||||||
|
* Get a byte array from a string. |
||||||
|
* |
||||||
|
* Assumes UTF-8 encoding |
||||||
|
* |
||||||
|
* @param string the string |
||||||
|
* |
||||||
|
* @return the bytes. |
||||||
|
*/ |
||||||
|
public static byte[] ba(final String string) { |
||||||
|
return string.getBytes(UTF_8); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get a string from a byte array. |
||||||
|
* |
||||||
|
* Assumes UTF-8 encoding |
||||||
|
* |
||||||
|
* @param bytes the bytes |
||||||
|
* |
||||||
|
* @return the string. |
||||||
|
*/ |
||||||
|
public static String str(final byte[] bytes) { |
||||||
|
return new String(bytes, UTF_8); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Get a list of keys where the keys are named key1..key1+N |
||||||
|
* in the range of {@code from} to {@code to} i.e. keyFrom..keyTo. |
||||||
|
* |
||||||
|
* @param from the first key |
||||||
|
* @param to the last key |
||||||
|
* |
||||||
|
* @return the array of keys |
||||||
|
*/ |
||||||
|
public static List<byte[]> keys(final int from, final int to) { |
||||||
|
final List<byte[]> keys = new ArrayList<>(to - from); |
||||||
|
for (int i = from; i < to; i++) { |
||||||
|
keys.add(ba("key" + i)); |
||||||
|
} |
||||||
|
return keys; |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue