Measure the finer chain tail: 84/120 becomes 18/120, and the derivation was right by cancellation
blit.s gains v7 -- v6's 24-pixel movem chain plus a second chain whose unit is
one `move.l (a0)+,(a2)+`. Measured over 13 span lengths by span.sh, every config
pixel-exact:
cycles = 66.0 per span + 9.143 per COARSE pixel + 9.978 per FINE pixel
fitting all 13 to within 0.2%. v5 and v6 re-measure to FINDINGS 30 exactly, so
the harness has not drifted underneath the new variant.
Rescored against the same scsi window and the same additive model, v7 takes
84/120 frames over budget to 18/120 -- exactly what FINDINGS 39.4 derived, and
that agreement is two cancelling errors: the derivation's 2-register movem tail
is 29% too dear per pixel, and its "nothing per span" for the second chain entry
is 22.3 clocks too cheap. The plain post-incrementing move.l is the right tail
instruction, and it makes the padding quantum 2 pixels, which a run of 4x4
blocks pads to exactly zero.
The DMAC stays dropped on a measurement now rather than an argument: v7 takes
back 37 of the 43 frames the array chain would, with no reserved channel and no
timing neither emulator here can verify. Break-even against all-V1 moves from
L=4 blocks to L=2.
The fine displacement is carried mid-stream rather than in the span record, so
the decoder holds nothing across the copy and keeps all 12 payload registers --
which is the whole reason the coarse unit is 24 pixels.
span.sh is now -seconds_to_run 200 (30 s wall, 36 configs) and takes its
expected snapshot count from the generated metadata instead of a literal 23.
Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
@@ -41,8 +41,11 @@ CPU + DMA, additive. Session 10's first pass used max(CPU, bus) and got 53/120
|
||||
where the additive model gives 84/120; FINDINGS 35's flat debit was right.
|
||||
|
||||
So the only material difference left is v6's 24-pixel padding quantum -- and
|
||||
that is a property of v6's unrolled chain, not of the CPU. The `v6 fine tail`
|
||||
column prices fixing it in software instead.
|
||||
that is a property of v6's unrolled chain, not of the CPU. The `v7 fine tail`
|
||||
column prices fixing it in software instead, and as of session 11 that column
|
||||
is MEASURED on the 68000 (blit.s v7, tools/bench/span.sh, FINDINGS 40) rather
|
||||
than derived: 66.0 clocks per span + 9.143 per coarse pixel + 9.978 per fine
|
||||
pixel, with a 2-pixel quantum that a run of 4x4 blocks pads to exactly.
|
||||
"""
|
||||
import sys, os, argparse
|
||||
sys.path.insert(0, "tools/encoder")
|
||||
@@ -100,8 +103,8 @@ def span_cost(design, L):
|
||||
if design == "v6":
|
||||
px = B.pad24(4 * L)
|
||||
return 4 * px, 4 * (B.V6_SPAN_CYC + px * B.V6_PX_CYC)
|
||||
if design == "v6fine":
|
||||
px, c = B.v6_fine(4 * L)
|
||||
if design == "v7":
|
||||
px, c = B.v7_span(4 * L)
|
||||
return 4 * px, 4 * c
|
||||
px = 4 * L
|
||||
return 4 * px, 4 * (B.DMA_CHAIN_CLK + px * a.dma_px_clk)
|
||||
@@ -127,7 +130,9 @@ def score(design):
|
||||
cur_b = sum(BLK_B[int(b)] for b in m[by][i:j])
|
||||
px, sc = span_cost(design, L)
|
||||
sc += L * C_SKIP_MIXED # the dispatch still happens
|
||||
span_b = 4 * SPAN_HDR + px * SPAN_BYTES_PX
|
||||
# v7 carries a second u16 (the fine displacement) per span.
|
||||
hdr = B.V7_SPAN_HDR if design == "v7" else SPAN_HDR
|
||||
span_b = 4 * hdr + px * SPAN_BYTES_PX
|
||||
if sc < cur_c:
|
||||
cand.append((cur_c - sc, span_b - cur_b, by, i, j, sc, L))
|
||||
cand.sort(key=lambda s: -(s[0] / max(s[1], 1)))
|
||||
@@ -154,7 +159,7 @@ def score(design):
|
||||
|
||||
|
||||
DESIGNS = [("today", "none"), ("v6 span", "v6"),
|
||||
("v6 fine tail", "v6fine"), ("DMAC chain", "dmac")]
|
||||
("v7 fine tail", "v7"), ("DMAC chain", "dmac")]
|
||||
res = {n: score(k) for n, k in DESIGNS}
|
||||
|
||||
print(f"{a.container}: {d.nframes} frames, {d.nb} blocks, {a.fps:g} fps")
|
||||
@@ -188,11 +193,11 @@ print(f"\n ADDITIVE: frame = CPU + span painting + disk DMA. The 68000 has no"
|
||||
|
||||
# What is left of the case, isolated.
|
||||
v6m = int((res["v6 span"][0] > FRAME_CYC).sum())
|
||||
finem = int((res["v6 fine tail"][0] > FRAME_CYC).sum())
|
||||
finem = int((res["v7 fine tail"][0] > FRAME_CYC).sum())
|
||||
dmam = int((res["DMAC chain"][0] > FRAME_CYC).sum())
|
||||
print(f"\nWHAT THE DMAC ACTUALLY BUYS, decomposed")
|
||||
print(f" v6 as built {v6m}/{d.nframes} frames over")
|
||||
print(f" v6 with a finer chain tail (software) {finem}/{d.nframes}")
|
||||
print(f" v7, a finer chain tail (MEASURED) {finem}/{d.nframes}")
|
||||
print(f" DMAC chain {dmam}/{d.nframes}")
|
||||
print(f" -> of the gap between v6 and the DMAC, "
|
||||
f"{100*(v6m-finem)/max(v6m-dmam,1):.0f}% is the 24-pixel padding")
|
||||
@@ -206,9 +211,9 @@ print(f" in software. The rest is 1.7% a pixel and 7.7 clocks a span.")
|
||||
|
||||
print(f"\nbreak-even against all-V1 ({C_V1:.1f} cycles/block), clocks per block")
|
||||
print(f" {'L':<16}" + "".join(f"{L:>8}" for L in (1, 2, 3, 4, 8, 16, 64)))
|
||||
for nm, dz in (("v6 as built", "v6"), ("v6 fine tail", "v6fine"), ("DMAC chain", "dmac")):
|
||||
for nm, dz in (("v6 as built", "v6"), ("v7 fine tail", "v7"), ("DMAC chain", "dmac")):
|
||||
print(f" {nm:<16}" + "".join(f"{span_cost(dz, L)[1]/L:>8.0f}"
|
||||
for L in (1, 2, 3, 4, 8, 16, 64)))
|
||||
for nm, dz in (("v6 as built", "v6"), ("v6 fine tail", "v6fine"), ("DMAC chain", "dmac")):
|
||||
for nm, dz in (("v6 as built", "v6"), ("v7 fine tail", "v7"), ("DMAC chain", "dmac")):
|
||||
brk = next((L for L in range(1, 65) if span_cost(dz, L)[1] < L * C_V1), None)
|
||||
print(f" {nm:<16} beats all-V1 from L={brk} blocks up")
|
||||
|
||||
+48
-14
@@ -110,20 +110,54 @@ def block_bus(mode_map, spanned=None):
|
||||
return pref, data
|
||||
|
||||
|
||||
# --- v6 with a finer tail (PROPOSAL, unmeasured -- Claude's, session 10) ----
|
||||
# --- v7: v6 with a finer tail (MEASURED, session 11, FINDINGS 40) ----------
|
||||
# v6 pads every span up to 24 pixels because its unrolled chain is built from
|
||||
# 12-register movem units. Adding a second, finer chain of 2-register units
|
||||
# (4 pixels) for the tail caps the padding at 3 pixels instead of 23, for the
|
||||
# price of some more unrolled code and nothing per span.
|
||||
# A 4-pixel unit: movem.l (a0)+,2 = 2w instr + 4 reads; movem.l 2,(a2) = 2w +
|
||||
# 4 writes; lea = 2w. 14 bus cycles for 4 pixels = 56 clocks, against a full
|
||||
# unit's 24 x 9.152 = 220 for 24. Dearer per pixel, paid at most once a span.
|
||||
V6_TAIL_PX, V6_TAIL_CLK = 4, 56
|
||||
# 12-register movem units, and FINDINGS 39.3 attributed 86% of the DMAC array
|
||||
# chain's advantage over v6 to exactly that padding. v7 keeps the coarse chain
|
||||
# and appends a second chain whose unit is one `move.l (a0)+,(a2)+` -- 2 pixels,
|
||||
# so the quantum is 2 and a run of 4x4 blocks pads to NOTHING.
|
||||
#
|
||||
# Session 10 proposed a 2-REGISTER MOVEM tail (4 pixels, derived at 56 clocks)
|
||||
# and that would have been the wrong instruction: movem.l (a0)+,d0-d1 plus
|
||||
# movem.l d0-d1,(a2) plus the lea is 14 bus cycles for 4 pixels, where two plain
|
||||
# move.l are 10. The plainest instruction on the machine wins the tail.
|
||||
#
|
||||
# The second entry point needs a second dispatch, and the fine displacement is
|
||||
# carried MID-STREAM (after the coarse pixels, before the fine ones) rather than
|
||||
# in the span record, so the decoder holds nothing extra across the copy and
|
||||
# keeps all 12 payload registers. Costed as 2 more bytes per span.
|
||||
#
|
||||
# MEASURED by tools/bench/span.sh (blit.s v7, 13 span lengths, every config
|
||||
# pixel-exact): cycles = 66.0/span + 9.143/coarse pixel + 9.978/fine pixel,
|
||||
# fitting all 13 to within 0.2%.
|
||||
V7_SPAN_CYC = 66.0 # MEASURED, FINDINGS 40
|
||||
V7_CPX_CYC = 9.143 # MEASURED, FINDINGS 40 (24-pixel coarse unit)
|
||||
V7_FPX_CYC = 9.978 # MEASURED, FINDINGS 40 (2-pixel fine unit)
|
||||
V7_FINE_PX = 2
|
||||
# Bus: per span v6's 9 plus a second {move.w (a0)+,d0 ; jmp} = 2 + 2.
|
||||
# Per fine unit: move.l (a0)+,(a2)+ = 1 instruction word + 2 reads + 2 writes.
|
||||
V7_SPAN_BUS = 13
|
||||
V7_FINE_BUS = 5
|
||||
V7_SPAN_HDR = 8 # {u32 address, u16 coarse disp} + u16 fine disp
|
||||
|
||||
|
||||
def v6_fine(npix):
|
||||
"""(pixels carried, CPU clocks) for a span with the finer tail."""
|
||||
k, r = divmod(npix, V6_UNIT_PX)
|
||||
t = -(-r // V6_TAIL_PX)
|
||||
return (k * V6_UNIT_PX + t * V6_TAIL_PX,
|
||||
V6_SPAN_CYC + k * V6_UNIT_PX * V6_PX_CYC + t * V6_TAIL_CLK)
|
||||
def pad2(npix):
|
||||
return -(-npix // V7_FINE_PX) * V7_FINE_PX
|
||||
|
||||
|
||||
def v7_span(npix):
|
||||
"""(pixels carried, CPU clocks) for a v7 span of npix pixels."""
|
||||
k, r = divmod(pad2(npix), V6_UNIT_PX)
|
||||
return (k * V6_UNIT_PX + r,
|
||||
V7_SPAN_CYC + k * V6_UNIT_PX * V7_CPX_CYC + r * V7_FPX_CYC)
|
||||
|
||||
|
||||
def v7_span_bus(npix):
|
||||
"""Bus CYCLES a v7 span occupies -- instruction words plus data accesses."""
|
||||
k, r = divmod(pad2(npix), V6_UNIT_PX)
|
||||
return V7_SPAN_BUS + k * V6_UNIT_BUS + (r // V7_FINE_PX) * V7_FINE_BUS
|
||||
|
||||
|
||||
def v6_span_bus(npix):
|
||||
k = pad24(npix) // V6_UNIT_PX
|
||||
return V6_SPAN_BUS + k * V6_UNIT_BUS
|
||||
|
||||
Reference in New Issue
Block a user