1414 */
1515package net .rptools .lib ;
1616
17+ import java .awt .EventQueue ;
1718import java .util .ArrayList ;
1819import java .util .LinkedHashMap ;
1920import java .util .List ;
2021import java .util .Map ;
2122import java .util .concurrent .TimeUnit ;
2223import net .rptools .maptool .client .AppState ;
2324import net .rptools .maptool .client .MapTool ;
25+ import org .apache .logging .log4j .LogManager ;
26+ import org .apache .logging .log4j .Logger ;
2427
2528public class CodeTimer {
2629 private static final ThreadLocal <CodeTimer > ROOT_TIMER =
2730 ThreadLocal .withInitial (() -> new CodeTimer ("" ));
2831 private static final ThreadLocal <List <CodeTimer >> timerStack =
2932 ThreadLocal .withInitial (ArrayList ::new );
33+ private static final Logger log = LogManager .getLogger (CodeTimer .class );
3034
3135 @ FunctionalInterface
3236 public interface TimedSection <Ex extends Throwable > {
@@ -50,11 +54,7 @@ public static <Ex extends Exception> void using(String name, TimedSection<Ex> ca
5054 final var lastTimer = stack .removeLast ();
5155 assert lastTimer == timer : "Timer stack is corrupted" ;
5256
53- if (timer .isEnabled ()) {
54- String results = timer .toString ();
55- MapTool .getProfilingNoteFrame ().addText (results );
56- }
57- timer .clear ();
57+ timer .complete ();
5858 }
5959 }
6060
@@ -63,8 +63,18 @@ public static CodeTimer get() {
6363 return stack .isEmpty () ? ROOT_TIMER .get () : stack .getLast ();
6464 }
6565
66- private final Map <String , Integer > counterMap = new LinkedHashMap <>();
67- private final Map <String , Timer > timeMap = new LinkedHashMap <>();
66+ private enum TimerEventType {
67+ Start ,
68+ Stop
69+ }
70+
71+ private record TimerEvent (TimerEventType type , long timeNs , String id , Object [] parameters ) {}
72+
73+ private record CounterEvent (long amount , String id ) {}
74+
75+ private ArrayList <TimerEvent > timerEvents = new ArrayList <>(100 );
76+ private ArrayList <CounterEvent > counterEvents = new ArrayList <>(100 );
77+
6878 private final String name ;
6979 private long threshold = 1 ;
7080 private TimeUnit reportingUnit = TimeUnit .MILLISECONDS ;
@@ -75,10 +85,6 @@ private CodeTimer(String n) {
7585 enabled = true ;
7686 }
7787
78- public boolean isEnabled () {
79- return enabled ;
80- }
81-
8288 public void setThreshold (long threshold ) {
8389 this .threshold = TimeUnit .NANOSECONDS .convert (threshold , TimeUnit .MILLISECONDS );
8490 }
@@ -103,58 +109,81 @@ public void increment(String id, int amount) {
103109 if (!enabled ) {
104110 return ;
105111 }
106- counterMap . merge ( id , amount , Integer :: sum );
112+ counterEvents . add ( new CounterEvent ( amount , id ) );
107113 }
108114
109115 public void start (String id , Object ... parameters ) {
110116 if (!enabled ) {
111117 return ;
112118 }
113- if (parameters .length > 0 ) {
114- id = String .format (id , parameters );
115- }
116-
117- Timer timer = timeMap .computeIfAbsent (id , key -> new Timer ());
118- timer .start ();
119+ timerEvents .add (new TimerEvent (TimerEventType .Start , System .nanoTime (), id , parameters ));
119120 }
120121
121122 public void stop (String id , Object ... parameters ) {
122123 if (!enabled ) {
123124 return ;
124125 }
125- if (parameters .length > 0 ) {
126- id = String .format (id , parameters );
126+ timerEvents .add (new TimerEvent (TimerEventType .Stop , System .nanoTime (), id , parameters ));
127+ }
128+
129+ private void complete () {
130+ if (!enabled ) {
131+ return ;
127132 }
128133
129- Timer timer = timeMap .get (id );
130- if (timer == null ) {
131- throw new IllegalArgumentException ("Could not find timer id: " + id );
134+ var name = this .name ;
135+ var reportingUnit = this .reportingUnit ;
136+ var threshold = this .threshold ;
137+ var timerEvents = this .timerEvents ;
138+ var counterEvents = this .counterEvents ;
139+ this .timerEvents = new ArrayList <>();
140+ this .counterEvents = new ArrayList <>();
141+
142+ EventQueue .invokeLater (
143+ () -> {
144+ String results = getResults (name , reportingUnit , threshold , timerEvents , counterEvents );
145+ MapTool .getProfilingNoteFrame ().addText (results );
146+ });
147+ }
148+
149+ private static String getResults (
150+ String name ,
151+ TimeUnit reportingUnit ,
152+ long threshold ,
153+ List <TimerEvent > timerEvents ,
154+ List <CounterEvent > counterEvents ) {
155+ final Map <String , Long > counterMap = new LinkedHashMap <>();
156+ // Maps timer names to start times.
157+ final Map <String , Timer > timeMap = new LinkedHashMap <>();
158+
159+ // Count up all the timing events.
160+ for (var event : timerEvents ) {
161+ var id = String .format (event .id (), event .parameters ());
162+ var timer = timeMap .computeIfAbsent (id , Timer ::new );
163+ switch (event .type ()) {
164+ case Start -> timer .startAt (event .timeNs ());
165+ case Stop -> timer .stopAt (event .timeNs ());
166+ }
132167 }
133- timer .stop ();
134- }
135168
136- public void clear () {
137- timeMap . clear ();
138- counterMap .clear ( );
139- }
169+ // Count up all the counter events.
170+ for ( var event : counterEvents ) {
171+ counterMap .merge ( event . id (), event . amount (), Long :: sum );
172+ }
140173
141- @ Override
142- public String toString () {
143174 StringBuilder builder = new StringBuilder (100 );
144-
145175 builder
146176 .append ("Timer " )
147177 .append (name )
148178 .append (" (" )
149179 .append (timeMap .size ())
150180 .append (" elements)\n " );
151-
152181 var i = -1 ;
153182 for (var entry : timeMap .entrySet ()) {
154183 ++i ;
155184
156185 var id = entry .getKey ();
157- long elapsed = entry .getValue ().getElapsed () ;
186+ long elapsed = entry .getValue ().elapsedNs ;
158187 if (elapsed < threshold ) {
159188 continue ;
160189 }
@@ -184,29 +213,29 @@ public String toString() {
184213 return builder .toString ();
185214 }
186215
187- private static class Timer {
188- long elapsed ;
189- long start = -1 ;
216+ private static final class Timer {
217+ String name ;
218+ long elapsedNs = 0 ;
219+ long startTimeNs = -1 ;
190220
191- private long getTime ( ) {
192- return System . nanoTime () ;
221+ public Timer ( String name ) {
222+ this . name = name ;
193223 }
194224
195- public void start () {
196- start = getTime ();
197- }
198-
199- public void stop () {
200- elapsed += (getTime () - start );
201- start = -1 ;
225+ public void startAt (long timeNs ) {
226+ if (startTimeNs >= 0 ) {
227+ log .warn ("Invalid timer state: attempted to start timer {} that was already started" , name );
228+ }
229+ startTimeNs = timeNs ;
202230 }
203231
204- public long getElapsed ( ) {
205- long time = elapsed ;
206- if ( start > 0 ) {
207- time += ( getTime () - start ) ;
232+ public void stopAt ( long timeNs ) {
233+ if ( startTimeNs < 0 ) {
234+ log . warn ( "Invalid timer state: attempted to stop timer {} that was not started" , name );
235+ return ;
208236 }
209- return time ;
237+ elapsedNs += (timeNs - startTimeNs );
238+ startTimeNs = -1 ;
210239 }
211240 }
212241}
0 commit comments