package de.axone.test; import java.util.Formatter; import java.util.LinkedHashMap; import java.util.Map; import org.slf4j.Logger; import de.axone.tools.E; import de.axone.tools.Text; /** * Helper class to record timing * * This class is thread-safe * * Usage * <pre> * Timing t = new Timing(); * t.start(); // Must be done * t.start( "Timing1" ); // Named timing * t.stop( "Timing1" ); // cannot cascade * t.start( "Timing2" ); * t.stop( "Timing2" ); * t.stop(); // Must be done * print( t.summary() ); * </pre> * * @author flo */ public class Timing { public static final String TOTAL = "Timing TOTAL Timer"; // Preserve first call order private final Map<Object, Timer> timers = new LinkedHashMap<Object,Timer>(); private final Logger logger; public Timing(){ this.logger = null; } public Timing( Logger logger ){ this.logger = logger; } public synchronized void start(){ start( TOTAL ); } // TODO: Da was rein mit AutoCloseable waere nett! // Z.B. handler = start(...) ... handler.close public synchronized void start( Object name ){ if( logger != null && logger.isDebugEnabled() ){ logger.debug( "Start: " + name ); } Timer t = timers.get( name ); if( t == null ){ t = new Timer(); timers.put( name, t ); } if( t.start != 0 ) throw new IllegalStateException( "Timer " + name + " was already running" ); t.start = System.currentTimeMillis(); } public synchronized void stop(){ stop( TOTAL ); } public synchronized void stop( Object name ){ // remember time here because this is more exact long time = System.currentTimeMillis(); Timer t = timers.get( name ); if( t == null ) throw new IllegalStateException( "Cannot find timer: " + name ); if( t.start == 0 ) throw new IllegalStateException( "Timer " + name + " was not running" ); long diff = time-t.start; t.sum += diff; t.start = 0; if( logger != null && logger.isTraceEnabled() ){ logger.trace( "Stop {} in {} ms", ""+name, diff ); } } public synchronized long time( Object name ){ Timer t = timers.get( name ); if( t == null ) throw new IllegalStateException( "Cannot find timer: " + name ); return t.sum; } public synchronized void reset( Object name ){ Timer t = timers.get( name ); if( t == null ) throw new IllegalStateException( "Cannot find timer: " + name ); t.start = t.sum = 0; } public synchronized void clear(){ timers.clear(); } public synchronized String summary(){ long grand=0; Timer total = timers.remove( TOTAL ); for( Object key : timers.keySet() ){ Timer t = timers.get( key ); grand += t.sum; if( t.start != 0 ) throw new IllegalStateException( "You forgot to stop timer: " + key ); } StringBuilder result = new StringBuilder(); @SuppressWarnings( "resource" ) Formatter formatter = new Formatter( result ); result.append( "Timing:\n" ); for( Object name : timers.keySet() ){ Timer t = timers.get( name ); result.append( Text.left( name.toString(), 30 ) ); formatter.format( "% 5dms (%4.1f%%/%4.1f%%)\n", t.sum, 100.0*t.sum/grand, 100.0*t.sum/total.sum ); } result.append( Text.line( '=', 50 ) ).append( '\n' ); result.append( Text.left( "Sum", 30 ) ); formatter.format( "% 5dms (%4.1f%%)\n", grand, 100.0*grand/total.sum ); result.append( Text.left( "Diff", 30 ) ); formatter.format( "% 5dms\n", total.sum - grand ); result.append( Text.line( '-', 50 ) ).append( '\n' ); result.append( Text.left( "Total", 30 ) ); formatter.format( "% 5dms\n", total.sum ); return result.toString(); } private static class Timer { long start=0, sum=0; } public static void main( String [] args ) throws Exception { Timing t = new Timing(); t.start(); Thread.sleep( 100 ); t.start( "A" ); Thread.sleep( 100 ); t.stop( "A" ); t.start( "B" ); Thread.sleep( 200 ); t.stop( "B" ); Thread.sleep( 100 ); t.stop(); E.cho( t.summary() ); } }