Skip to content

Instantly share code, notes, and snippets.

@koduki
Last active October 14, 2018 02:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save koduki/fdeb93b87dfd41aa7d8add9c5d4f3815 to your computer and use it in GitHub Desktop.
Save koduki/fdeb93b87dfd41aa7d8add9c5d4f3815 to your computer and use it in GitHub Desktop.
Java 11でJMC/Flight Recorderが完全OSS化したのでJFR APIを使ったマイクロベンチマークツールのサンプル。解説は https://qiita.com/koduki/items/6e8fbcbafdcc2f743f56
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package cn.orz.pascal.tinybench;
import java.io.IOException;
import java.nio.file.Path;
import java.text.ParseException;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import jdk.jfr.Configuration;
import jdk.jfr.Event;
import jdk.jfr.Label;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordingFile;
import static cn.orz.pascal.jl2.Extentions.*;
import cn.orz.pascal.jl2.collections.Tuples;
import java.util.Comparator;
import java.util.Map;
import java.util.stream.LongStream;
import jdk.jfr.consumer.RecordedEvent;
/**
*
* @author koduki
*/
public class TinyBenchmark {
@Label("Benchmark")
static class BenchmarkEvent extends Event {
@Label("Test Case")
String testcase;
@Label("Loop Count")
int loop;
@Label("Response")
long response;
@Label("Arguments")
String arguments;
@Override
public String toString() {
return "BenchmarkEvent{" + "testcase=" + testcase + ", loop=" + loop + ", response=" + response + ", arguments=" + arguments + '}';
}
}
public static void benchmarks(Path path, String name, int loopCount, Consumer<Integer> callback) throws IOException, ParseException {
Configuration jfrConfig = Configuration.getConfiguration("default");
try (Recording recording = new Recording(jfrConfig)) {
recording.setName(name);
recording.start();
recording.enable(BenchmarkEvent.class);
for (int i = 0; i < loopCount; i++) {
callback.accept(i);
}
recording.stop();
recording.dump(path);
}
}
public static void benchmark(int loopCount, String methodName, Runnable callback, Object... arguments) {
BenchmarkEvent event = new BenchmarkEvent();
event.begin();
long s = System.nanoTime();
callback.run();
long e = System.nanoTime();
event.loop = loopCount;
event.testcase = methodName;
event.response = (e - s);
List<String> args = Arrays.stream(arguments).map(x -> x.toString()).collect(Collectors.toList());
event.arguments = "[" + String.join(",", args) + "]";
event.commit();
}
public static void printReport(List<Map<String, Object>> result) {
result.forEach(e -> {
String fmt = String.join("\t",
"testcase:%s",
"loop:%d",
"arguments:%s",
"response(ms):%d",
"gc_count:%d",
"gc_total(ms):%d",
"gc_max(ms):%d",
"gc_avg(ms):%f"
);
String msg = String.format(fmt,
e.get("testcase"),
e.get("loop"),
e.get("arguments"),
(Long) (e.get("response")) / 100_0000,
e.get("gc_count"),
e.get("gc_total"),
e.get("gc_max"),
e.get("gc_avg")
);
System.out.println(msg);
});
}
public static List<Map<String, Object>> makeReport(Path path) throws IOException {
List<RecordedEvent> gcEvents = getGCEvents(path);
List<RecordedEvent> bmEvent = getBenchmarkEvent(path);
List<Map<String, Object>> result = bmEvent.stream()
.map((event) -> parseBenchmarkEvent(gcEvents, event))
.sorted(Comparator.comparing(x -> (Long) (x.get("startTime"))))
.collect(Collectors.toList());
return result;
}
static Map<String, Object> parseBenchmarkEvent(List<RecordedEvent> gcEvents, RecordedEvent event) {
Tuples.Tuple4 gc = calcGCValues(gcEvents, event);
return map(
$("testcase", event.getValue("testcase")),
$("loop", event.getValue("loop")),
$("response", event.getValue("response")),
$("arguments", event.getValue("arguments")),
$("gc_count", gc._1()),
$("gc_total", gc._2()),
$("gc_max", gc._3()),
$("gc_avg", gc._4()),
$("startTime", event.getStartTime().toEpochMilli())
);
}
static List<RecordedEvent> getBenchmarkEvent(Path path) throws IOException {
List<RecordedEvent> bmEvent = RecordingFile.readAllEvents(path).stream()
.filter((e) -> e.getEventType().getName().equals(BenchmarkEvent.class.getName()))
.map(e -> e)
.collect(Collectors.toList());
return bmEvent;
}
static List<RecordedEvent> getGCEvents(Path path) throws IOException {
List<RecordedEvent> gcEvents = RecordingFile.readAllEvents(path).stream()
.filter((e) -> e.getEventType().getName().equals("jdk.G1GarbageCollection"))
.collect(Collectors.toList());
return gcEvents;
}
static Tuples.Tuple4<Integer, Long, Long, Double> calcGCValues(List<RecordedEvent> gcEvents, RecordedEvent bmEvent) {
long[] gc = gcEvents.stream()
.filter(e -> isContain(bmEvent, e))
.mapToLong(e -> e.getDuration().toMillis())
.toArray();
long max = (gc.length == 0) ? 0 : LongStream.of(gc).max().getAsLong();
double avg = (gc.length == 0) ? 0 : LongStream.of(gc).average().getAsDouble();
long total = LongStream.of(gc).sum();
int count = gc.length;
return $(count, total, max, avg);
}
static boolean isContain(RecordedEvent longEvent, RecordedEvent shortEvent) {
return longEvent.getStartTime().compareTo(shortEvent.getStartTime()) == -1
&& shortEvent.getEndTime().compareTo(longEvent.getEndTime()) == -1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment