package profiling; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.function.Supplier; public class Timer { private static final String TOP = "TOP"; private static final int RAWTOTAL = 0; private static final int RAWMAX = 1; private static final int NETTOTAL = 2; private static final int NETMAX = 3; private static final int COUNT = 4; public static <T> T record(String name,Supplier<T> fun){ Timer.activate(name);try{ return fun.get(); }finally{Timer.deactivate(name);} } public static List<Long> fakeTimes=new ArrayList<>();//for testing public static long giveTime(){ if(fakeTimes.isEmpty()){return System.currentTimeMillis();} long res=fakeTimes.get(0); fakeTimes=fakeTimes.subList(1, fakeTimes.size()); return res; } private static class TimerEntry{ String name;long time; boolean isOpen; TimerEntry(String name,long time, boolean isOpen){this.name=name;this.time=time; this.isOpen = isOpen;} } public static final List<TimerEntry>timers=new ArrayList<>(); public static void activate(String name){ assert timers.isEmpty() || !timers.get(timers.size()-1).name.equals("o_"+name); timers.add(new TimerEntry(name,giveTime(), true)); } public static void deactivate(String name){ timers.add(new TimerEntry(name,giveTime(), false)); } public static void restart(){timers.clear(); } public static String report(){ try{return _report();} catch(Throwable t){ t.printStackTrace(); return "Unable to profile, caused by "+t;} } private static String _report(){ if(timers.size()%2!=0){ Timer.deactivate("TOP"); } Map<String,long[]> processStatistic = new HashMap<String, long[]>(); timerTreeNode timerTree = new timerTreeNode(0, TOP); getTimersTree(timers.listIterator(), timerTree); if(timerTree.childs.isEmpty()) return "nothing to see\n"; if(timerTree.childs.get(0).name.equals(TOP)) timerTree = timerTree.childs.get(0); else{ timerTree.name = TOP; timerTree.startTime = timerTree.childs.get(0).startTime; timerTree.endTime = timerTree.childs.get(timerTree.childs.size()-1).endTime; } try { timerTree.calculateTimes(processStatistic, new ArrayList<String>()); } catch (Exception e) { e.printStackTrace(); return "something went wrong\n"; } String result="\n*******************************\n"; List<String>names=new ArrayList<>(processStatistic.keySet()); names.sort((n1,n2)->(int)(processStatistic.get(n2)[RAWTOTAL]-processStatistic.get(n1)[RAWTOTAL])); long totTop = timerTree.total(); for(String name:names){ long[] stats =processStatistic.get(name); result+="# "+name+"\n"; result+=String.format("percentage:%.2f/%.2f tot:%.3f/%.3f max:%.3f/%.3f number:%d", (stats[RAWTOTAL]/(float)totTop), (stats[NETTOTAL]/(float)totTop), stats[RAWTOTAL]/60000f, stats[NETTOTAL]/60000f, stats[RAWMAX]/60000f, stats[NETMAX]/60000f, (int)stats[COUNT]) +"\n"; } return result+"*************************************\n"; } private static long getTimersTree(ListIterator<TimerEntry> timerIterator, timerTreeNode parent){ while(true){ if(!timerIterator.hasNext()) return 0; TimerEntry current = timerIterator.next(); long time = current.time; String name = current.name; if(!current.isOpen) return time; timerTreeNode self = new timerTreeNode(time, name); parent.addChild(self); long endTime = getTimersTree(timerIterator, self); self.endTime = endTime; } } protected static class timerTreeNode{ long startTime; long endTime; String name; List<timerTreeNode> childs; timerTreeNode(long startTime, String name) { super(); this.startTime = startTime; this.endTime = 0; this.name = name; this.childs = new ArrayList<>(); } void addChild(timerTreeNode child){ this.childs.add(child); } long total(){ return endTime-startTime; } void calculateTimes(Map<String, long[]> outputList, List<String> elders) throws Exception{ long netNodeTime = this.total(); boolean isAncestor = true; if(elders.contains(this.name)) isAncestor = false; else elders.add(this.name); for(timerTreeNode child:this.childs){ child.calculateTimes(outputList, elders); netNodeTime -= child.total(); } if(isAncestor) elders.remove(this.name); if(netNodeTime<0) throw new Exception("unreliable tree"); long newNode[] = new long[5]; if(outputList.containsKey(this.name)){ newNode[RAWTOTAL] = outputList.get(this.name)[RAWTOTAL]; newNode[RAWMAX] = outputList.get(this.name)[RAWMAX]; newNode[NETTOTAL] = outputList.get(this.name)[NETTOTAL]; newNode[COUNT] = outputList.get(this.name)[COUNT]; } if(isAncestor) newNode[RAWTOTAL] += this.total(); if(newNode[RAWMAX]<this.total()) newNode[RAWMAX] = this.total(); if(newNode[NETMAX]<netNodeTime) newNode[NETMAX] = netNodeTime; newNode[NETTOTAL] += netNodeTime; newNode[COUNT] += 1; outputList.put(this.name, newNode); } } }