@@ -103,6 +103,67 @@ private static record TickInformation(
103103 long tickTimeCPU
104104 ) {}
105105
106+ public Double getTPSAverage (final TickTime inProgress , final long tickInterval ) {
107+ return this .getTPSAverage (inProgress , tickInterval , false );
108+ }
109+
110+ public Double getTPSAverage (final TickTime inProgress , final long tickInterval , final boolean createFakeTick ) {
111+ if (this .timeData .isEmpty () && inProgress == null ) {
112+ return null ;
113+ }
114+
115+ final List <TickTime > allData = new ArrayList <>(this .timeData );
116+ if (inProgress != null ) {
117+ allData .add (inProgress );
118+ }
119+
120+ final List <TickInformation > collapsedData = collapseData (allData , tickInterval , createFakeTick );
121+ if (collapsedData .isEmpty ()) {
122+ return null ;
123+ }
124+
125+ long totalTimeBetweenTicks = 0L ;
126+ int collectedTicks = 0 ;
127+ for (final TickInformation time : collapsedData ) {
128+ totalTimeBetweenTicks += time .differenceFromLastTick ();
129+ ++collectedTicks ;
130+ }
131+ return (double )collectedTicks / ((double )totalTimeBetweenTicks / 1.0E9 );
132+ }
133+
134+ public record MSPTData (double avg , long [] rawData ) {}
135+
136+ public MSPTData getMSPTData (final TickTime inProgress , final long tickInterval ) {
137+ return this .getMSPTData (inProgress , tickInterval , false );
138+ }
139+
140+ public MSPTData getMSPTData (final TickTime inProgress , final long tickInterval , final boolean createFakeTick ) {
141+ if (this .timeData .isEmpty () && inProgress == null ) {
142+ return null ;
143+ }
144+
145+ final List <TickTime > allData = new ArrayList <>(this .timeData );
146+ if (inProgress != null ) {
147+ allData .add (inProgress );
148+ }
149+
150+ final List <TickInformation > collapsedData = collapseData (allData , tickInterval , createFakeTick );
151+ if (collapsedData .isEmpty ()) {
152+ return null ;
153+ }
154+
155+ long totalTimeTicking = 0L ;
156+ int collectedTicks = 0 ;
157+ final long [] timePerTickDataRaw = new long [collapsedData .size ()];
158+ for (int i = 0 ; i < collapsedData .size (); ++i ) {
159+ final TickInformation time = collapsedData .get (i );
160+ totalTimeTicking += time .tickTime ();
161+ timePerTickDataRaw [i ] = time .tickTime ();
162+ ++collectedTicks ;
163+ }
164+ return new MSPTData ((double )totalTimeTicking / (double )collectedTicks * 1.0E-6 , timePerTickDataRaw );
165+ }
166+
106167 public TickReportData generateTickReport (final TickTime inProgress , final long endTime , final long tickInterval ) {
107168 return this .generateTickReport (inProgress , endTime , tickInterval , false );
108169 }
@@ -118,6 +179,11 @@ public TickReportData generateTickReport(final TickTime inProgress, final long e
118179 allData .add (inProgress );
119180 }
120181
182+ final List <TickInformation > collapsedData = collapseData (allData , tickInterval , createFakeTick );
183+ if (collapsedData .isEmpty ()) {
184+ return null ;
185+ }
186+
121187 final long intervalStart = allData .get (0 ).tickStart ();
122188 final long intervalEnd = allData .get (allData .size () - 1 ).tickEnd ();
123189
@@ -139,90 +205,6 @@ public TickReportData generateTickReport(final TickTime inProgress, final long e
139205 }
140206 }
141207
142- // we only care about ticks, but because of inbetween tick task execution
143- // there will be data in allData that isn't ticks. But, that data cannot
144- // be ignored since it contributes to utilisation.
145- // So, we will "compact" the data by merging any inbetween tick times
146- // the next tick.
147- // If there is no "next tick", then we will create one.
148- final List <TickInformation > collapsedData = new ArrayList <>();
149- for (int i = 0 , len = allData .size (); i < len ; ++i ) {
150- final List <TickTime > toCollapse = new ArrayList <>();
151- TickTime lastTick = null ;
152- for (;i < len ; ++i ) {
153- final TickTime time = allData .get (i );
154- if (!time .isTickExecution ()) {
155- toCollapse .add (time );
156- continue ;
157- }
158- lastTick = time ;
159- break ;
160- }
161-
162- if (toCollapse .isEmpty ()) {
163- // nothing to collapse
164- final TickTime last = allData .get (i );
165- collapsedData .add (
166- new TickInformation (
167- last .differenceFromLastTick (tickInterval ),
168- last .tickLength (),
169- last .supportCPUTime () ? last .tickCpuTime () : 0L
170- )
171- );
172- } else {
173- long totalTickTime = 0L ;
174- long totalCpuTime = 0L ;
175- for (int k = 0 , len2 = toCollapse .size (); k < len2 ; ++k ) {
176- final TickTime time = toCollapse .get (k );
177- totalTickTime += time .tickLength ();
178- totalCpuTime += time .supportCPUTime () ? time .tickCpuTime () : 0L ;
179- }
180- if (i < len ) {
181- // we know there is a tick to collapse into
182- final TickTime last = allData .get (i );
183- collapsedData .add (
184- new TickInformation (
185- last .differenceFromLastTick (tickInterval ),
186- last .tickLength () + totalTickTime ,
187- (last .supportCPUTime () ? last .tickCpuTime () : 0L ) + totalCpuTime
188- )
189- );
190- } else if (createFakeTick ) {
191- // we do not have a tick to collapse into, so we must make one up
192- // we will assume that the tick is "starting now" and ongoing
193-
194- // compute difference between imaginary tick and last tick
195- final long differenceBetweenTicks ;
196- if (lastTick != null ) {
197- // we have a last tick, use it
198- differenceBetweenTicks = lastTick .tickStart ();
199- } else {
200- // we don't have a last tick, so we must make one up that makes sense
201- // if the current interval exceeds the max tick time, then use it
202-
203- // Otherwise use the interval length.
204- // This is how differenceFromLastTick() works on TickTime when there is no previous interval.
205- differenceBetweenTicks = Math .max (
206- tickInterval , totalTickTime
207- );
208- }
209-
210- collapsedData .add (
211- new TickInformation (
212- differenceBetweenTicks ,
213- totalTickTime ,
214- totalCpuTime
215- )
216- );
217- }
218- }
219- }
220-
221-
222- if (collapsedData .isEmpty ()) {
223- return null ;
224- }
225-
226208 final int collectedTicks = collapsedData .size ();
227209 final long [] tickStartToStartDifferences = new long [collectedTicks ];
228210 final long [] timePerTickDataRaw = new long [collectedTicks ];
@@ -303,6 +285,88 @@ public TickReportData generateTickReport(final TickTime inProgress, final long e
303285 );
304286 }
305287
288+ private static List <TickInformation > collapseData (final List <TickTime > allData , final long tickInterval , final boolean createFakeTick ) {
289+ // we only care about ticks, but because of inbetween tick task execution
290+ // there will be data in allData that isn't ticks. But, that data cannot
291+ // be ignored since it contributes to utilisation.
292+ // So, we will "compact" the data by merging any inbetween tick times
293+ // the next tick.
294+ // If there is no "next tick", then we will create one.
295+ final List <TickInformation > collapsedData = new ArrayList <>();
296+ for (int i = 0 , len = allData .size (); i < len ; ++i ) {
297+ final List <TickTime > toCollapse = new ArrayList <>();
298+ TickTime lastTick = null ;
299+ for (;i < len ; ++i ) {
300+ final TickTime time = allData .get (i );
301+ if (!time .isTickExecution ()) {
302+ toCollapse .add (time );
303+ continue ;
304+ }
305+ lastTick = time ;
306+ break ;
307+ }
308+
309+ if (toCollapse .isEmpty ()) {
310+ // nothing to collapse
311+ final TickTime last = allData .get (i );
312+ collapsedData .add (
313+ new TickInformation (
314+ last .differenceFromLastTick (tickInterval ),
315+ last .tickLength (),
316+ last .supportCPUTime () ? last .tickCpuTime () : 0L
317+ )
318+ );
319+ } else {
320+ long totalTickTime = 0L ;
321+ long totalCpuTime = 0L ;
322+ for (int k = 0 , len2 = toCollapse .size (); k < len2 ; ++k ) {
323+ final TickTime time = toCollapse .get (k );
324+ totalTickTime += time .tickLength ();
325+ totalCpuTime += time .supportCPUTime () ? time .tickCpuTime () : 0L ;
326+ }
327+ if (i < len ) {
328+ // we know there is a tick to collapse into
329+ final TickTime last = allData .get (i );
330+ collapsedData .add (
331+ new TickInformation (
332+ last .differenceFromLastTick (tickInterval ),
333+ last .tickLength () + totalTickTime ,
334+ (last .supportCPUTime () ? last .tickCpuTime () : 0L ) + totalCpuTime
335+ )
336+ );
337+ } else if (createFakeTick ) {
338+ // we do not have a tick to collapse into, so we must make one up
339+ // we will assume that the tick is "starting now" and ongoing
340+
341+ // compute difference between imaginary tick and last tick
342+ final long differenceBetweenTicks ;
343+ if (lastTick != null ) {
344+ // we have a last tick, use it
345+ differenceBetweenTicks = lastTick .tickStart ();
346+ } else {
347+ // we don't have a last tick, so we must make one up that makes sense
348+ // if the current interval exceeds the max tick time, then use it
349+
350+ // Otherwise use the interval length.
351+ // This is how differenceFromLastTick() works on TickTime when there is no previous interval.
352+ differenceBetweenTicks = Math .max (
353+ tickInterval , totalTickTime
354+ );
355+ }
356+
357+ collapsedData .add (
358+ new TickInformation (
359+ differenceBetweenTicks ,
360+ totalTickTime ,
361+ totalCpuTime
362+ )
363+ );
364+ }
365+ }
366+ }
367+ return collapsedData ;
368+ }
369+
306370 public static final record TickReportData (
307371 int collectedTicks ,
308372 long collectedTickIntervalStart ,
0 commit comments