/* * Copyright 2003-2011 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.util.performance; import org.jetbrains.annotations.NotNull; import java.util.*; /** * Evgeny Gryaznov, Feb 23, 2010 */ public class PerformanceTracer implements IPerformanceTracer { public static final boolean IS_TRACING = true; private int top = 0; private StackElement[] myStack = new StackElement[4096]; private String traceName; private List<String> externalText; public PerformanceTracer(String name) { this.traceName = name; for (int i = 0; i < myStack.length; i++) { myStack[i] = new StackElement(); } for (int i = 0; i < precreated.length; i++) { precreated[i] = new Task("default"); } myStack[0].name = "Zero [" + name + "]"; myStack[0].task = new Task(myStack[0].name); externalText = new ArrayList<String>(); } @Override public void push(String taskName, boolean isMajor) { top++; myStack[top].isMajor = isMajor; myStack[top].name = isMajor ? "[" + taskName + "]" : taskName; myStack[top].correction = 0; myStack[top].startTime = System.nanoTime(); } @Override public void pop() { long len = System.nanoTime() - myStack[top].startTime; long correction = myStack[top].correction; if (myStack[top].isMajor) { for (int i = 0; i < top; i++) { myStack[i].correction += len - correction; } } String name = myStack[top].name; Task wasTask = myStack[top].task; // propagate leaves if (!myStack[top].children.isEmpty()) { if (wasTask == null) { wasTask = getTask(top); } wasTask.tasks.addAll(myStack[top].children.values()); myStack[top].children.clear(); } if (wasTask == null) { myStack[top - 1].addLeaf(name, len, correction); } else { wasTask.executionTime += len; wasTask.correction += correction; } myStack[top].task = null; top--; } private Task getTask(int i) { Task t = myStack[i].task; if (t == null) { t = createFast(myStack[i].name); myStack[i].task = t; Task parent = getTask(i - 1); parent.addTask(t); } return t; } @Override public void addText(String s) { externalText.add(s); } @Override public String report(String... separate) { if (top == 0) { myStack[0].task.tasks.addAll(myStack[0].children.values()); myStack[0].task.merge(new HashSet<>(Arrays.asList(separate))); StringBuilder sb = new StringBuilder(); sb.append("["); sb.append(traceName); sb.append("]\n"); myStack[0].task.toString(sb, 0); for (String s : externalText) { sb.append(s); sb.append("\n"); } return sb.toString(); } else { return null; } } private class StackElement { String name; long startTime; long correction; Task task; boolean isMajor; Map<String, Task> children = new HashMap<String, Task>(); private Task addLeaf(String name, long time, long correction) { Task t = children.get(name); if (t == null) { t = createFast(name); children.put(name, t); } else { t.invocationCount++; } t.executionTime += time; t.correction += correction; return t; } } private static class Task implements Comparable<Task> { String name; long executionTime; long correction; int invocationCount; List<Task> tasks = new LinkedList<Task>(); private Task(String name) { this.name = name; this.executionTime = 0; this.invocationCount = 1; } private void addTask(Task task) { tasks.add(task); } public void merge(Set<String> keepUnmerged) { Map<String, Task> map = new HashMap<String, Task>(); Iterator<Task> it = tasks.iterator(); while (it.hasNext()) { Task n = it.next(); if (keepUnmerged.contains(n.name)) { continue; } Task prev = map.get(n.name); if (prev == null) { map.put(n.name, n); } else { it.remove(); prev.mergeWith(n); } } for (Task t : tasks) { t.merge(keepUnmerged); } } private void mergeWith(Task n) { executionTime += n.executionTime; invocationCount += n.invocationCount; correction += n.correction; tasks.addAll(n.tasks); } public void toString(StringBuilder sb, int indent) { if (name != null) { for (int i = 0; i < indent; i++) { sb.append(" "); } sb.append(name); sb.append(':'); sb.append(invocationCount); sb.append(": "); sb.append(executionTime / 1000000.); sb.append(" ms"); if (correction != 0) { sb.append(" (real: "); sb.append((executionTime - correction) / 1000000.); sb.append(" ms)"); } sb.append("\n"); } Collections.sort(tasks); for (Task t : tasks) { t.toString(sb, name == null ? indent : indent + 2); } } @Override public int compareTo(@NotNull Task task) { return Long.valueOf(task.executionTime).compareTo(executionTime); } } private Task createFast(String name) { if (precreatedIndex < precreated.length) { Task t = precreated[precreatedIndex++]; t.name = name; return t; } else { return new Task(name); } } private Task[] precreated = new Task[4096]; // todo: AP what is that? int precreatedIndex = 0; }