The Renaissance Benchmark Suite aggregates common modern JVM workloads, including, but not limited to, Big Data, machine-learning, and functional programming. The suite is intended to be used to optimize just-in-time compilers, interpreters, GCs, and for tools such as profilers, debuggers, or static analyzers, and even different hardware. It is intended to be an open-source, collaborative project, in which the community can propose and improve benchmark workloads.
To build the suite and create the so-called fat JAR (or super JAR), you only
need to run sbt
build tool as follows:
$ tools/sbt/bin/sbt assembly
This will retrieve all the dependencies, compile all the benchmark projects and the harness,
bundle the JARs and create the final JAR under the target
directory.
To run a Renaissance benchmark, you need to have a JRE installed.
This allows you to execute the following java
command:
$ java -jar '<renaissance-home>/target/renaissance-gpl-0.10.0.jar' <benchmarks>
Above, the <renaissance-home>
is the path to the root directory of the Renaissance distribution,
and <benchmarks>
is the list of benchmarks that you wish to run.
For example, you can specify scala-kmeans
as the benchmark.
The suite generally executes the benchmark's measured operation multiple times. By default, the suite executes each benchmark operation for a specific number of times. The benchmark-specific number of repetitions is only intended for quick visual evaluation of benchmark execution time, but is not sufficient for thorough experimental evaluation, which will generally need much more repetitions.
For thorough experimental evaluation, the benchmarks should be repeated for a large number of
times or executed for a long time. The number of repetitions and the execution time can be
set for all benchmarks using the -r
or -t
options. More fine-grained control over benchmark
execution can be achieved by providing the harness with a plugin implementing a custom execution
policy (see below for details).
The following is a complete list of command-line options.
Renaissance Benchmark Suite, version 0.10.0
Usage: renaissance [options] [benchmark-specification]
-h, --help Prints this usage text.
-r, --repetitions <count>
Execute the measured operation a fixed number of times.
-t, --run-seconds <seconds>
Execute the measured operation for fixed time (wall-clock).
--operation-run-seconds <seconds>
Execute the measured operation for fixed accumulated operation time (wall-clock).
--policy <class-path>!<class-name>
Use policy plugin to control repetition of measured operation execution.
--plugin <class-path>!<class-name>
Load external plugin. Can appear multiple times.
--with-arg <value> Adds an argument to the plugin or policy specified last. Can appear multiple times.
--csv <file-path> Output results to CSV file.
--json <file-path> Output results to JSON file.
-c, --configuration <name>
Run benchmarks with given named configuration.
--list Print list of benchmarks with their description.
--raw-list Print list of benchmarks (each benchmark name on separate line).
--group-list Print list of benchmark groups (each group name on separate line).
benchmark-specification Comma-separated list of benchmarks (or groups) that must be executed (or all).
The following is the complete list of benchmarks, separated into groups.
-
akka-uct
- Runs the Unbalanced Cobwebbed Tree actor workload in Akka. (default repetitions: 24) -
reactors
- Runs benchmarks inspired by the Savina microbenchmark workloads in a sequence on Reactors.IO. (default repetitions: 10)
-
als
- Runs the ALS algorithm from the Spark MLlib. (default repetitions: 30) -
chi-square
- Runs the chi-square test from Spark MLlib. (default repetitions: 60) -
dec-tree
- Runs the Random Forest algorithm from Spark MLlib. (default repetitions: 40) -
gauss-mix
- Computes a Gaussian mixture model using expectation-maximization. (default repetitions: 40) -
log-regression
- Runs the logistic regression workload from the Spark MLlib. (default repetitions: 20) -
movie-lens
- Recommends movies using the ALS algorithm. (default repetitions: 20) -
naive-bayes
- Runs the multinomial naive Bayes algorithm from the Spark MLlib. (default repetitions: 30) -
page-rank
- Runs a number of PageRank iterations, using RDDs. (default repetitions: 20)
db-shootout
- Executes a shootout test using several in-memory databases. (default repetitions: 16)
-
dummy-empty
- A dummy benchmark which only serves to test the harness. (default repetitions: 20) -
dummy-failing
- A dummy benchmark for testing the harness (fails during iteration). (default repetitions: 20) -
dummy-param
- A dummy benchmark for testing the harness (test configurable parameters). (default repetitions: 20) -
dummy-setup-failing
- A dummy benchmark for testing the harness (fails during setup). (default repetitions: 20) -
dummy-teardown-failing
- A dummy benchmark for testing the harness (fails during teardown). (default repetitions: 20) -
dummy-validation-failing
- A dummy benchmark for testing the harness (fails during validation). (default repetitions: 20)
-
fj-kmeans
- Runs the k-means algorithm using the fork/join framework. (default repetitions: 30) -
future-genetic
- Runs a genetic algorithm using the Jenetics library and futures. (default repetitions: 50)
-
mnemonics
- Solves the phone mnemonics problem using JDK streams. (default repetitions: 16) -
par-mnemonics
- Solves the phone mnemonics problem using parallel JDK streams. (default repetitions: 16) -
scrabble
- Solves the Scrabble puzzle using JDK Streams. (default repetitions: 50)
neo4j-analytics
- Executes Neo4J graph queries against a movie database. (default repetitions: 20)
rx-scrabble
- Solves the Scrabble puzzle using the Rx streams. (default repetitions: 80)
dotty
- Runs the Dotty compiler on a set of source code files. (default repetitions: 50)
scala-doku
- Solves Sudoku Puzzles using Scala collections. (default repetitions: 20)
scala-kmeans
- Runs the K-Means algorithm using Scala collections. (default repetitions: 50)
-
philosophers
- Solves a variant of the dining philosophers problem using ScalaSTM. (default repetitions: 30) -
scala-stm-bench7
- Runs the stmbench7 benchmark using ScalaSTM. (default repetitions: 60)
-
finagle-chirper
- Simulates a microblogging service using Twitter Finagle. (default repetitions: 90) -
finagle-http
- Sends many small Finagle HTTP requests to a Finagle HTTP server and awaits response. (default repetitions: 12)
If you are using an external tool to inspect a benchmark, such as an instrumentation agent, or a profiler, then you may need to make this tool aware of when a benchmark's measured operation is about to be executed and when it finished executing.
If you need to collect additional metrics associated with the execution of the measured operation, e.g., hardware counters, you will need to be notified about operation execution, and you may want to store the measured values in the output files produced by the harness.
If you need the harness to produce output files in different format (other than CSV or JSON), you will need to be notified about values of metrics collected by the harness and other plugins.
If you need more fine-grained control over the repetition of the benchmark's measured operation, you will need to be able to tell the harness when to keep executing the benchmark and when to stop.
To this end, the suite provides hooks for plugins which can subscribe to events related to
harness state and benchmark execution. A plugin is a user-defined class which must implement
the Plugin
marker interface and provide at least a default (parameter-less)
constructor. However, such a minimal plugin would not receive any notifications. To receive
notifications, the plugin class must implement interfaces from the Plugin
interface name space depending on the type of events it wants to receive, or services it wants
to provide. This is demonstrated in the following example:
class SimplePlugin extends Plugin
with HarnessInitListener
with OperationSetUpListener
with OperationTearDownListener {
override def afterHarnessInit() = {
// Initialize the plugin after the harness finished initializing
}
override def afterOperationSetUp(benchmark: String, index: Int) = {
// Notify the tool that the measured operation is about to start.
}
override def beforeOperationTearDown(benchmark: String, index: Int) = {
// Notify the tool that the measured operations has finished.
}
}
The following interfaces provide common (paired) event types which allow a plugin to hook into a specific point in the benchmark execution sequence. They are analogous to common annotations known from testing frameworks such as JUnit.
HarnessInitListener
HarnessShutdownListener
BenchmarkSetUpListener
BenchmarkTearDownListener
OperationSetUpListener
OperationTearDownListener
The following interfaces provide special non-paired event types:
MeasurementResultListener
, intended for plugins that want to receive measurements results (perhaps to store them in a custom format). The harness calls theonMeasurementResult
method with the name of the metric and its value, but only if the benchmark operation produces a valid result.BenchmarkFailureListener
, which indicates that the benchmark execution has either failed in some way (the benchmark triggered an exception), or that the benchmark operation produced a result which failed validation. This means that no measurements results will be received.
And finally the following interface are used by the harness to request services from plugins:
MeasurementResultPublisher
, intended for plugins that want to collect values of additional metrics around the execution of the benchmark operation. The harness calls theonMeasurementResultsRequested
method with an instance of event dispatcher which the plugin is supposed to use to notify other result listeners about custom measurement results.ExecutionPolicy
, intended for plugins that want to control the execution of the benchmark's measured operation. Such a plugin should implement other interfaces to get enough information to determine, per-benchmark, whether to execute the measured operation or not. The harness calls thecanExecute
method before executing the benchmark's measured operation, and will pass the result ofisLast
method to some other events.
To make the harness use an external plugin, it needs to be specified on the command line.
The harness can load multiple plugins, and each must be enabled using the
--plugin <class-path>!<class-name>
option. The <class-path>
is the class path on which
to look for the plugin class, and <class-name>
is a fully qualified name of the plugin class.
Custom execution policy must be enabled using the --policy <class-path>!<class-name>
option.
The syntax is the same as in case of normal plugins (and the policy is also a plugin, which
can register for all event types), but this option tells the harness to actually use the
plugin to control benchmark execution. Other than that, policy is treated the same was as
plugin.
When registering plugins for pair events (harness init/shutdown, benchmark set up/tear down, operation set up/tear down), the plugins specified earlier "wrap" plugins specified later. This means that plugins that need to be the closest to the measured operation need to be specified last. Note that this also applies to the execution policy, which would be generally specified first, but any order is possible.
Plugins (and policies) can receive additional command line arguments. Each argument must be
given using the --with-arg <arg>
option, which appends <arg>
to the list of arguments for
the plugin (or policy) that was last mentioned on the command line. Whenever a --plugin
(or --policy
) option is encountered, the subsequent --with-arg
options will append
arguments to that plugin (or policy). A plugin that wants to receive command line arguments
must define a constructor which takes an array of strings (String[]
) or a string vararg
(String...
) as parameter. The harness tries to use this constructor first and falls back
to the default (parameter-less) constructor.
You can also build and run Renaissance with JMH. To build a JMH-enabled JAR, run:
$ tools/sbt/bin/sbt renaissanceJmh/jmh:assembly
To run the benchmarks using JMH, you can execute the following java
command:
$ java -jar 'renaissance-jmh/target/scala-2.12/renaissance-jmh-assembly-0.10.0.jar'
Please see the CONTRIBUTION page for a description of the contributing process.
The Renaissance Suite comes in two distributions, and is available under both the MIT license and the GPL3 license. The GPL distribution with all the benchmarks is licensed under the GPL3 license, while the MIT distribution includes only those benchmarks that themselves have less restrictive licenses.
Depending on your needs, you can use either of the two distributions. The following table contains the licensing information of all the benchmarks:
Benchmark | Licenses | Renaissance Distro |
---|---|---|
akka-uct | MIT | MIT |
als | APACHE2 | MIT |
chi-square | APACHE2 | MIT |
db-shootout | APACHE2 | MIT |
dec-tree | APACHE2 | MIT |
dotty | BSD3 | MIT |
dummy-empty | MIT | MIT |
dummy-failing | MIT | MIT |
dummy-param | MIT | MIT |
dummy-setup-failing | MIT | MIT |
dummy-teardown-failing | MIT | MIT |
dummy-validation-failing | MIT | MIT |
finagle-chirper | APACHE2 | MIT |
finagle-http | APACHE2 | MIT |
fj-kmeans | APACHE2 | MIT |
future-genetic | APACHE2 | MIT |
gauss-mix | APACHE2 | MIT |
log-regression | APACHE2 | MIT |
mnemonics | MIT | MIT |
movie-lens | APACHE2 | MIT |
naive-bayes | APACHE2 | MIT |
neo4j-analytics | GPL3 | GPL3 |
page-rank | APACHE2 | MIT |
par-mnemonics | MIT | MIT |
philosophers | BSD3 | MIT |
reactors | MIT | MIT |
rx-scrabble | GPL2 | GPL3 |
scala-doku | MIT | MIT |
scala-kmeans | MIT | MIT |
scala-stm-bench7 | BSD3, GPL2 | GPL3 |
scrabble | GPL2 | GPL3 |
The Renaissance benchmark suite is organized into several sbt
projects:
- the
renaissance-core
folder that contains a set of core classes (common interfaces and a harness launcher) - the
renaissance-harness
folder that contains the actual harness - the
benchmarks
folder contains a set of subprojects, each containing a set of benchmarks for a specific domain (and having a separate set of dependencies)
The core project is written in pure Java, and it contains the basic benchmark API.
Its most important elements are the Benchmark
interface,
which must be implemented by each benchmark, and the annotations in the
Benchmark
interface name space, which are used to provide
benchmark meta data, such as a summary or a detailed description.
Consequently, each subproject depends on the core project.
Classes from the core are loaded (when Renaissance is started) by the default classloader. Classes from other projects (including the harness and individual benchmarks) and external plugins or execution policies are loaded by separate classloaders. This separation helps ensure that there are no clashes between dependencies of different projects (each benchmark may depend on different versions of external libraries).
The harness project implements the functionality necessary to parse the input arguments, to run the benchmarks, to generate documentation, and so on. The harness is written in a mix of Java and Scala, and is loaded by the core in a separate classloader to ensure clean environment for running the benchmarks.
The JARs of the subprojects (benchmarks and harness) are copied as generated resources and embedded into the resulting JAR artifact.
renaissance-core
^
| (classpath dependencies)
|
|-- renaissance harness
|
|-- benchmark one
| `-- dependencies for benchmark one
|
|-- ...
|
`-- benchmark n
When the harness is started, it uses the input arguments to select the benchmark, and then unpacks the JARs of the corresponding benchmark group into a temporary directory. The harness then creates a classloader that searches the unpacked JARs and loads the benchmark group. The class loader is created directly below the default class loader. Because the default class loader contains only base JRE classes and common interfaces of core, it ensures that dependencies of a benchmark are never mixed with any dependencies of any other benchmark or the harness.
boot class loader (JDK)
^
|
system class loader
(core)
^ ^
| |
URL class loader URL class loader
(harness) (benchmark)
We need to do this to, e.g., avoid accidentally resolving the wrong class by going through the system class loader (this can easily happen with, e.g. Apache Spark and Scala, due to the way that Spark internally resolves some classes).
You can see the further details of the build system in the top-level build.sbt
file,
and in the source code of the RenaissanceSuite
and
ModuleLoader
classes.