ScalaMeter tests can be (despite what the name might suggest) written in Java as well. This section shows the basics of how to do this. The short story is: Java users use an alternative annotation-based frontend, where generators, setups, teardowns, configs and benchmarks snippets are specified with annotations instead of a special DSL. This frontend is available for both Java and Scala users.
Lets create a new file named MyHistogramTest.java
.
We will use it to write an offline report test,
which tests the performance of a histogram application.
We will traverse a linked list and
group its elements to different linked lists in a hash map.
We start by importing the following package,
which allows us to use the ScalaMeter’s Java interface:
import org.scalameter.japi.*;
Next, we import required anntoations from the annotation
subpackage:
import org.scalameter.japi.annotation.*;
We should also import the following classes for our test:
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
We extend the JBench.OfflineReport
class –
you can read more about it in the
configuration section:
public class MyHistogramTest extends JBench.OfflineReport {
Benchmarks in a test are organized into groups.
In the Scala frontend, we encode the groups using nested closures.
In the Java frontend, we encode them by placing the benchmark
annotation
whose String
argument denotes the group that the benchmark belongs to.
We now declare benchmark snippet stub in the ops
group that is nested
in the list
group:
@benchmark("list.ops")
public void histogram() {
}
Next, we want to declare a custom configuration context, which we will name config
:
public final Context config = new ContextBuilder()
.put("exec.benchRuns", 20)
.put("exec.independentSamples", 1)
.put("exec.outliers.covMultiplier", 1.5)
.put("exec.outliers.suspectPercent", 40)
.build();
Let’s assume that we want to apply this custom config
not only to our
histogram
method, but to all benchmark snippets in the list
group.
We do this by adding the optional scopes
annotation to the MyHistogramTest
class,
as follows:
@scopes({
@scopeCtx(scope = "list", context = "config")
})
public class MyHistogramTest extends JBench.OfflineReport {
Next, we declare a generator for our snippet (see more about generators in the generator section).
public final JGen<Integer> sizes = JGen.intValues("size", 500000);
public final JGen<Tuple2<Integer, LinkedList<Integer>>> lists =
sizes.zip(sizes.map(
new Fun1<Integer, LinkedList<Integer>>() {
public LinkedList<Integer> apply(Integer v) {
return new LinkedList<Integer>();
}
}
));
@gen("lists")
@benchmark("list.ops")
public void histogram(Tuple2<Integer, LinkedList<Integer>> v) {
}
Now, assume that the list needs to be initialized with random data before the
benchmark begins.
We will declare our own custom setup method named listSetup
that does this initialization.
Then, to ensure that listSetup
is invoked before the benchmark,
we need to add the @setup
annotation to the histogram
method:
public void listSetup(Tuple2<Integer, LinkedList<Integer>> v) {
int size = v._1();
LinkedList<Integer> list = v._2();
Random random = new Random(size);
for (int i = 0; i < size; i++) {
list.add(random.nextInt(size));
}
}
@gen("lists")
@setup("listSetup")
@benchmark("list.ops")
public void histogram(Tuple2<Integer, LinkedList<Integer>> v) {
}
By default, the curve corresponding to the benchmark snippet
will be equal to the method name.
If we would like a custom name for this method, we need to use the @curve
annotation:
@gen("lists")
@setup("listSetup")
@curve("groupBy")
@benchmark("list.ops")
public void histogram(Tuple2<Integer, LinkedList<Integer>> v) {
}
Finally, we add the actual code of the snippet method:
@gen("lists")
@setup("listSetup")
@curve("groupBy")
@benchmark("list.ops")
public HashMap<Integer, LinkedList<Integer>> histogram(
Tuple2<Integer, LinkedList<Integer>> v
) {
LinkedList<Integer> list = v._2();
HashMap<Integer, LinkedList<Integer>> hm =
new HashMap<Integer, LinkedList<Integer>>();
for (int i = 0; i < 10; i++) {
hm.put(i, new LinkedList<Integer>());
}
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
Integer element = it.next();
LinkedList<Integer> tmp = hm.get(element % 10);
tmp.add(element);
hm.put(element % 10, tmp);
}
return hm;
}
The complete test is here:
@scopes({
@scopeCtx(scope = "list", context = "config")
})
public class MyHistogramTest extends JBench.OfflineReport {
public final Context config = new ContextBuilder()
.put("exec.benchRuns", 20)
.put("exec.independentSamples", 1)
.put("exec.outliers.covMultiplier", 1.5)
.put("exec.outliers.suspectPercent", 40)
.build();
public final JGen<Integer> sizes = JGen.intValues("size", 500000);
public final JGen<Tuple2<Integer, LinkedList<Integer>>> lists =
sizes.zip(sizes.map(
new Fun1<Integer, LinkedList<Integer>>() {
public LinkedList<Integer> apply(Integer v) {
return new LinkedList<Integer>();
}
}
));
public void listSetup(Tuple2<Integer, LinkedList<Integer>> v) {
int size = v._1();
LinkedList<Integer> list = v._2();
Random random = new Random(size);
for (int i = 0; i < size; i++) {
list.add(random.nextInt(size));
}
}
@gen("lists")
@setup("listSetup")
@curve("groupBy")
@benchmark("list.ops")
public HashMap<Integer, LinkedList<Integer>> histogram(
Tuple2<Integer, LinkedList<Integer>> v
) {
LinkedList<Integer> list = v._2();
HashMap<Integer, LinkedList<Integer>> hm =
new HashMap<Integer, LinkedList<Integer>>();
for (int i = 0; i < 10; i++) {
hm.put(i, new LinkedList<Integer>());
}
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
Integer element = it.next();
LinkedList<Integer> tmp = hm.get(element % 10);
tmp.add(element);
hm.put(element % 10, tmp);
}
return hm;
}
}
A complete example of an SBT project using ScalaMeter from Java is available in the ScalaMeter examples repo.