/*
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.graalvm.compiler.truffle.debug;
import static java.util.function.Function.identity;
import static org.graalvm.compiler.truffle.TruffleCompilerOptions.TruffleCompilationStatisticDetails;
import static org.graalvm.compiler.truffle.TruffleCompilerOptions.TruffleCompilationStatistics;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.LongSummaryStatistics;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.function.Function;
import org.graalvm.compiler.code.CompilationResult;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.truffle.GraalTruffleRuntime;
import org.graalvm.compiler.truffle.OptimizedCallTarget;
import org.graalvm.compiler.truffle.OptimizedCompilationProfile;
import org.graalvm.compiler.truffle.OptimizedDirectCallNode;
import org.graalvm.compiler.truffle.TruffleCompilerOptions;
import org.graalvm.compiler.truffle.TruffleInlining;
import org.graalvm.compiler.truffle.TruffleInlining.CallTreeNodeVisitor;
import org.graalvm.compiler.truffle.TruffleInliningDecision;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.LoopNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeCost;
import jdk.vm.ci.code.site.Infopoint;
public final class CompilationStatisticsListener extends AbstractDebugCompilationListener {
private long firstCompilation;
private int compilations;
private int invalidations;
private int failures;
private int success;
private int queues;
private int dequeues;
private int splits;
private final IntSummaryStatistics deferCompilations = new IntSummaryStatistics();
private final LongSummaryStatistics timeToQueue = new LongSummaryStatistics();
private final LongSummaryStatistics timeToCompilation = new LongSummaryStatistics();
private final IntSummaryStatistics nodeCount = new IntSummaryStatistics();
private final IntSummaryStatistics nodeCountTrivial = new IntSummaryStatistics();
private final IntSummaryStatistics nodeCountNonTrivial = new IntSummaryStatistics();
private final IntSummaryStatistics nodeCountMonomorphic = new IntSummaryStatistics();
private final IntSummaryStatistics nodeCountPolymorphic = new IntSummaryStatistics();
private final IntSummaryStatistics nodeCountMegamorphic = new IntSummaryStatistics();
private final IdentityStatistics<Class<?>> nodeStatistics = new IdentityStatistics<>();
private final IntSummaryStatistics callCount = new IntSummaryStatistics();
private final IntSummaryStatistics callCountIndirect = new IntSummaryStatistics();
private final IntSummaryStatistics callCountDirect = new IntSummaryStatistics();
private final IntSummaryStatistics callCountDirectDispatched = new IntSummaryStatistics();
private final IntSummaryStatistics callCountDirectInlined = new IntSummaryStatistics();
private final IntSummaryStatistics callCountDirectCloned = new IntSummaryStatistics();
private final IntSummaryStatistics callCountDirectNotCloned = new IntSummaryStatistics();
private final IntSummaryStatistics loopCount = new IntSummaryStatistics();
private final LongSummaryStatistics compilationTime = new LongSummaryStatistics();
private final LongSummaryStatistics compilationTimeTruffleTier = new LongSummaryStatistics();
private final LongSummaryStatistics compilationTimeGraalTier = new LongSummaryStatistics();
private final LongSummaryStatistics compilationTimeCodeInstallation = new LongSummaryStatistics();
private final IntSummaryStatistics truffleTierNodeCount = new IntSummaryStatistics();
private final IdentityStatistics<Class<?>> truffleTierNodeStatistics = new IdentityStatistics<>();
private final IntSummaryStatistics graalTierNodeCount = new IntSummaryStatistics();
private final IdentityStatistics<Class<?>> graalTierNodeStatistics = new IdentityStatistics<>();
private final IntSummaryStatistics compilationResultCodeSize = new IntSummaryStatistics();
private final IntSummaryStatistics compilationResultExceptionHandlers = new IntSummaryStatistics();
private final IntSummaryStatistics compilationResultInfopoints = new IntSummaryStatistics();
private final IdentityStatistics<String> compilationResultInfopointStatistics = new IdentityStatistics<>();
private final IntSummaryStatistics compilationResultMarks = new IntSummaryStatistics();
private final IntSummaryStatistics compilationResultTotalFrameSize = new IntSummaryStatistics();
private final IntSummaryStatistics compilationResultDataPatches = new IntSummaryStatistics();
private CompilationStatisticsListener() {
}
private final ThreadLocal<CompilationLocal> compilationLocal = new ThreadLocal<>();
public static void install(GraalTruffleRuntime runtime) {
if (TruffleCompilerOptions.getValue(TruffleCompilationStatistics) || TruffleCompilerOptions.getValue(TruffleCompilationStatisticDetails)) {
runtime.addCompilationListener(new CompilationStatisticsListener());
}
}
@Override
public synchronized void notifyCompilationSplit(OptimizedDirectCallNode callNode) {
splits++;
}
@Override
public synchronized void notifyCompilationQueued(OptimizedCallTarget target) {
queues++;
if (firstCompilation == 0) {
firstCompilation = System.nanoTime();
}
OptimizedCompilationProfile profile = target.getCompilationProfile();
if (profile != null) {
timeToQueue.accept(System.nanoTime() - profile.getTimestamp());
}
}
@Override
public synchronized void notifyCompilationDequeued(OptimizedCallTarget target, Object source, CharSequence reason) {
dequeues++;
}
@Override
public synchronized void notifyCompilationFailed(OptimizedCallTarget target, StructuredGraph graph, Throwable t) {
failures++;
}
@Override
public synchronized void notifyCompilationInvalidated(OptimizedCallTarget target, Object source, CharSequence reason) {
invalidations++;
}
@Override
public synchronized void notifyCompilationStarted(OptimizedCallTarget target) {
compilations++;
CompilationLocal local = new CompilationLocal();
local.compilationStarted = System.nanoTime();
compilationLocal.set(local);
OptimizedCompilationProfile profile = target.getCompilationProfile();
if (profile != null) {
deferCompilations.accept(profile.getDeferredCount());
timeToCompilation.accept(local.compilationStarted - profile.getTimestamp());
}
}
@Override
public synchronized void notifyCompilationTruffleTierFinished(OptimizedCallTarget target, TruffleInlining inliningDecision, StructuredGraph graph) {
compilationLocal.get().truffleTierFinished = System.nanoTime();
nodeStatistics.accept(nodeClasses(target, inliningDecision));
CallTargetNodeStatistics callTargetStat = new CallTargetNodeStatistics(target, inliningDecision);
nodeCount.accept(callTargetStat.getNodeCount());
nodeCountTrivial.accept(callTargetStat.getNodeCountTrivial());
nodeCountNonTrivial.accept(callTargetStat.getNodeCountNonTrivial());
nodeCountMonomorphic.accept(callTargetStat.getNodeCountMonomorphic());
nodeCountPolymorphic.accept(callTargetStat.getNodeCountPolymorphic());
nodeCountMegamorphic.accept(callTargetStat.getNodeCountMegamorphic());
callCount.accept(callTargetStat.getCallCount());
callCountIndirect.accept(callTargetStat.getCallCountIndirect());
callCountDirect.accept(callTargetStat.getCallCountDirect());
callCountDirectDispatched.accept(callTargetStat.getCallCountDirectDispatched());
callCountDirectInlined.accept(callTargetStat.getCallCountDirectInlined());
callCountDirectCloned.accept(callTargetStat.getCallCountDirectCloned());
callCountDirectNotCloned.accept(callTargetStat.getCallCountDirectNotCloned());
loopCount.accept(callTargetStat.getLoopCount());
truffleTierNodeCount.accept(graph.getNodeCount());
if (TruffleCompilerOptions.getValue(TruffleCompilationStatisticDetails)) {
truffleTierNodeStatistics.accept(nodeClasses(graph));
}
}
@Override
public synchronized void notifyCompilationGraalTierFinished(OptimizedCallTarget target, StructuredGraph graph) {
compilationLocal.get().graalTierFinished = System.nanoTime();
graalTierNodeCount.accept(graph.getNodeCount());
if (TruffleCompilerOptions.getValue(TruffleCompilationStatisticDetails)) {
graalTierNodeStatistics.accept(nodeClasses(graph));
}
}
private static Collection<String> infopoints(CompilationResult result) {
Collection<String> infopoints = new ArrayList<>();
for (Infopoint infopoint : result.getInfopoints()) {
infopoints.add(infopoint.reason.toString());
}
return infopoints;
}
private static Collection<Class<?>> nodeClasses(OptimizedCallTarget target, TruffleInlining inliningDecision) {
Collection<Class<?>> nodeClasses = new ArrayList<>();
for (Node node : target.nodeIterable(inliningDecision)) {
if (node != null) {
nodeClasses.add(node.getClass());
}
}
return nodeClasses;
}
private static Collection<Class<?>> nodeClasses(StructuredGraph graph) {
Collection<Class<?>> classes = new ArrayList<>();
for (org.graalvm.compiler.graph.Node node : graph.getNodes()) {
classes.add(node.getClass());
}
return classes;
}
@Override
public synchronized void notifyCompilationSuccess(OptimizedCallTarget target, TruffleInlining inliningDecision, StructuredGraph graph, CompilationResult result) {
success++;
long compilationDone = System.nanoTime();
CompilationLocal local = compilationLocal.get();
compilationTime.accept(compilationDone - local.compilationStarted);
compilationTimeTruffleTier.accept(local.truffleTierFinished - local.compilationStarted);
compilationTimeGraalTier.accept(local.graalTierFinished - local.truffleTierFinished);
compilationTimeCodeInstallation.accept(compilationDone - local.graalTierFinished);
compilationResultCodeSize.accept(result.getTargetCodeSize());
compilationResultTotalFrameSize.accept(result.getTotalFrameSize());
compilationResultExceptionHandlers.accept(result.getExceptionHandlers().size());
compilationResultInfopoints.accept(result.getInfopoints().size());
compilationResultInfopointStatistics.accept(infopoints(result));
compilationResultMarks.accept(result.getMarks().size());
compilationResultDataPatches.accept(result.getDataPatches().size());
}
@Override
public void notifyShutdown(GraalTruffleRuntime rt) {
printStatistics(rt);
}
public void printStatistics(GraalTruffleRuntime rt) {
long endTime = System.nanoTime();
rt.log("Truffle compilation statistics:");
printStatistic(rt, "Compilations", compilations);
printStatistic(rt, " Success", success);
printStatistic(rt, " Failed", failures);
printStatistic(rt, " Interrupted", compilations - (success + failures));
printStatistic(rt, "Invalidated", invalidations);
printStatistic(rt, "Queues", queues);
printStatistic(rt, "Dequeues", dequeues);
printStatistic(rt, "Splits", splits);
printStatistic(rt, "Compilation Accuracy", 1.0 - invalidations / (double) compilations);
printStatistic(rt, "Queue Accuracy", 1.0 - dequeues / (double) queues);
printStatistic(rt, "Compilation Utilization", compilationTime.getSum() / (double) (endTime - firstCompilation));
printStatistic(rt, "Remaining Compilation Queue", rt.getCompilationQueueSize());
printStatistic(rt, "Times defered until compilation", deferCompilations);
printStatisticTime(rt, "Time to queue", timeToQueue);
printStatisticTime(rt, "Time to compilation", timeToCompilation);
printStatisticTime(rt, "Compilation time", compilationTime);
printStatisticTime(rt, " Truffle Tier", compilationTimeTruffleTier);
printStatisticTime(rt, " Graal Tier", compilationTimeGraalTier);
printStatisticTime(rt, " Code Installation", compilationTimeCodeInstallation);
printStatistic(rt, "Truffle node count", nodeCount);
printStatistic(rt, " Trivial", nodeCountTrivial);
printStatistic(rt, " Non Trivial", nodeCountNonTrivial);
printStatistic(rt, " Monomorphic", nodeCountMonomorphic);
printStatistic(rt, " Polymorphic", nodeCountPolymorphic);
printStatistic(rt, " Megamorphic", nodeCountMegamorphic);
printStatistic(rt, "Truffle call count", callCount);
printStatistic(rt, " Indirect", callCountIndirect);
printStatistic(rt, " Direct", callCountDirect);
printStatistic(rt, " Dispatched", callCountDirectDispatched);
printStatistic(rt, " Inlined", callCountDirectInlined);
printStatistic(rt, " ----------");
printStatistic(rt, " Cloned", callCountDirectCloned);
printStatistic(rt, " Not Cloned", callCountDirectNotCloned);
printStatistic(rt, "Truffle loops", loopCount);
printStatistic(rt, "Graal node count");
printStatistic(rt, " After Truffle Tier", truffleTierNodeCount);
printStatistic(rt, " After Graal Tier", graalTierNodeCount);
printStatistic(rt, "Graal comilation result");
printStatistic(rt, " Code size", compilationResultCodeSize);
printStatistic(rt, " Total frame size", compilationResultTotalFrameSize);
printStatistic(rt, " Exception handlers", compilationResultExceptionHandlers);
printStatistic(rt, " Infopoints", compilationResultInfopoints);
compilationResultInfopointStatistics.printStatistics(rt, identity());
printStatistic(rt, " Marks", compilationResultMarks);
printStatistic(rt, " Data references", compilationResultDataPatches);
if (TruffleCompilerOptions.getValue(TruffleCompilationStatisticDetails)) {
printStatistic(rt, "Truffle nodes");
nodeStatistics.printStatistics(rt, Class::getSimpleName);
printStatistic(rt, "Graal nodes after Truffle tier");
truffleTierNodeStatistics.printStatistics(rt, Class::getSimpleName);
printStatistic(rt, "Graal nodes after Graal tier");
graalTierNodeStatistics.printStatistics(rt, Class::getSimpleName);
}
}
private static void printStatistic(GraalTruffleRuntime rt, String label) {
rt.log(String.format(" %-50s: ", label));
}
private static void printStatistic(GraalTruffleRuntime rt, String label, int value) {
rt.log(String.format(" %-50s: %d", label, value));
}
private static void printStatistic(GraalTruffleRuntime rt, String label, double value) {
rt.log(String.format(" %-50s: %f", label, value));
}
private static void printStatistic(GraalTruffleRuntime rt, String label, IntSummaryStatistics value) {
rt.log(String.format(" %-50s: count=%4d, sum=%8d, min=%8d, average=%12.2f, max=%8d ", label, value.getCount(), value.getSum(), value.getMin(), value.getAverage(), value.getMax()));
}
private static void printStatisticTime(GraalTruffleRuntime rt, String label, LongSummaryStatistics value) {
rt.log(String.format(" %-50s: count=%4d, sum=%8d, min=%8d, average=%12.2f, max=%8d (milliseconds)", label, value.getCount(), value.getSum() / 1000000, value.getMin() / 1000000,
value.getAverage() / 1e6, value.getMax() / 1000000));
}
private static final class IdentityStatistics<T> {
final Map<T, IntSummaryStatistics> types = new HashMap<>();
public void printStatistics(GraalTruffleRuntime rt, Function<T, String> toStringFunction) {
SortedSet<T> sortedSet = new TreeSet<>(Comparator.comparing((T c) -> -types.get(c).getSum()));
sortedSet.addAll(types.keySet());
sortedSet.forEach(c -> {
printStatistic(rt, String.format(" %s", toStringFunction.apply(c)), types.get(c));
});
}
public void accept(Collection<T> elements) {
/* First compute the histogram. */
HashMap<T, Integer> histogram = new HashMap<>();
for (T e : elements) {
histogram.compute(e, (key, count) -> (count == null) ? 1 : count + 1);
}
/* Then create the summary statistics. */
for (Map.Entry<T, Integer> entry : histogram.entrySet()) {
T element = entry.getKey();
Integer count = entry.getValue();
types.computeIfAbsent(element, key -> new IntSummaryStatistics()).accept(count.intValue());
}
}
}
private static final class CallTargetNodeStatistics {
// nodeCount = truffleNodeCountTrivial + truffleNodeCountNonTrivial
private int nodeCountTrivial;
private int nodeCountNonTrivial;
private int nodeCountMonomorphic;
private int nodeCountPolymorphic;
private int nodeCountMegamorphic;
// callCount = truffleCallCountDirect + truffleCallCountIndirect
private int callCountIndirect;
// callCountDirect = truffleCallCountDirectDispatched + truffleCallCountDirectInlined
private int callCountDirectDispatched;
private int callCountDirectInlined;
private int callCountDirectCloned;
private int callCountDirectNotCloned;
private int loopCount;
CallTargetNodeStatistics(OptimizedCallTarget target, TruffleInlining inliningDecision) {
target.accept((CallTreeNodeVisitor) this::visitNode, inliningDecision);
}
private boolean visitNode(List<TruffleInlining> stack, Node node) {
if (node == null) {
return true;
}
NodeCost cost = node.getCost();
if (cost.isTrivial()) {
nodeCountTrivial++;
} else {
nodeCountNonTrivial++;
if (cost == NodeCost.MONOMORPHIC) {
nodeCountMonomorphic++;
} else if (cost == NodeCost.POLYMORPHIC) {
nodeCountPolymorphic++;
} else if (cost == NodeCost.MEGAMORPHIC) {
nodeCountMegamorphic++;
}
}
if (node instanceof DirectCallNode) {
TruffleInliningDecision decision = CallTreeNodeVisitor.getCurrentInliningDecision(stack);
if (decision != null && decision.getProfile().getCallNode() == node && decision.isInline()) {
callCountDirectInlined++;
} else {
callCountDirectDispatched++;
}
if (decision != null && decision.getProfile().getCallNode().isCallTargetCloned()) {
callCountDirectCloned++;
} else {
callCountDirectNotCloned++;
}
} else if (node instanceof IndirectCallNode) {
callCountIndirect++;
} else if (node instanceof LoopNode) {
loopCount++;
}
return true;
}
public int getCallCountDirectCloned() {
return callCountDirectCloned;
}
public int getCallCountDirectNotCloned() {
return callCountDirectNotCloned;
}
public int getNodeCount() {
return nodeCountTrivial + nodeCountNonTrivial;
}
public int getCallCount() {
return getCallCountDirect() + callCountIndirect;
}
public int getCallCountDirect() {
return callCountDirectDispatched + callCountDirectInlined;
}
public int getNodeCountTrivial() {
return nodeCountTrivial;
}
public int getNodeCountNonTrivial() {
return nodeCountNonTrivial;
}
public int getNodeCountMonomorphic() {
return nodeCountMonomorphic;
}
public int getNodeCountPolymorphic() {
return nodeCountPolymorphic;
}
public int getNodeCountMegamorphic() {
return nodeCountMegamorphic;
}
public int getCallCountIndirect() {
return callCountIndirect;
}
public int getCallCountDirectDispatched() {
return callCountDirectDispatched;
}
public int getCallCountDirectInlined() {
return callCountDirectInlined;
}
public int getLoopCount() {
return loopCount;
}
}
private static class CompilationLocal {
private long compilationStarted;
private long truffleTierFinished;
private long graalTierFinished;
}
}