joeylib2/PERF.md

4.2 KiB

JoeyLib cross-port performance (UBER benchmark)

Regenerated 2026-06-30. ST/Amiga/DOS columns are fresh measurements from the headless harness (scripts/bench-atarist.sh Hatari, bench-amiga.sh FS-UAE, bench-dos.sh DOSBox). The IIgs column is the perf reference: the generic+override redesign was bit-identical on IIgs (MAME iigs-verify checksum 0817FE), so its codegen — and thus its ops/sec — is unchanged from the last IIgs capture. Every non-IIgs cell is a ratio (port / IIgs); the directive is that every target meet or beat the IIgs floor (>= 1.0x).

Sprite ops now meet or beat the IIgs on BOTH 68k targets after this session's work (compiled DRAW emitters + raw-plane-byte save/restore); they were the dominant violations (down to 0.13x). The ST column is honest for the first time — the ST port was crashing on first present (user-mode I/O poke) until the Super(0L) fix in jlpInit.

Genuine remaining sub-1.0x cells and why:

  • ST jlDrawCircle (0.61-0.65x) — inherent planar-outline cost: a 4-plane word RMW per sparse outline pixel (8 memory accesses) vs the IIgs single nibble, amplified by ST video-DMA contention. Cycle-bound; ~0.80x ceiling even fully optimized. Not winnable in this format.
  • Amiga jlFillCircle (0.82x) — already asm span-fill (long-store middles); closing the gap needs a blitter area-fill rewrite (risk: the blitter has wedged the system before).
  • ST jlSpriteSaveAndDraw (0.97x) — ST runs save+draw as two dispatched ops; the IIgs fuses them. Marginal.
  • ST jlTileFill (0.98x), Amiga jlFillRect 80x80 (0.95x) — within noise.
  • ST jlFillRect 320x200 (0.53x) — ARTIFACT: the IIgs reference (60) is the SEI-inflated full-screen reading (see the ceiling note below; honest ~28). Against the honest reference ST is ~1.1x. Same caveat for surfaceClear / stagePresent full.
Op IIgs (ops/sec) Amiga (vs IIGS) Atari ST (vs IIGS) DOS (vs IIGS)
jlSurfaceClear 28 2.68x 1.32x 449.21x
jlPaletteSet 678 9.09x 4.27x 22.01x
jlScbSetRange 1005 3.62x 1.86x 14.20x
jlDrawPixel 1755 2.04x 1.18x 9.40x
jlDrawLine H 682 2.26x 1.61x 26.93x
jlDrawLine V 90 1.70x 1.14x 145.83x
jlDrawLine diag 35 1.14x 1.06x 475.37x
jlDrawRect 100x100 75 1.67x 1.16x 206.49x
jlDrawCircle r=16 232 1.20x 0.65x 64.79x
jlDrawCircle r=80 56 1.16x 0.61x 301.25x
jlFillRect 16x16 450 1.24x 1.11x 36.04x
jlFillRect 80x80 75 0.95x 1.33x 205.09x
jlFillRect 320x200 60 1.13x 0.53x 188.92x
jlFillCircle r=40 38 0.82x 1.39x 398.24x
jlSamplePixel 1916 3.42x 1.92x 18.04x
jlTileFill 1252 1.74x 0.98x 10.60x
jlTileCopy 997 1.80x 1.04x 7.88x
jlTileCopyMasked 498 1.81x 1.10x 32.48x
jlTilePaste 1106 1.99x 1.12x 14.64x
jlTileSnap 1473 2.29x 1.53x 9.96x
jlSpriteSaveUnder 528 1.05x 1.29x 23.69x
jlSpriteDraw 438 2.09x 1.09x 23.85x
jlSpriteRestoreUnder 487 1.08x 1.11x 34.83x
jlSpriteSaveAndDraw 277 1.23x 0.97x 53.79x
jlStagePresent full 42 7.81x 1.10x 412.17x
jlInputPoll 273 13.94x 4.19x 37.87x
jlKeyDown 3382 7.75x 3.76x 18.94x
jlKeyPressed 3345 7.66x 3.81x 18.73x
jlMouseX 4170 10.40x 5.22x 26.30x
joeyJoyConnected 3378 7.71x 3.71x 18.96x
jlAudioFrameTick 4106 8.00x 2.50x 12.42x
jlAudioIsPlayingMod 3536 9.30x 4.33x 26.78x
surfaceMarkDirtyRect (via jlFillRect 32x32) 240 1.42x 1.31x 47.12x

IIgs full-screen ceiling (~29 ops/sec). A full-screen op moves 32000 bytes = 16000 word-stores x 6 cyc (STA long,X / PEI, both 3 cyc/byte) / 2.8 MHz ~= 34 ms ~= 29 ops/sec maximum. So jlSurfaceClear, jlFillRect 320x200, and jlStagePresent full cannot legitimately exceed ~29; a higher reading means the benchmark clock was perturbed. The clock is jlFrameCount = the VBL-interrupt- driven GetTick, so an op that holds SEI while it runs can over- report its ops/sec. Treat full-screen IIgs numbers above ~29 as inflated and cross-check against the cycle model.