/* * Copyright (c) 2015, 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 java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.IntSummaryStatistics; import java.util.List; import java.util.Map; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodes.BeginNode; import org.graalvm.compiler.nodes.DeoptimizeNode; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.VirtualState; import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin; import org.graalvm.compiler.nodes.java.MethodCallTargetNode; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; import org.graalvm.compiler.truffle.OptimizedCallTarget; import jdk.vm.ci.meta.ResolvedJavaMethod; public class HistogramInlineInvokePlugin implements InlineInvokePlugin { private final Map<ResolvedJavaMethod, MethodStatistics> histogram = new HashMap<>(); private final StructuredGraph graph; private HistogramInlineInvokePlugin.MethodStatistic currentStatistic; public HistogramInlineInvokePlugin(StructuredGraph graph) { this.graph = graph; } @Override public void notifyBeforeInline(ResolvedJavaMethod methodToInline) { currentStatistic = new MethodStatistic(currentStatistic, methodToInline, countNodes(), countCalls()); } @Override public void notifyAfterInline(ResolvedJavaMethod methodToInline) { assert methodToInline.equals(currentStatistic.method); currentStatistic.applyNodeCountAfter(countNodes()); currentStatistic.applyCallsAfter(countCalls()); accept(currentStatistic); currentStatistic = currentStatistic.getParent(); } private int countNodes() { return graph.getNodes().filter(node -> isNonTrivial(node)).count(); } private int countCalls() { return graph.getNodes(MethodCallTargetNode.TYPE).count(); } private static boolean isNonTrivial(Node node) { return !(node instanceof VirtualState || node instanceof VirtualObjectNode || node instanceof BeginNode || node instanceof DeoptimizeNode); } private void accept(MethodStatistic current) { ResolvedJavaMethod method = current.getMethod(); HistogramInlineInvokePlugin.MethodStatistics statistics = histogram.get(method); if (statistics == null) { statistics = new MethodStatistics(method); histogram.put(method, statistics); } statistics.accept(current); } public void print(OptimizedCallTarget target) { OptimizedCallTarget.log(String.format("Truffle expansion histogram for %s", target)); OptimizedCallTarget.log(" Invocations = Number of expanded invocations"); OptimizedCallTarget.log(" Nodes = Number of non-trival Graal nodes created for this method during partial evaluation."); OptimizedCallTarget.log(" Calls = Number of not expanded calls created for this method during partial evaluation."); OptimizedCallTarget.log(String.format(" %-11s |Nodes %5s %5s %5s %8s |Calls %5s %5s %5s %8s | Method Name", "Invocations", "Sum", "Min", "Max", "Avg", "Sum", "Min", "Max", "Avg")); /* First filter the statistics and collect them in a list. */ List<MethodStatistics> statisticsList = new ArrayList<>(); for (MethodStatistics statistics : histogram.values()) { if (statistics.shallowCount.getSum() > 0) { statisticsList.add(statistics); } } /* Then sort the list. */ Collections.sort(statisticsList); /* Finally print the filtered and sorted statistics. */ for (MethodStatistics statistics : statisticsList) { statistics.print(); } } private static class MethodStatistics implements Comparable<MethodStatistics> { private final ResolvedJavaMethod method; private int count; private final IntSummaryStatistics shallowCount = new IntSummaryStatistics(); private final IntSummaryStatistics callCount = new IntSummaryStatistics(); MethodStatistics(ResolvedJavaMethod method) { this.method = method; } public void print() { OptimizedCallTarget.log(String.format(" %11d | %5d %5d %5d %8.2f | %5d %5d %5d %8.2f | %s", // count, shallowCount.getSum(), shallowCount.getMin(), shallowCount.getMax(), // shallowCount.getAverage(), callCount.getSum(), callCount.getMin(), callCount.getMax(), // callCount.getAverage(), method.format("%h.%n(%p)"))); } @Override public int compareTo(MethodStatistics o) { int result = Long.compare(o.shallowCount.getSum(), shallowCount.getSum()); if (result == 0) { return Integer.compare(o.count, count); } return result; } public void accept(MethodStatistic statistic) { if (!statistic.method.equals(method)) { throw new IllegalArgumentException("invalid statistic"); } count++; callCount.accept(statistic.getShallowCallCount()); shallowCount.accept(statistic.getShallowNodeCount()); } } private static class MethodStatistic { private final MethodStatistic parent; private final List<MethodStatistic> children = new ArrayList<>(); private final ResolvedJavaMethod method; private int deepNodeCount; private int callCount; MethodStatistic(MethodStatistic parent, ResolvedJavaMethod method, int nodeCountBefore, int callsBefore) { this.parent = parent; this.method = method; this.callCount = callsBefore; this.deepNodeCount = nodeCountBefore; if (parent != null) { this.parent.getChildren().add(this); } } public ResolvedJavaMethod getMethod() { return method; } public List<MethodStatistic> getChildren() { return children; } public int getShallowNodeCount() { int shallowCount = deepNodeCount; for (MethodStatistic child : children) { shallowCount -= child.deepNodeCount; } return shallowCount; } public int getShallowCallCount() { int shallowCount = callCount; for (MethodStatistic child : children) { shallowCount -= child.callCount; } return shallowCount; } public void applyNodeCountAfter(int nodeCountAfter) { deepNodeCount = nodeCountAfter - this.deepNodeCount; } public void applyCallsAfter(int callsAfter) { callCount = callsAfter - this.callCount; } public MethodStatistic getParent() { return parent; } } }