"Trust no one, bench everything." - sbt plugin for JMH (Java Microbenchmark Harness)

Overview

sbt-jmh

Join the chat at https://gitter.im/ktoso/sbt-jmh

SBT plugin for running OpenJDK JMH benchmarks.

JMH about itself:

JMH is a Java harness for building, running, and analysing nano/micro/milli/macro benchmarks written in Java and other languages targeting the JVM.

Please read nanotrusting nanotime and other blog posts on micro-benchmarking (or why most benchmarks are wrong) and make sure your benchmark is valid, before you set out to implement your benchmarks.

Versions

Plugin version Default JMH version Notes
0.4.3 (sbt 1.3.0+) 1.32
0.4.2 (sbt 1.3.0+) 1.31 JMH -prof async supports 2.x
0.4.1 (sbt 1.3.0+) 1.30
0.4.0 (sbt 1.3.0+) 1.25 profilers now in JMH core
0.3.7 (sbt 0.13.17 / sbt 1.1.4) 1.21 support JDK 11
0.3.6 (sbt 0.13.17 / sbt 1.1.4) 1.21 support JDK 11
0.3.4 (sbt 0.13.17 / sbt 1.1.4) 1.21 support of GraalVM
0.3.3 (sbt 0.13.17 / sbt 1.1.1) 1.20 JMH bugfix release
0.3.2 (sbt 0.13.16 / sbt 1.0) 1.19 minor bugfix release
0.3.1 (sbt 0.13.16 / sbt 1.0) 1.19 minor bugfix release
0.3.0 (sbt 0.13.16 / sbt 1.0) 1.19 async profiler, flame-graphs
... ...

Not interesting versions are skipped in the above listing. Always use the newest which has the JMH version you need. You should stick to the latest version at all times anyway of course.

Adding to your project

Since sbt-jmh is an AutoPlugin all you need to do in order to activate it in your project is to add the below line to your project/plugins.sbt file:

// project/plugins.sbt
addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.3")

and enable it in the projects where you want to (useful in multi-project builds, as you can enable it only where you need it):

// build.sbt
enablePlugins(JmhPlugin)

If you define your project in a Build.scala, you also need the following import:

import pl.project13.scala.sbt.JmhPlugin

You can read more about auto plugins in sbt on it's documentation page.

Write your benchmarks in src/main/scala. They will be picked up and instrumented by the plugin.

JMH has a very specific way of working (it generates loads of code), so you should prepare a separate project for your benchmarks. In it, just type run in order to run your benchmarks. All JMH options work as expected. For help type run -h. Another example of running it is:

jmh:run -i 3 -wi 3 -f1 -t1 .*FalseSharing.*

Which means "3 iterations" "3 warmup iterations" "1 fork" "1 thread". Please note that benchmarks should be usually executed at least in 10 iterations (as a rule of thumb), but more is better.

For "real" results we recommend to at least warm up 10 to 20 iterations, and then measure 10 to 20 iterations again. Forking the JVM is required to avoid falling into specific optimisations (no JVM optimisation is really "completely" predictable)

If your benchmark should be a module in a multimodule project and needs access to another modules test classes then you might want to define your benchmarks in src/test as well (because Intellij does not support "compile->test" dependencies). While this is not directly supported it can be achieved with some tweaks. Assuming the benchmarks live in a module bench and need access to test classes from anotherModule, you have to define this dependency in your main build.sbt:

lazy val bench = project.dependsOn(anotherModule % "test->test").enablePlugins(JmhPlugin)

In bench/build.sbt you need to tweak some settings:

sourceDirectory in Jmh := (sourceDirectory in Test).value
classDirectory in Jmh := (classDirectory in Test).value
dependencyClasspath in Jmh := (dependencyClasspath in Test).value
// rewire tasks, so that 'jmh:run' automatically invokes 'jmh:compile' (otherwise a clean 'jmh:run' would fail)
compile in Jmh := (compile in Jmh).dependsOn(compile in Test).value
run in Jmh := (run in Jmh).dependsOn(Keys.compile in Jmh).evaluated

Options

Please invoke run -h to get a full list of run as well as output format options.

Useful hint: If you plan to aggregate the collected data you should have a look at the available output formats (-lrf). For example it's possible to keep the benchmark's results as csv or json files for later regression analysis.

Using Java Flight Recorder / async-profiler.

NOTE: sbt-jmh-s integration with async-profiler and Java Flight Recorder has been contributed to the JMH project as of JMH 1.25 and removed from this project. Please migrate to using -prof jfr / -prof async. Use -prof jfr:help / -prof async:help to list available options.

Examples

The examples are scala-fied examples from the original JMH repo, check them out, and run them!

The results will look somewhat like this:

...

[info] # Run progress: 92.86% complete, ETA 00:00:15
[info] # VM invoker: /Library/Java/JavaVirtualMachines/jdk1.7.0_60.jdk/Contents/Home/jre/bin/java
[info] # VM options: <none>
[info] # Fork: 1 of 1
[info] # Warmup: 2 iterations, single-shot each
[info] # Measurement: 3 iterations, single-shot each
[info] # Threads: 1 thread, will synchronize iterations
[info] # Benchmark mode: Single shot invocation time
[info] # Benchmark: org.openjdk.jmh.samples.JMHSample_02_BenchmarkModes.measureSingleShot
[info] # Warmup Iteration   1: 100322.000 us
[info] # Warmup Iteration   2: 100556.000 us
[info] Iteration   1: 100162.000 us
[info] Iteration   2: 100468.000 us
[info] Iteration   3: 100706.000 us
[info]
[info] Result : 100445.333 ±(99.9%) 4975.198 us
[info]   Statistics: (min, avg, max) = (100162.000, 100445.333, 100706.000), stdev = 272.707
[info]   Confidence interval (99.9%): [95470.135, 105420.532]
[info]
[info]
[info] # Run progress: 96.43% complete, ETA 00:00:07
[info] # VM invoker: /Library/Java/JavaVirtualMachines/jdk1.7.0_60.jdk/Contents/Home/jre/bin/java
[info] # VM options: <none>
[info] # Fork: 1 of 1
[info] # Warmup: 2 iterations, single-shot each, 5000 calls per batch
[info] # Measurement: 3 iterations, single-shot each, 5000 calls per batch
[info] # Threads: 1 thread, will synchronize iterations
[info] # Benchmark mode: Single shot invocation time
[info] # Benchmark: org.openjdk.jmh.samples.JMHSample_26_BatchSize.measureRight
[info] # Warmup Iteration   1: 15.344 ms
[info] # Warmup Iteration   2: 13.499 ms
[info] Iteration   1: 2.305 ms
[info] Iteration   2: 0.716 ms
[info] Iteration   3: 0.473 ms
[info]
[info] Result : 1.165 ±(99.9%) 18.153 ms
[info]   Statistics: (min, avg, max) = (0.473, 1.165, 2.305), stdev = 0.995
[info]   Confidence interval (99.9%): [-16.988, 19.317]
[info]
[info]
[info] Benchmark                                                 Mode   Samples         Mean   Mean error    Units
[info] o.o.j.s.JMHSample_22_FalseSharing.baseline               thrpt         3      692.034      179.561   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.baseline:reader        thrpt         3      199.185      185.188   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.baseline:writer        thrpt         3      492.850        7.307   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.contended              thrpt         3      706.532      293.880   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.contended:reader       thrpt         3      210.202      277.801   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.contended:writer       thrpt         3      496.330       78.508   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.hierarchy              thrpt         3     1751.941      222.535   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.hierarchy:reader       thrpt         3     1289.003      277.126   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.hierarchy:writer       thrpt         3      462.938       55.329   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.padded                 thrpt         3     1745.650       83.783   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.padded:reader          thrpt         3     1281.877       47.922   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.padded:writer          thrpt         3      463.773      104.223   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.sparse                 thrpt         3     1362.515      461.782   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.sparse:reader          thrpt         3      898.282      415.388   ops/us
[info] o.o.j.s.JMHSample_22_FalseSharing.sparse:writer          thrpt         3      464.233       49.958   ops/us

Advanced: Using custom Runners

It is possible to hand over the running of JMH to an App implemented by you, which allows you to programmatically access all test results and modify JMH arguments before you actually invoke it.

To use a custom runner class with runMain, simply use it: jmh:runMain com.example.MyRunner -i 10 .* – an example for this is available in plugin/src/sbt-test/sbt-jmh/runMain (open the test file).

To replace the runner class which is used when you type jmh:run, you can set the class in your build file – an example for this is available in plugin/src/sbt-test/sbt-jmh/custom-runner (open the build.sbt file).

Contributing

Yes, pull requests and opening issues is very welcome!

The plugin is maintained at an best-effort basis -- submitting a PR is the best way of getting something done :-)

You can locally publish the plugin with:

sbt '; project plugin; ^publishLocal'

Please test your changes by adding to the [scripted test suite][sbt-jmh/plugin/src/sbt-test/sbt-jmh/] which can be run with:

 sbt '; project plugin; ^scripted'

Special thanks

Special thanks for contributing async-profiler and flame-graphs support and other improvements go to @retronym of Lightbend's Scala team.

Comments
  • Cross-compile the plugin against 0.13.15 and 1.0.0-M6

    Cross-compile the plugin against 0.13.15 and 1.0.0-M6

    This PR fixes #115.

    I had to update the two CustomRunnerApp in the scripted tests two use the main() method. I ran into the IncompatibleClassChangeError issue, changes fix #66.

    Now you have to use ^ scripted and ^ publishSigned to cross-compile with the correct sbt version (see the 0.13.16-M1 release notes).

    opened by lomigmegard 15
  • java.lang.InternalError: Malformed class name on jmh:run

    java.lang.InternalError: Malformed class name on jmh:run

    Currently trying to run previously built/running benchmarks with sbt-jmh and appears to fail with a runtime error. I have tried turning the example into something simple, which may help in reproducing.

    scala version: 2.11.4 sbt version: 0.13.7 sbt-jmh version: 0.2.6 (also tried with 0.2.4, and it fails there now also)

    The project is a multi-module project, where there is a benchmarks module containing the jmh benchmarks and has the JmhPlugin enabled.

    Below, is a simple benchmark that can be run, in isolation:

    package foo.bar
    
    import org.openjdk.jmh.annotations.Benchmark
    
    class Benchmarks {
      @Benchmark
      def helloWorldBench(): Unit = {
      }
    }
    

    If we put this file in the same directory, then we get the error seen below:

    package foo.bar
    object Bar {
      object Hello {
        //Note: if we move the trait below out of Hello, but within Bar it "fixes"
        trait Something
      }
    }
    

    Here is the general error observed:

    [info] Compiling 1 Scala source to ...
    Processing 140 classes from /.../target/scala-2.11/classes with "reflection" generator
    Writing out Java source to /.../target/scala-2.11/src_managed/jmh and resources to /.../target/scala-2.11/resource_managed/jmh
    Annotation generator had thrown the exception.
    java.lang.InternalError: Malformed class name
        at java.lang.Class.getSimpleName(Class.java:1330)
        at java.lang.Class.getCanonicalName(Class.java:1399)
        at org.openjdk.jmh.generators.reflection.RFClassInfo.getQualifiedName(RFClassInfo.java:67)
        at org.openjdk.jmh.generators.core.BenchmarkGenerator.buildAnnotatedSet(BenchmarkGenerator.java:226)
        at org.openjdk.jmh.generators.core.BenchmarkGenerator.generate(BenchmarkGenerator.java:85)
        at org.openjdk.jmh.generators.bytecode.JmhBytecodeGenerator.main(JmhBytecodeGenerator.java:100)
        at pl.project13.scala.sbt.JmhPlugin$.pl$project13$scala$sbt$JmhPlugin$$internalGenerateBenchmarkSourcesAndResources(JmhPlugin.scala:91)
        at pl.project13.scala.sbt.JmhPlugin$$anonfun$2.apply(JmhPlugin.scala:74)
        at pl.project13.scala.sbt.JmhPlugin$$anonfun$2.apply(JmhPlugin.scala:72)
        at sbt.FileFunction$$anonfun$cached$1.apply(Tracked.scala:186)
        at sbt.FileFunction$$anonfun$cached$1.apply(Tracked.scala:186)
        at sbt.FileFunction$$anonfun$cached$2$$anonfun$apply$3$$anonfun$apply$4.apply(Tracked.scala:200)
        at sbt.FileFunction$$anonfun$cached$2$$anonfun$apply$3$$anonfun$apply$4.apply(Tracked.scala:196)
        at sbt.Difference.apply(Tracked.scala:175)
        at sbt.Difference.apply(Tracked.scala:157)
        at sbt.FileFunction$$anonfun$cached$2$$anonfun$apply$3.apply(Tracked.scala:196)
        at sbt.FileFunction$$anonfun$cached$2$$anonfun$apply$3.apply(Tracked.scala:195)
        at sbt.Difference.apply(Tracked.scala:175)
        at sbt.Difference.apply(Tracked.scala:151)
        at sbt.FileFunction$$anonfun$cached$2.apply(Tracked.scala:195)
        at sbt.FileFunction$$anonfun$cached$2.apply(Tracked.scala:193)
        at pl.project13.scala.sbt.JmhPlugin$.pl$project13$scala$sbt$JmhPlugin$$generateBenchmarkSourcesAndResources(JmhPlugin.scala:76)
        at pl.project13.scala.sbt.JmhPlugin$$anonfun$projectSettings$9.apply(JmhPlugin.scala:42)
        at pl.project13.scala.sbt.JmhPlugin$$anonfun$projectSettings$9.apply(JmhPlugin.scala:42)
        at scala.Function1$$anonfun$compose$1.apply(Function1.scala:47)
        at sbt.$tilde$greater$$anonfun$$u2219$1.apply(TypeFunctions.scala:40)
        at sbt.std.Transform$$anon$4.work(System.scala:63)
        at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226)
        at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:226)
        at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:17)
        at sbt.Execute.work(Execute.scala:235)
        at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226)
        at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:226)
        at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:159)
        at sbt.CompletionService$$anon$2.call(CompletionService.scala:28)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
    
    opened by jrudnick 10
  • jmh:compile doesnt work with 0.2.3 ?

    jmh:compile doesnt work with 0.2.3 ?

    We didn't change any code just upgraded to 0.2.3. Did we miss any step ?

    java:9: package org.openjdk.jmh.annotations does not exist
    [error] org.openjdk.jmh.annotations.CompilerControl
    
    java:10: package org.openjdk.jmh.runner does not exist
    [error] org.openjdk.jmh.runner.InfraControl
    
    java:11: cannot find symbol
    [error]   symbol:   class ThreadParams
    [error]   location: package org.openjdk.jmh.infra
    [error] org.openjdk.jmh.infra.ThreadParams
    
    java:12: package org.openjdk.jmh.results does not exist
    [error] org.openjdk.jmh.results.BenchmarkTaskResult
    
    java:13: package org.openjdk.jmh.results does not exist
    [error] org.openjdk.jmh.results.Result
    
    java:15: package org.openjdk.jmh.results does not exist
    [error] org.openjdk.jmh.results.AverageTimeResult
    
    bug question 
    opened by ghost 8
  • Enable programmatic running of benchmarks

    Enable programmatic running of benchmarks

    As discussed in https://github.com/ktoso/sbt-jmh/issues/27#issuecomment-71740709 we sometimes may want to runMain com.example.MyRunner in order to get programatic access to the test results (send them over somewhere etc).

    This does not currently work, and we'll get

    [info] # JMH 1.5 (released 5 days ago)
    [info] # VM invoker: /Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home/jre/bin/java
    [info] # VM options: <none>
    [info] # Warmup: 1 iterations, single-shot each
    [info] # Measurement: 1 iterations, single-shot each
    [info] # Timeout: 10 min per iteration
    [info] # Threads: 1 thread
    [info] # Benchmark mode: Single shot invocation time
    [info] # Benchmark: org.openjdk.jmh.samples.JMHSample_02_BenchmarkModes.measureSingleShot
    [info]
    [info] # Run progress: 100.00% complete, ETA 00:00:00
    [info] # Fork: 1 of 1
    [info] <failure>
    [info]
    [info] java.lang.IllegalArgumentException: Benchmark does not match a class
    [info]  at org.openjdk.jmh.util.ClassUtils.loadClass(ClassUtils.java:90)
    [info]  at org.openjdk.jmh.runner.BaseRunner.runBenchmark(BaseRunner.java:214)
    [info]  at org.openjdk.jmh.runner.BaseRunner.runBenchmarks(BaseRunner.java:111)
    [info]  at org.openjdk.jmh.runner.ForkedRunner.run(ForkedRunner.java:51)
    [info]  at org.openjdk.jmh.runner.ForkedMain.main(ForkedMain.java:80)
    [info] Caused by: java.lang.ClassNotFoundException: org.openjdk.jmh.samples.generated.JMHSample_02_BenchmarkModes_measureSingleShot
    [info]  at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    [info]  at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    [info]  at java.security.AccessController.doPrivileged(Native Method)
    [info]  at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    [info]  at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    [info]  at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    [info]  at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    [info]  at java.lang.Class.forName0(Native Method)
    [info]  at java.lang.Class.forName(Class.java:191)
    [info]  at org.openjdk.jmh.util.ClassUtils.loadClass(ClassUtils.java:72)
    [info]  ... 4 more
    [info]
    [info]
    

    Adding these:

    ./target/scala-2.10/classes/...
    

    to the classpath should solve things.

    enhancement 
    opened by ktoso 8
  • Cannot load resource

    Cannot load resource

    First of all, thanks for this sbt plugin!

    I have a little problem: If I try to load a resource like:

    getClass.getClassLoader.getResourceAsStream("my-file.json")
    

    with my-file.json inside resources,

    -> this works with a Main extends App -> but does not work for a JMH benchmark.

    I guess that the resources are not available for the jmh:run sbt scope.

    opened by yanns 7
  • JMH version bump to 1.3.1

    JMH version bump to 1.3.1

    I am profiling a library written in scala and many of the hottest regions aren't reported (pretty printed) because they are unparseable.

    I would like to see how 1.3.1 behaves because perfasm has been improved since 1.1.

    Thx!

    enhancement 
    opened by biboudis 7
  • Improvements to profilers

    Improvements to profilers

    • Support Flight Recorder on OpenJDK 11
      • Enable -XX:+DebugNonSafePoints for async-profiler, by default.
      • Add an option to async-profiler to also dump its data in JFR format.

    Fixes #156

    opened by retronym 6
  • Make classDirectory+dependencyClasspath configurable

    Make classDirectory+dependencyClasspath configurable

    As mentioned in https://github.com/ktoso/sbt-jmh/issues/63#issuecomment-230509786 one can hack around the hardcoded config that is used for classDirectory and dependencyClasspath (both are taken from the Compile config).

    This change allows the user to set

    classDirectory in Jmh := (classDirectory in Test).value
    dependencyClasspath in Jmh := (dependencyClasspath in Test).value
    

    so that jmh will use the appropriate values.

    Not sure if this change provides enough value to be merged, at least it seems to be cleaner when having benchmark code in the Test config.

    opened by magro 6
  • Error when trying to use Flight Recorder

    Error when trying to use Flight Recorder

    When I try to use Flight Recorder with OpenJDK11 and newest sbt-jmh, I receive this error:

    sbt:root> bench/jmh:run -bm avgt -prof jmh.extras.JFR
    [info] Packaging /mnt/Workspaces/MateuszKubuszok/test-bench/bench/target/scala-2.12/bench_2.12-0.1.0-SNAPSHOT.jar ...
    [info] Done packaging.
    [info] Running (fork) org.openjdk.jmh.Main -bm avgt -prof jmh.extras.JFR
    [error] WARNING: An illegal reflective access operation has occurred
    [error] WARNING: Illegal reflective access by org.openjdk.jmh.util.Utils (file:/tmp/sbt_266d0556/target/44244710/jmh-core-1.21.jar) to field java.io.PrintStream.charOut
    [error] WARNING: Please consider reporting this to the maintainers of org.openjdk.jmh.util.Utils
    [error] WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
    [error] WARNING: All illegal access operations will be denied in a future release
    [info] # JMH version: 1.21
    [info] # VM version: JDK 11.0.7, OpenJDK 64-Bit Server VM, 11.0.7+10
    [info] # VM invoker: /usr/lib/jvm/java-11-openjdk/bin/java
    [info] # VM options: <none>
    [info] # Warmup: 5 iterations, 10 s each
    [info] # Measurement: 5 iterations, 10 s each
    [info] # Timeout: 10 min per iteration
    [info] # Threads: 1 thread, will synchronize iterations
    [info] # Benchmark mode: Average time, time/op
    [info] # Benchmark: example.FizzBuzzBenchmark.run
    [info] # Run progress: 0.00% complete, ETA 00:08:20
    [info] # Fork: 1 of 5
    [info] # Preparing profilers: JFR
    [info] # Warmup Iteration   1: Error: Could not find or load main class 464690
    [info] Caused by: java.lang.ClassNotFoundException: 464690
    [info] Error: Could not find or load main class 464690
    [info] Caused by: java.lang.ClassNotFoundException: 464690
    [info] <failure>
    [info] java.lang.RuntimeException: Non zero exit code from: /usr/lib/jvm/java-11-openjdk/bin/java 464690 JFR.start name=JMH-profile-example.FizzBuzzBenchmark.run-warmup settings=profile
    [info]  at pl.project13.scala.jmh.extras.profiler.ProfilerUtils.startAndWait(ProfilerUtils.java:40)
    [info]  at pl.project13.scala.jmh.extras.profiler.FlightRecordingProfiler.jcmd(FlightRecordingProfiler.java:305)
    [info]  at pl.project13.scala.jmh.extras.profiler.FlightRecordingProfiler.startJfr(FlightRecordingProfiler.java:211)
    [info]  at pl.project13.scala.jmh.extras.profiler.FlightRecordingProfiler.beforeIteration(FlightRecordingProfiler.java:154)
    [info]  at org.openjdk.jmh.runner.BenchmarkHandler.startProfilers(BenchmarkHandler.java:261)
    [info]  at org.openjdk.jmh.runner.BenchmarkHandler.runIteration(BenchmarkHandler.java:334)
    [info]  at org.openjdk.jmh.runner.BaseRunner.runBenchmark(BaseRunner.java:262)
    [info]  at org.openjdk.jmh.runner.BaseRunner.runBenchmark(BaseRunner.java:234)
    [info]  at org.openjdk.jmh.runner.BaseRunner.doSingle(BaseRunner.java:139)
    [info]  at org.openjdk.jmh.runner.BaseRunner.runBenchmarksForked(BaseRunner.java:76)
    [info]  at org.openjdk.jmh.runner.ForkedRunner.run(ForkedRunner.java:72)
    [info]  at org.openjdk.jmh.runner.ForkedMain.main(ForkedMain.java:84)
    [info] # Run progress: 20.00% complete, ETA 00:00:01
    [info] # Fork: 2 of 5
    [info] # Preparing profilers: JFR
    [info] # Warmup Iteration   1: Error: Could not find or load main class 464748
    [info] Caused by: java.lang.ClassNotFoundException: 464748
    [info] Error: Could not find or load main class 464748
    [info] Caused by: java.lang.ClassNotFoundException: 464748
    [info] <failure>
    [info] java.lang.RuntimeException: Non zero exit code from: /usr/lib/jvm/java-11-openjdk/bin/java 464748 JFR.start name=JMH-profile-example.FizzBuzzBenchmark.run-warmup settings=profile
    [info]  at pl.project13.scala.jmh.extras.profiler.ProfilerUtils.startAndWait(ProfilerUtils.java:40)
    [info]  at pl.project13.scala.jmh.extras.profiler.FlightRecordingProfiler.jcmd(FlightRecordingProfiler.java:305)
    [info]  at pl.project13.scala.jmh.extras.profiler.FlightRecordingProfiler.startJfr(FlightRecordingProfiler.java:211)
    [info]  at pl.project13.scala.jmh.extras.profiler.FlightRecordingProfiler.beforeIteration(FlightRecordingProfiler.java:154)
    [info]  at org.openjdk.jmh.runner.BenchmarkHandler.startProfilers(BenchmarkHandler.java:261)
    [info]  at org.openjdk.jmh.runner.BenchmarkHandler.runIteration(BenchmarkHandler.java:334)
    [info]  at org.openjdk.jmh.runner.BaseRunner.runBenchmark(BaseRunner.java:262)
    [info]  at org.openjdk.jmh.runner.BaseRunner.runBenchmark(BaseRunner.java:234)
    [info]  at org.openjdk.jmh.runner.BaseRunner.doSingle(BaseRunner.java:139)
    [info]  at org.openjdk.jmh.runner.BaseRunner.runBenchmarksForked(BaseRunner.java:76)
    [info]  at org.openjdk.jmh.runner.ForkedRunner.run(ForkedRunner.java:72)
    [info]  at org.openjdk.jmh.runner.ForkedMain.main(ForkedMain.java:84)
    [info] # Run progress: 40.00% complete, ETA 00:00:01
    ...
    [info] # Run progress: 80.00% complete, ETA 00:00:00
    [info] # Fork: 5 of 5
    [info] # Preparing profilers: JFR
    [info] # Warmup Iteration   1: Error: Could not find or load main class 464957
    [info] Caused by: java.lang.ClassNotFoundException: 464957
    [info] Error: Could not find or load main class 464957
    [info] Caused by: java.lang.ClassNotFoundException: 464957
    [info] <failure>
    [info] java.lang.RuntimeException: Non zero exit code from: /usr/lib/jvm/java-11-openjdk/bin/java 464957 JFR.start name=JMH-profile-example.FizzBuzzBenchmark.run-warmup settings=profile
    [info]  at pl.project13.scala.jmh.extras.profiler.ProfilerUtils.startAndWait(ProfilerUtils.java:40)
    [info]  at pl.project13.scala.jmh.extras.profiler.FlightRecordingProfiler.jcmd(FlightRecordingProfiler.java:305)
    [info]  at pl.project13.scala.jmh.extras.profiler.FlightRecordingProfiler.startJfr(FlightRecordingProfiler.java:211)
    [info]  at pl.project13.scala.jmh.extras.profiler.FlightRecordingProfiler.beforeIteration(FlightRecordingProfiler.java:154)
    [info]  at org.openjdk.jmh.runner.BenchmarkHandler.startProfilers(BenchmarkHandler.java:261)
    [info]  at org.openjdk.jmh.runner.BenchmarkHandler.runIteration(BenchmarkHandler.java:334)
    [info]  at org.openjdk.jmh.runner.BaseRunner.runBenchmark(BaseRunner.java:262)
    [info]  at org.openjdk.jmh.runner.BaseRunner.runBenchmark(BaseRunner.java:234)
    [info]  at org.openjdk.jmh.runner.BaseRunner.doSingle(BaseRunner.java:139)
    [info]  at org.openjdk.jmh.runner.BaseRunner.runBenchmarksForked(BaseRunner.java:76)
    [info]  at org.openjdk.jmh.runner.ForkedRunner.run(ForkedRunner.java:72)
    [info]  at org.openjdk.jmh.runner.ForkedMain.main(ForkedMain.java:84)
    [info] # Run complete. Total time: 00:00:01
    [info] REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
    [info] why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
    [info] experiments, perform baseline and negative tests that provide experimental control, make sure
    [info] the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
    [info] Do not assume the numbers tell you what you want them to tell.
    [info] Benchmark  Mode  Cnt  Score   Error  Units
    

    This is a very simple example test with one @Benchmark that I used to try out Flight Recorder from OpenJDK with OpenJDK11. I get this error when I run:

    sbt:root> bench/jmh:run -bm avgt -prof jmh.extras.JFR
    

    If I run benchmarks without Flight Recorder profile

    sbt:root> bench/jmh:run -bm avgt
    

    everything works correctly.

    It's quite clear from the error message that the arguments passed to java are wrong because main class is missing. Looking at jps during both runs, it seems that org.openjdk.jmh.runner.ForkedMain 127.0.0.1 arguments are dropped for some reason.

    Is there some issue with my setup, is is this some bug that I can help fixing?

    opened by MateuszKubuszok 5
  • Support async-profiler, improve JFR profiler

    Support async-profiler, improve JFR profiler

    Existing Flight Recorder profiler modified to:

    • Discard samples collected during the warmup period
    • By default, use AsyncGetCallTrace based sampling
    • Make stackdepth configurable and increase the default
    • Optionally, generate flamegraphs for (requires local clone of brendangregg/FlameGraph and a local clone and build of chrishantha/jfr-flame-graph.)

    Support async-profiler:

    • Requires local clone and build of jvm-profiling-tools/async-profiler
    • Samples only collected after warmup has completed
    • CPU and HEAP events supported.
    • Optionally, generate flame graphs

    async-profiler is primarily Linux based, but it also has preliminary support for MacOS.

    The location of the external tools can be provided with the followng environment variables: FLAME_GRAPH_DIR, JFR_FLAME_GRAPH_DIR, ASYNC_PROFILER_DIR. Alternatively, these can be provided as options to the profiler.

    Help:

    sbt> jmh:run Bench -prof jmh.extras.JFR:help
    Option                              Description
    ------                              -----------
    --debugNonSafepoints <Boolean>      (default: [true, false])
    --dir <Output directory>
    --events <JfrEventType>             (default: [CPU, ALLOCATION_TLAB,
                                          ALLOCATION_OUTSIDE_TLAB, EXCEPTIONS,
                                          LOCKS])
    --flameGraphDir <directory>       Location of clone of https://github.
                                          com/brendangregg/FlameGraph. Also
                                          can be provided as $FLAME_GRAPH_DIR
    --flameGraphDirection <Directions>  Directions to generate flamegraphs
    --flameGraphOpts                    Options passed to FlameGraph.pl
    --flightRecorderOpts
    --help                              Display help.
    --jfrFlameGraphDir <directory>    Location of clone of https://github.
                                          com/chrishantha/jfr-flame-graph.
                                          Also can be provided as
                                          $JFR_FLAME_GRAPH_DIR
    --jfrFlameGraphOpts                 Options passed to flamegraph-output.sh
    --stackDepth <Integer>              (default: 1024)
    --verbose <Boolean>                 Output the sequence of commands
                                          (default: false)
    
    
    sbt> jmh:run Bench -prof jmh.extras.Async:help
    
    Option                              Description
    ------                              -----------
    --asyncProfilerDir <directory>    Location of clone of https://github.
                                          com/jvm-profiling-tools/async-
                                          profiler. Also can be provided as
                                          $ASYNC_PROFILER_DIR
    --dir <<directory>>                 Output directory
    --event <AsyncProfilerEventType>    Event to sample (default: [CPU, HEAP])
    --flameGraphDir <directory>       Location of clone of https://github.
                                          com/brendangregg/FlameGraph. Also
                                          can be provided as $FLAME_GRAPH_DIR
    --flameGraphDirection <Directions>  Directions to generate flamegraphs
                                          (default: [BOTH, NONE, FORWARD,
                                          REVERSE])
    --flameGraphOpts                    Options passed to FlameGraph.pl
    --framebuf <Long>                   Size of profiler framebuffer (default:
                                          8388608)
    --help                              Display help.
    --threads <Boolean>                 profile threads separately (default:
                                          [false, true])
    --verbose <Boolean>                 Output the sequence of commands
                                          (default: false)
    
    

    Examples:

    sbt> jmh:run Bench -f1 -wi 5 -i5 -prof jmh.extras.Async:event=HEAP;dir=/tmp/profile-async;flameGraphOpts=--minwidth,2;asyncProfilerDir=/code/async-profiler;flameGraphDir=/code/FlameGraph
    
    sbt> jmh:run Bench -f1 -wi 5 -i5 -prof jmh.extras.JFR:dir=/tmp/profile-jfr;flameGraphDir=/code/FlameGraph;jfrFlameGraphDir=/code/jfr-flame-graph;flameGraphOpts=--minwidth,2;verbose=true
    
    opened by retronym 5
  • Add license header.

    Add license header.

    I only added license header in plugin project without extras project because extras didn't specific license in build.sbt. I think we could move sbt-key - licenses to commonSettings to let all projects with Apache-2.0. Since JMH is used GPLv2 with Classpath exception as OpenJDK, I think it should be fine.

    opened by jiminhsieh 5
  • `Test / skip := true` leads to `java.lang.IllegalArgumentException: Benchmark does not match a class`

    `Test / skip := true` leads to `java.lang.IllegalArgumentException: Benchmark does not match a class`

    When a project with is build or run by sbt-jmh contains Test / skip := true, well, it produces a jar which can't be used at all.

    Any benchmark fails as:

    [info] # JMH version: 1.32
    [info] # VM version: JDK 1.8.0_322, OpenJDK 64-Bit Server VM, 25.322-b06
    [info] # VM invoker: /Library/Java/JavaVirtualMachines/openjdk8-temurin/Contents/Home/jre/bin/java
    [info] # VM options: <none>
    [info] # Blackhole mode: full + dont-inline hint
    [info] # Warmup: 5 iterations, 10 s each
    [info] # Measurement: 5 iterations, 10 s each
    [info] # Timeout: 10 min per iteration
    [info] # Threads: 1 thread, will synchronize iterations
    [info] # Benchmark mode: Throughput, ops/time
    [info] # Benchmark: test.TestBenchmark.readFromFile
    [info] # Run progress: 66.67% complete, ETA 00:00:01
    [info] # Fork: 1 of 5
    [info] <failure>
    [info] java.lang.IllegalArgumentException: Benchmark does not match a class
    [info] 	at org.openjdk.jmh.util.ClassUtils.loadClass(ClassUtils.java:94)
    [info] 	at org.openjdk.jmh.runner.BenchmarkHandler.<init>(BenchmarkHandler.java:69)
    [info] 	at org.openjdk.jmh.runner.BaseRunner.runBenchmark(BaseRunner.java:232)
    [info] 	at org.openjdk.jmh.runner.BaseRunner.doSingle(BaseRunner.java:138)
    [info] 	at org.openjdk.jmh.runner.BaseRunner.runBenchmarksForked(BaseRunner.java:75)
    [info] 	at org.openjdk.jmh.runner.ForkedRunner.run(ForkedRunner.java:72)
    [info] 	at org.openjdk.jmh.runner.ForkedMain.main(ForkedMain.java:84)
    [info] Caused by: java.lang.ClassNotFoundException: test.jmh_generated.TestBenchmark_readFromFile_jmhTest
    [info] 	at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
    [info] 	at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
    [info] 	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
    [info] 	at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    [info] 	at java.lang.Class.forName0(Native Method)
    [info] 	at java.lang.Class.forName(Class.java:264)
    [info] 	at org.openjdk.jmh.util.ClassUtils.loadClass(ClassUtils.java:73)
    [info] 	... 6 more
    

    How to reproduce:

    1. Get the last version. I've used https://github.com/sbt/sbt-jmh/pull/215 as local root.
    2. publish locally artifacts by: sbt ^^1.3.10 publishLocal
    3. add Test / skip := true to sbt-jmh-tester/build.sbt
    4. run sbt jmh:run inside sbt-jmh-tester folder

    Here it is.

    opened by catap 0
  • Fix `sbt-jmh-tester`

    Fix `sbt-jmh-tester`

    It fails on attempt to use like:

    /Users/catap/src/sbt-jmh/sbt-jmh-tester/project/plugins.sbt:2: error: object Source is not a member of package sbt.io
      val is = io.Source.fromFile(new File("../version.sbt")).mkString
                  ^
    
    
    opened by catap 0
  • Log4j Dependencies not being passed to the Benchmark

    Log4j Dependencies not being passed to the Benchmark

    I have a project that creates a new log4j2 appender and has log4j dependencies called out in the project

    "org.apache.logging.log4j" % "log4j-core" % "2.17.1"
    "org.apache.logging.log4j" % "log4j-api" % "2.17.1"
    "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.17.1"
    "org.apache.logging.log4j" % "log4j-1.2-api" % "2.17.1"
    

    When I run the benchmarks, I get the following failure

    AverageTime(RandomLogBench_perform_jmhTest.java:162)
    [info] 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [info] 	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    [info] 	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    [info] 	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    [info] 	at org.openjdk.jmh.runner.BenchmarkHandler$BenchmarkTask.call(BenchmarkHandler.java:470)
    [info] 	at org.openjdk.jmh.runner.BenchmarkHandler$BenchmarkTask.call(BenchmarkHandler.java:453)
    [info] 	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    [info] 	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    [info] 	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    [info] 	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128
    [info] 	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628
    [info] 	at java.base/java.lang.Thread.run(Thread.java:829)
    [info] Caused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.LogManager
    [info] 	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
    [info] 	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
    [info] 	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
    [info] 	... 15 more
    

    The module does compile and test successfully using the same classes, only the jmh:run sees this failure.

    I further explored the class path values

    show bench/Jmh/dependencyClasspath
    
    ...
    [info] * Attributed([snip]/.ivy2/cache/org.apache.logging.log4j/log4j-api/jars/log4j-api-2.17.1.jar)
    [info] * Attributed([snip]/.ivy2/cache/org.apache.logging.log4j/log4j-slf4j-impl/jars/log4j-slf4j-impl-2.17.1.jar)
    [info] * Attributed([snip]/.ivy2/cache/org.apache.logging.log4j/log4j-1.2-api/jars/log4j-1.2-api-2.17.1.jar)
    ...
    [info] * Attributed([snip]/.ivy2/cache/org.apache.logging.log4j/log4j-core/jars/log4j-core-2.17.1.jar)
    ...
    

    I tried outputting the classpath from an empty bench and there I don't see the log4j jars in the class path, but I see all other dependencies.

    Do I need to do anything special to propagate the log4j libraries to the benchmarks?

    opened by pavibhai 0
  • Specifying javaHome per configuration

    Specifying javaHome per configuration

    Is it possible to specify javaHome per configuration? Here is a fragment of my build.sbt:

    import pl.project13.scala.sbt.JmhPlugin.JmhKeys.Jmh
    
    lazy val Corretto11 = config("corretto11") extend Jmh
    lazy val Zulu11 = config("zulu11") extend Jmh
    
    lazy val myProject = project
      .enablePlugins(JmhPlugin)
      .configs(Corretto11, Zulu11)
      .settings(
        scalaVersion := "2.13.7",
        fork := true,
        Corretto11 / javaHome := Some(file("path/to/corretto-11.0.14")),
        Zulu11 / javaHome := Some(file("path/to/azul-11.0.14"))
      )
    

    Corretto11 / javaHome in sbt console returns the correct path but when I use Corretto11 / run it runs benchmarks with a fork of JVM used to run sbt shell instead of the one specified in javaHome.

    opened by AvaPL 0
  • jmh:run caused Unable to find the resource: /META-INF/BenchmarkList in multiple projects

    jmh:run caused Unable to find the resource: /META-INF/BenchmarkList in multiple projects

    I had an error when using this plugin with modules project.

    Project structure

    root/

    • build.sbt
    • project/
      • plugin.sbt
    • modules/
      • projectA/
        • build.sbt
          • src/
            • main.scala.example/
              • MyBenchmark.scala
            • test.scala.example/
              • MyBenchmarkTest.scala

    Code reproduce

    // root/project/plugin.sbt
    addSbtPlugin("pl.project13.scala" % "sbt-jmh"  % "0.4.0")
    
    // root/build.sbt
    enablePlugins(JmhPlugin)
    
    // MyBenchmarkTest.scala
    class MyBenchmark {
      @Benchmark
      def testMethod(blackHole: Blackhole): Double = {
        val list: List[Int] = List.range(1, Integer.MAX_VALUE/100)
        val sum: Double = list.sum
        blackHole.consume(sum)
        sum
      }
    }
    
    // MyBenchmark.scala
    class MyBenchmark {
      @Benchmark
      def testMethod(blackHole: Blackhole): Double = {
        val list: List[Int] = List.range(1, Integer.MAX_VALUE/100)
        val sum: Double = list.sum
        blackHole.consume(sum)
        sum
      }
    }
    

    I got this error when run the command: sbt jmh:complie -> sbt jmh:run

    Error

    [error] WARNING: An illegal reflective access operation has occurred [error] WARNING: Illegal reflective access by org.openjdk.jmh.util.Utils (file:/Users/linhtt20/Projects/data-access/target/bg-jobs/sbt_1393f137/target/bbd5338e/49ad8c14/jmh-core-1.25.jar) to field java.io.PrintStream.charOut [error] WARNING: Please consider reporting this to the maintainers of org.openjdk.jmh.util.Utils [error] WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations [error] WARNING: All illegal access operations will be denied in a future release [error] Exception in thread "main" java.lang.RuntimeException: ERROR: Unable to find the resource: /META-INF/BenchmarkList [error] at org.openjdk.jmh.runner.AbstractResourceReader.getReaders(AbstractResourceReader.java:98) [error] at org.openjdk.jmh.runner.BenchmarkList.find(BenchmarkList.java:122) [error] at org.openjdk.jmh.runner.Runner.internalRun(Runner.java:266) [error] at org.openjdk.jmh.runner.Runner.run(Runner.java:209) [error] at org.openjdk.jmh.Main.main(Main.java:71) [error] Nonzero exit code returned from runner: 1 [error] (Jmh / run) Nonzero exit code returned from runner: 1

    opened by linrium 0
Releases(v0.4.2)
Owner
sbt
Community organization for all sbt plugin authors, sbt modules and sbt itself.
sbt
Bloofi: A java implementation of multidimensional Bloom filters

Bloofi: A java implementation of multidimensional Bloom filters Bloom filters are probabilistic data structures commonly used for approximate membersh

Daniel Lemire 71 Nov 2, 2022
A high performance caching library for Java

Caffeine is a high performance, near optimal caching library. For more details, see our user's guide and browse the API docs for the latest release. C

Ben Manes 13k Jan 5, 2023
Chronicle Bytes has a similar purpose to Java NIO's ByteBuffer with many extensions

Chronicle-Bytes Chronicle-Bytes Chronicle Bytes contains all the low level memory access wrappers. It is built on Chronicle Core’s direct memory and O

Chronicle Software : Open Source 334 Jan 1, 2023
High performance Java implementation of a Cuckoo filter - Apache Licensed

Cuckoo Filter For Java This library offers a similar interface to Guava's Bloom filters. In most cases it can be used interchangeably and has addition

Mark Gunlogson 161 Dec 30, 2022
An advanced, but easy to use, platform for writing functional applications in Java 8.

Getting Cyclops X (10) The latest version is cyclops:10.4.0 Stackoverflow tag cyclops-react Documentation (work in progress for Cyclops X) Integration

AOL 1.3k Dec 29, 2022
Eclipse Collections is a collections framework for Java with optimized data structures and a rich, functional and fluent API.

English | 中文 | Deutsch | Español | Ελληνικά | Français | 日本語 | Norsk (bokmål) | Português-Brasil | Русский | हिंदी Eclipse Collections is a comprehens

Eclipse Foundation 2.1k Dec 29, 2022
External-Memory Sorting in Java

Externalsortinginjava External-Memory Sorting in Java: useful to sort very large files using multiple cores and an external-memory algorithm. The vers

Daniel Lemire 235 Dec 29, 2022
A Java library for quickly and efficiently parsing and writing UUIDs

fast-uuid fast-uuid is a Java library for quickly and efficiently parsing and writing UUIDs. It yields the most dramatic performance gains when compar

Jon Chambers 142 Jan 1, 2023
Geohash utitlies in java

geo Java utility methods for geohashing. Status: production, available on Maven Central Maven site reports are here including javadoc. Add this to you

Dave Moten 386 Jan 1, 2023
Hollow is a java library and toolset for disseminating in-memory datasets from a single producer to many consumers for high performance read-only access.

Hollow Hollow is a java library and toolset for disseminating in-memory datasets from a single producer to many consumers for high performance read-on

Netflix, Inc. 1.1k Dec 25, 2022
High Performance Primitive Collections for Java

HPPC: High Performance Primitive Collections Collections of primitive types (maps, sets, stacks, lists) with open internals and an API twist (no java.

Carrot Search 890 Dec 28, 2022
Java port of a concurrent trie hash map implementation from the Scala collections library

About This is a Java port of a concurrent trie hash map implementation from the Scala collections library. It is almost a line-by-line conversion from

null 147 Oct 31, 2022
Java library for the HyperLogLog algorithm

java-hll A Java implementation of HyperLogLog whose goal is to be storage-compatible with other similar offerings from Aggregate Knowledge. NOTE: This

Aggregate Knowledge (a Neustar service) 296 Dec 30, 2022
A simple integer compression library in Java

JavaFastPFOR: A simple integer compression library in Java License This code is released under the Apache License Version 2.0 http://www.apache.org/li

Daniel Lemire 487 Dec 30, 2022
Java Collections till the last breadcrumb of memory and performance

Koloboke A family of projects around collections in Java (so far). The Koloboke Collections API A carefully designed extension of the Java Collections

Roman Leventov 967 Nov 14, 2022
Port of LevelDB to Java

LevelDB in Java This is a rewrite (port) of LevelDB in Java. This goal is to have a feature complete implementation that is within 10% of the performa

Dain Sundstrom 1.4k Dec 30, 2022
LMDB for Java

LMDB JNI LMDB JNI provide a Java API to LMDB which is an ultra-fast, ultra-compact key-value embedded data store developed by Symas for the OpenLDAP P

deephacks 201 Apr 6, 2022
Lightning Memory Database (LMDB) for Java: a low latency, transactional, sorted, embedded, key-value store

LMDB for Java LMDB offers: Transactions (full ACID semantics) Ordered keys (enabling very fast cursor-based iteration) Memory-mapped files (enabling o

null 680 Dec 23, 2022