2222import org .bukkit .util .Vector ;
2323
2424import java .io .IOException ;
25+ import java .io .UncheckedIOException ;
2526import java .nio .file .Files ;
2627import java .nio .file .Path ;
28+ import java .nio .file .StandardOpenOption ;
2729import java .util .ArrayList ;
2830import java .util .Arrays ;
2931import java .util .HashMap ;
@@ -50,6 +52,8 @@ public final class DroppedItemBenchmarkPlugin extends JavaPlugin {
5052 private static volatile long blackhole ;
5153
5254 private final List <String > results = new ArrayList <>();
55+ private Path resultsOutput ;
56+ private Path completionOutput ;
5357
5458 @ Override
5559 public void onEnable () {
@@ -69,6 +73,11 @@ private void runBenchmarks() throws IOException {
6973 World world = Bukkit .getWorlds ().getFirst ();
7074 world .setAutoSave (false );
7175 world .getEntitiesByClass (Item .class ).forEach (Item ::remove );
76+ Files .createDirectories (getDataFolder ().toPath ());
77+ resultsOutput = getDataFolder ().toPath ().resolve ("benchmark-results.jsonl" );
78+ completionOutput = getDataFolder ().toPath ().resolve ("benchmark-complete.txt" );
79+ Files .writeString (resultsOutput , "" );
80+ Files .deleteIfExists (completionOutput );
7281
7382 for (int itemCount : List .of (250 , 1000 , 2500 )) {
7483 benchmarkCramping (world , itemCount , false );
@@ -87,10 +96,8 @@ private void runBenchmarks() throws IOException {
8796 reportTokenBucket (pending , 128 , 32 );
8897 }
8998
90- Files .createDirectories (getDataFolder ().toPath ());
91- Path output = getDataFolder ().toPath ().resolve ("benchmark-results.jsonl" );
92- Files .write (output , results );
93- getLogger ().info ("Wrote " + results .size () + " A/B results to " + output .toAbsolutePath ());
99+ Files .writeString (completionOutput , Integer .toString (results .size ()));
100+ getLogger ().info ("Wrote " + results .size () + " A/B results to " + resultsOutput .toAbsolutePath ());
94101 }
95102
96103 private void benchmarkCramping (World world , int itemCount , boolean clustered ) {
@@ -198,20 +205,11 @@ private void benchmarkVisibility(int itemCount, int viewerCount, String distribu
198205 throw new IllegalStateException ("Visibility A/B mismatch: linear=" + expectedLabels
199206 + ", legacy=" + legacyLabels + ", candidate=" + candidateLabels );
200207 }
201- Comparison productionComparison ;
202- Comparison referenceComparison ;
203- if (((itemCount + viewerCount + seed ) & 1 ) == 0 ) {
204- productionComparison = compare (legacyProduction , candidate );
205- referenceComparison = compare (linearReference , candidate );
206- } else {
207- referenceComparison = compare (linearReference , candidate );
208- productionComparison = compare (legacyProduction , candidate );
209- }
208+ boolean baselineStarts = ((itemCount * 31L + viewerCount * 17L + seed ) & 1L ) == 0L ;
209+ Comparison productionComparison = compare (legacyProduction , candidate , baselineStarts );
210210 double reduction = itemCount == 0 ? 0.0D : 100.0D * (itemCount - expectedLabels ) / itemCount ;
211211 reportVisibilityComparison ("visibility-production-ab" , "legacy-production-viewer-grid" , true ,
212212 distribution , seed , itemCount , viewerCount , productionComparison , expectedLabels , reduction );
213- reportVisibilityComparison ("visibility-linear-reference" , "linear-viewer-snapshot-any" , false ,
214- distribution , seed , itemCount , viewerCount , referenceComparison , expectedLabels , reduction );
215213 }
216214
217215 private void reportVisibilityComparison (String benchmark , String baseline , boolean baselineUsesGrid ,
@@ -224,16 +222,35 @@ private void reportVisibilityComparison(String benchmark, String baseline, boole
224222 "\" candidate\" :\" production-primitive-viewer-index\" ," +
225223 "\" candidateStorage\" :\" primitive-soa\" ," +
226224 "\" indexRebuiltPerOperation\" :true,\" candidateUsesGrid\" :false," +
227- "\" sampleTargetNs\" :%d,\" measurementRounds\" :%d," +
225+ "\" sampleTargetNs\" :%d,\" warmupRounds\" :%d,\" measurementRounds\" :%d," +
226+ "\" roundOrder\" :\" %s\" ," +
228227 "\" baselineMedianNs\" :%d,\" baselineP95Ns\" :%d," +
229228 "\" candidateMedianNs\" :%d,\" candidateP95Ns\" :%d,\" speedup\" :%.3f," +
229+ "\" baselineFirstBaselineMedianNs\" :%d," +
230+ "\" baselineFirstCandidateMedianNs\" :%d," +
231+ "\" baselineFirstBaselineP95Ns\" :%d," +
232+ "\" baselineFirstCandidateP95Ns\" :%d," +
233+ "\" baselineFirstSpeedup\" :%.3f," +
234+ "\" candidateFirstBaselineMedianNs\" :%d," +
235+ "\" candidateFirstCandidateMedianNs\" :%d," +
236+ "\" candidateFirstBaselineP95Ns\" :%d," +
237+ "\" candidateFirstCandidateP95Ns\" :%d," +
238+ "\" candidateFirstSpeedup\" :%.3f," +
230239 "\" baselineRoundsNs\" :%s,\" candidateRoundsNs\" :%s," +
231240 "\" allLabels\" :%d,\" activeLabels\" :%d," +
232241 "\" labelReductionPct\" :%.3f}" ,
233242 benchmark , distribution , seed , itemCount , viewerCount , VIEW_DISTANCE , baseline , baselineUsesGrid ,
234- SAMPLE_TARGET_NANOS , MEASUREMENT_ROUNDS ,
243+ SAMPLE_TARGET_NANOS , WARMUP_ROUNDS , MEASUREMENT_ROUNDS , comparison . roundOrder () ,
235244 comparison .baseline ().median (), comparison .baseline ().p95 (),
236245 comparison .candidate ().median (), comparison .candidate ().p95 (), comparison .speedup (),
246+ comparison .baselineFirst ().baseline ().median (),
247+ comparison .baselineFirst ().candidate ().median (),
248+ comparison .baselineFirst ().baseline ().p95 (), comparison .baselineFirst ().candidate ().p95 (),
249+ comparison .baselineFirst ().speedup (),
250+ comparison .candidateFirst ().baseline ().median (),
251+ comparison .candidateFirst ().candidate ().median (),
252+ comparison .candidateFirst ().baseline ().p95 (), comparison .candidateFirst ().candidate ().p95 (),
253+ comparison .candidateFirst ().speedup (),
237254 Arrays .toString (comparison .baselineRoundsNs ()), Arrays .toString (comparison .candidateRoundsNs ()),
238255 itemCount , activeLabels , reduction );
239256 report (result );
@@ -315,8 +332,13 @@ private void reportTokenBucket(int pending, int capacity, int refill) {
315332 }
316333
317334 private static Comparison compare (LongSupplier baseline , LongSupplier candidate ) {
335+ return compare (baseline , candidate , true );
336+ }
337+
338+ private static Comparison compare (LongSupplier baseline , LongSupplier candidate , boolean baselineStarts ) {
318339 for (int round = 0 ; round < WARMUP_ROUNDS ; round ++) {
319- if ((round & 1 ) == 0 ) {
340+ boolean baselineFirst = ((round & 1 ) == 0 ) == baselineStarts ;
341+ if (baselineFirst ) {
320342 time (baseline );
321343 time (candidate );
322344 } else {
@@ -326,8 +348,11 @@ private static Comparison compare(LongSupplier baseline, LongSupplier candidate)
326348 }
327349 long [] baselineTimes = new long [MEASUREMENT_ROUNDS ];
328350 long [] candidateTimes = new long [MEASUREMENT_ROUNDS ];
351+ boolean [] baselineFirstRounds = new boolean [MEASUREMENT_ROUNDS ];
329352 for (int round = 0 ; round < MEASUREMENT_ROUNDS ; round ++) {
330- if ((round & 1 ) == 0 ) {
353+ boolean baselineFirst = ((round & 1 ) == 0 ) == baselineStarts ;
354+ baselineFirstRounds [round ] = baselineFirst ;
355+ if (baselineFirst ) {
331356 baselineTimes [round ] = time (baseline );
332357 candidateTimes [round ] = time (candidate );
333358 } else {
@@ -337,7 +362,10 @@ private static Comparison compare(LongSupplier baseline, LongSupplier candidate)
337362 }
338363 Samples baselineSamples = Samples .of (baselineTimes );
339364 Samples candidateSamples = Samples .of (candidateTimes );
340- return new Comparison (baselineSamples , candidateSamples , baselineTimes , candidateTimes ,
365+ return new Comparison (baselineSamples , candidateSamples ,
366+ OrderStratum .of (baselineTimes , candidateTimes , baselineFirstRounds , true ),
367+ OrderStratum .of (baselineTimes , candidateTimes , baselineFirstRounds , false ),
368+ baselineStarts ? "ABBA" : "BAAB" , baselineTimes , candidateTimes ,
341369 (double ) baselineSamples .median () / Math .max (1L , candidateSamples .median ()));
342370 }
343371
@@ -355,6 +383,11 @@ private static long time(LongSupplier operation) {
355383
356384 private void report (String result ) {
357385 results .add (result );
386+ try {
387+ Files .writeString (resultsOutput , result + System .lineSeparator (), StandardOpenOption .APPEND );
388+ } catch (IOException exception ) {
389+ throw new UncheckedIOException ("Unable to append benchmark result" , exception );
390+ }
358391 getLogger ().info ("IV_BENCH_RESULT " + result );
359392 }
360393
@@ -367,7 +400,35 @@ private static Samples of(long[] values) {
367400 }
368401 }
369402
370- private record Comparison (Samples baseline , Samples candidate , long [] baselineRoundsNs ,
403+ private record OrderStratum (Samples baseline , Samples candidate , double speedup ) {
404+
405+ private static OrderStratum of (long [] baselineTimes , long [] candidateTimes ,
406+ boolean [] baselineFirstRounds , boolean baselineFirst ) {
407+ int size = 0 ;
408+ for (boolean value : baselineFirstRounds ) {
409+ if (value == baselineFirst ) {
410+ size ++;
411+ }
412+ }
413+ long [] baseline = new long [size ];
414+ long [] candidate = new long [size ];
415+ int output = 0 ;
416+ for (int index = 0 ; index < baselineFirstRounds .length ; index ++) {
417+ if (baselineFirstRounds [index ] == baselineFirst ) {
418+ baseline [output ] = baselineTimes [index ];
419+ candidate [output ] = candidateTimes [index ];
420+ output ++;
421+ }
422+ }
423+ Samples baselineSamples = Samples .of (baseline );
424+ Samples candidateSamples = Samples .of (candidate );
425+ return new OrderStratum (baselineSamples , candidateSamples ,
426+ (double ) baselineSamples .median () / Math .max (1L , candidateSamples .median ()));
427+ }
428+ }
429+
430+ private record Comparison (Samples baseline , Samples candidate , OrderStratum baselineFirst ,
431+ OrderStratum candidateFirst , String roundOrder , long [] baselineRoundsNs ,
371432 long [] candidateRoundsNs , double speedup ) {
372433 }
373434
0 commit comments