Skip to content

Commit 5b7acd8

Browse files
committed
feat(metal): MTP dynamic draft cap — depth-gated additive raises off acceptance streaks (#359); 26B pair @16k 92.0 -> 99.8 tok/s
The calibration curves' loudest finding converted: 40% of 26B cycles accept the whole fixed K=4 block (median end-of-block cumprod 0.995), densest exactly where verify forwards are dearest (83% acceptance at 16K depth). mtpDraftLen raises the cap 4 -> 6 -> 8 after consecutive fully-accepted cycles and resets to base on any partial cycle; raises are gated to pos >= 4096 because the live A/B priced the ungated version (+8.6% at 16K but -2.7% shallow where blocks mostly miss at 48% acceptance) — below the gate the loop is byte-identical to the fixed-K engine, pinned by test. Output is unchanged by construction (verify pins the emitted stream to the target's greedy continuation at any block length). Zero per-token cost: acceptance history only — the cumprod theta-stop (0.40, measured) stays banked in #359 for the live-prob slice. Receipts: 26B @16k 92.0 -> 99.9 ungated / 99.8 gated (twice, identical output), verify forwards 10 -> 9; shallow 123.7 preserved by the gate; kill switch LTHN_MTP_DRAFTLEN=0; suite 1942/17 pkgs green. Co-Authored-By: Virgil <virgil@lethean.io>
1 parent 2d27879 commit 5b7acd8

3 files changed

Lines changed: 178 additions & 2 deletions

File tree

go/engine/metal/assistant_load.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,6 +1724,7 @@ func (pair *AssistantPair) GenerateFromSessionEach(target *ArchSession, promptID
17241724
stopped := false
17251725
lowAcceptStreak := 0
17261726
var reng mtpReengage
1727+
dl := newMTPDraftLen(draftTokens)
17271728
// runPlainStretch runs the bounded cooldown stretch (committing a pending
17281729
// lead first), measures the live plain rate, and re-arms drafting into a
17291730
// probe window. done=true ends the outer loop (stop / maxNew / error).
@@ -1741,7 +1742,7 @@ func (pair *AssistantPair) GenerateFromSessionEach(target *ArchSession, promptID
17411742
}
17421743
for len(result.Tokens) < maxNew && !stopped {
17431744
remaining := maxNew - len(result.Tokens)
1744-
blockSize := min(draftTokens, remaining)
1745+
blockSize := dl.next(remaining)
17451746
cycleT0 := time.Now()
17461747
wasProbing := reng.probing()
17471748
diagRound := mtpDiagForTest && result.TargetVerifyCalls < 3
@@ -1819,6 +1820,7 @@ func (pair *AssistantPair) GenerateFromSessionEach(target *ArchSession, promptID
18191820
break
18201821
}
18211822
if verify.AllAccepted {
1823+
dl.cycle(true, target.pos)
18221824
lowAcceptStreak = 0
18231825
carryLead = -1
18241826
if !mtpConfForce && !wasProbing && reng.needsDeepBootstrap(target.pos, len(result.Tokens), maxNew) {
@@ -1849,6 +1851,7 @@ func (pair *AssistantPair) GenerateFromSessionEach(target *ArchSession, promptID
18491851
continue
18501852
}
18511853

1854+
dl.cycle(false, target.pos)
18521855
replacement := verify.ReplacementToken
18531856
if nativeAssistantEmitToken(&result, replacement, eosID, yield) {
18541857
stopped = true
@@ -1963,6 +1966,7 @@ func (pair *AssistantPair) GenerateSampledFromSessionEach(target *ArchSession, p
19631966
draftSampler := model.NewSampler(0)
19641967
lowAcceptStreak := 0
19651968
var reng mtpReengage
1969+
dl := newMTPDraftLen(draftTokens)
19661970
defer func() { target.sampleHistory = finalHistory }()
19671971
// runPlainStretch is the sampled twin of the greedy lane's: the bounded
19681972
// cooldown stretch, rate measurement, and probe re-arm (#299).
@@ -1982,7 +1986,7 @@ func (pair *AssistantPair) GenerateSampledFromSessionEach(target *ArchSession, p
19821986
}
19831987
for len(result.Tokens) < maxNew && !stopped {
19841988
remaining := maxNew - len(result.Tokens)
1985-
blockSize := min(draftTokens, remaining)
1989+
blockSize := dl.next(remaining)
19861990
cycleT0 := time.Now()
19871991
wasProbing := reng.probing()
19881992
pickParams := target.mtpSamplePickParams(params, stopTokens, len(result.Tokens))
@@ -2044,6 +2048,7 @@ func (pair *AssistantPair) GenerateSampledFromSessionEach(target *ArchSession, p
20442048
break
20452049
}
20462050
if verify.AllAccepted {
2051+
dl.cycle(true, target.pos)
20472052
lowAcceptStreak = 0
20482053
carryLead = -1
20492054
if !mtpConfForce && !wasProbing && reng.needsDeepBootstrap(target.pos, len(result.Tokens), maxNew) {
@@ -2074,6 +2079,7 @@ func (pair *AssistantPair) GenerateSampledFromSessionEach(target *ArchSession, p
20742079
continue
20752080
}
20762081

2082+
dl.cycle(false, target.pos)
20772083
replacement := verify.ReplacementToken
20782084
result.Tokens = append(result.Tokens, replacement)
20792085
yieldStopped := yield != nil && !yield(replacement)

go/engine/metal/mtp_draftlen.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// SPDX-Licence-Identifier: EUPL-1.2
2+
3+
//go:build darwin && arm64
4+
5+
package native
6+
7+
import "os"
8+
9+
// mtpDraftLen — #359's first live scheduling slice: an additive-only dynamic
10+
// draft cap driven by acceptance history. The LTHN_MTP_CONF calibration runs
11+
// showed 40% of 26B cycles (30% on 12B) accept the whole fixed K=4 block and
12+
// end with cumprod ~0.99 — the drafter wants to keep going and is right, and
13+
// the waste is worst exactly where drafting pays most (83% acceptance at 16K
14+
// depth, where every avoided verify forward is the expensive deep kind). So:
15+
// consecutive fully-accepted cycles raise the cap toward mtpDraftLenMax;
16+
// any partial cycle resets it to the caller's base. Output is unchanged by
17+
// construction — verification pins the emitted stream to the target's greedy
18+
// continuation whatever the block length; only the cycle economics move.
19+
// Confidence-cumprod truncation (the θ=0.40 early stop) is the next slice —
20+
// it needs a per-token probability on the live path, which this deliberately
21+
// avoids: acceptance history is free.
22+
//
23+
// LTHN_MTP_DRAFTLEN=0 restores the fixed base cap byte-exactly (the repro
24+
// anchor, same convention as LTHN_MTP_REENGAGE).
25+
var mtpDraftLenDisabled = os.Getenv("LTHN_MTP_DRAFTLEN") == "0"
26+
27+
const (
28+
// mtpDraftLenMax bounds the raised cap: DSpark ships block7 drafters and
29+
// our verify lane is sized for small widths (#354), so 8 stays inside
30+
// the measured-cheap regime (block + carried lead = 9 verify rows).
31+
mtpDraftLenMax = 8
32+
// mtpDraftLenHotBar — full-accept cycles in a row before a raise. One
33+
// full block is common noise (a hot streak is not); two in a row was the
34+
// calibration sample's reliable "still hot" signal.
35+
mtpDraftLenHotBar = 2
36+
// mtpDraftLenRaise — cap step per hot streak (4 → 6 → 8).
37+
mtpDraftLenRaise = 2
38+
// mtpDraftLenDeepPos gates raises to depth: the calibration curves put
39+
// dense full-accept streaks at depth (83% acceptance at 16K vs 48%
40+
// shallow), and the live A/B confirmed the trade — +8.6% at 16K, −2.7%
41+
// shallow when raises applied everywhere. Below this position the cap
42+
// stays at base and the loop is byte-identical to the fixed-K engine.
43+
mtpDraftLenDeepPos = 4096
44+
)
45+
46+
type mtpDraftLen struct {
47+
base int // caller-resolved fixed draft tokens (flag or engine default)
48+
cap int // live cap, [base, mtpDraftLenMax]
49+
hot int // consecutive fully-accepted cycles at the current cap
50+
}
51+
52+
func newMTPDraftLen(base int) mtpDraftLen {
53+
return mtpDraftLen{base: base, cap: base}
54+
}
55+
56+
// next returns the block size for the coming draft call.
57+
func (d *mtpDraftLen) next(remaining int) int {
58+
return min(d.cap, remaining)
59+
}
60+
61+
// cycle feeds one verify outcome. fullAccept must be the cycle's AllAccepted
62+
// verdict (carry-aware), not a raw count comparison; pos is the target
63+
// position, gating raises to the deep regime where the streaks are dense.
64+
func (d *mtpDraftLen) cycle(fullAccept bool, pos int) {
65+
if mtpDraftLenDisabled {
66+
return
67+
}
68+
if !fullAccept {
69+
d.cap = d.base
70+
d.hot = 0
71+
return
72+
}
73+
if pos < mtpDraftLenDeepPos {
74+
return
75+
}
76+
d.hot++
77+
if d.hot >= mtpDraftLenHotBar && d.cap < mtpDraftLenMax {
78+
d.cap = min(d.cap+mtpDraftLenRaise, mtpDraftLenMax)
79+
d.hot = 0
80+
}
81+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// SPDX-Licence-Identifier: EUPL-1.2
2+
3+
//go:build darwin && arm64
4+
5+
package native
6+
7+
import "testing"
8+
9+
// TestMTPDraftLenRaisesOnHotStreak pins the additive ladder: two full-accept
10+
// cycles raise the cap one step, two more raise it again, capped at max.
11+
func TestMTPDraftLenRaisesOnHotStreak(t *testing.T) {
12+
if mtpDraftLenDisabled {
13+
t.Skip("LTHN_MTP_DRAFTLEN=0")
14+
}
15+
d := newMTPDraftLen(4)
16+
if got := d.next(100); got != 4 {
17+
t.Fatalf("initial next = %d, want 4", got)
18+
}
19+
d.cycle(true, mtpDraftLenDeepPos)
20+
if d.cap != 4 {
21+
t.Fatalf("cap after 1 hot = %d, want 4 (bar is 2)", d.cap)
22+
}
23+
d.cycle(true, mtpDraftLenDeepPos)
24+
if d.cap != 6 {
25+
t.Fatalf("cap after 2 hot = %d, want 6", d.cap)
26+
}
27+
d.cycle(true, mtpDraftLenDeepPos)
28+
d.cycle(true, mtpDraftLenDeepPos)
29+
if d.cap != 8 {
30+
t.Fatalf("cap after 4 hot = %d, want 8", d.cap)
31+
}
32+
d.cycle(true, mtpDraftLenDeepPos)
33+
d.cycle(true, mtpDraftLenDeepPos)
34+
if d.cap != 8 {
35+
t.Fatalf("cap past max = %d, want 8", d.cap)
36+
}
37+
}
38+
39+
// TestMTPDraftLenShallowNeverRaises pins the depth gate: full-accept streaks
40+
// below mtpDraftLenDeepPos leave the cap at base (the shallow lane stays
41+
// byte-identical to the fixed-K engine).
42+
func TestMTPDraftLenShallowNeverRaises(t *testing.T) {
43+
if mtpDraftLenDisabled {
44+
t.Skip("LTHN_MTP_DRAFTLEN=0")
45+
}
46+
d := newMTPDraftLen(4)
47+
for range 6 {
48+
d.cycle(true, mtpDraftLenDeepPos-1)
49+
}
50+
if d.cap != 4 {
51+
t.Fatalf("shallow cap = %d, want 4", d.cap)
52+
}
53+
}
54+
55+
// TestMTPDraftLenResetsOnPartialCycle pins the re-anchor: any non-full cycle
56+
// drops the cap straight back to base and clears the streak.
57+
func TestMTPDraftLenResetsOnPartialCycle(t *testing.T) {
58+
if mtpDraftLenDisabled {
59+
t.Skip("LTHN_MTP_DRAFTLEN=0")
60+
}
61+
d := newMTPDraftLen(4)
62+
d.cycle(true, mtpDraftLenDeepPos)
63+
d.cycle(true, mtpDraftLenDeepPos)
64+
if d.cap != 6 {
65+
t.Fatalf("setup cap = %d, want 6", d.cap)
66+
}
67+
d.cycle(false, mtpDraftLenDeepPos)
68+
if d.cap != 4 || d.hot != 0 {
69+
t.Fatalf("after partial: cap=%d hot=%d, want 4,0", d.cap, d.hot)
70+
}
71+
// a lone full accept after the reset must not raise (streak restarted)
72+
d.cycle(true, mtpDraftLenDeepPos)
73+
if d.cap != 4 {
74+
t.Fatalf("cap after reset+1 hot = %d, want 4", d.cap)
75+
}
76+
}
77+
78+
// TestMTPDraftLenNextRespectsRemaining pins that the emitted block never
79+
// exceeds the tokens still wanted, whatever the cap.
80+
func TestMTPDraftLenNextRespectsRemaining(t *testing.T) {
81+
d := newMTPDraftLen(4)
82+
d.cap = 8
83+
if got := d.next(3); got != 3 {
84+
t.Fatalf("next(3) = %d, want 3", got)
85+
}
86+
if got := d.next(50); got != 8 {
87+
t.Fatalf("next(50) = %d, want 8", got)
88+
}
89+
}

0 commit comments

Comments
 (0)