Skip to content

Commit e15ba1e

Browse files
committed
perf(engine/metal): submit-ahead generalised to a depth queue — depth 1 by receipt (#341 phase 4)
generateSubmitAheadLinks becomes a liveSubmitAheadDepth queue with the encode-before-wait ordering preserved (the first cut of the refactor waited before encoding and idled the GPU through every host encode: 132.5 tok/s vs 155.0 — caught by the receipt, fixed, and pinned in a comment on the loop). Clean depth A/B on the real 26B with correct ordering: depth 2 = 154.2, depth 1 = 152.9 — inside the wobble band, host/sync gap pinned at 0.18 ms/token either way. One queued link already lets the GPU start the next command buffer the instant the current completes; the residual gap is cb-to-cb scheduling latency no queue depth removes. Depth stays 1 (less speculative discard at a stop) as a documented tunable; the stop unwind generalises to any depth (pos -= queue length, one truncate). Receipts: MoE identity suites green incl. the wrapped-ring EOS rollback; metal 1397 green; repo 10451 green (both at depth 2 pre-flip, identity re-run at depth 1). Co-Authored-By: Virgil <virgil@lethean.io>
1 parent 452ed73 commit e15ba1e

1 file changed

Lines changed: 56 additions & 34 deletions

File tree

go/engine/metal/arch_session.go

Lines changed: 56 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4229,60 +4229,82 @@ func (s *ArchSession) discardLiveLink(link pendingLiveLink) {
42294229
s.headEnc.putGreedyScratch(link.scratch)
42304230
}
42314231

4232-
// generateSubmitAheadLinks runs the chained live decode with ONE speculative link in flight:
4233-
// token N+1's command buffer commits while N executes — its input is N's on-GPU embed, so no
4234-
// host value is needed — and the host encode overlaps the GPU. A stop with a speculative link
4235-
// in flight unwinds it (wait, position back one, paged-KV truncate). A declined link here is
4236-
// a structural fault, not a fallback case: its tail never wrote xA, so a further link would
4237-
// compute on stale input.
4232+
// liveSubmitAheadDepth is how many speculative chained links stay committed while the
4233+
// oldest executes. Depth 1 by receipt (#341 phase 4): with the correct encode-before-
4234+
// wait ordering, depth 2 measured 154.2 tok/s vs depth 1's 152.9 on the real 26B —
4235+
// inside the run-to-run wobble band — with the host/sync gap pinned at 0.18 ms/token
4236+
// either way. One queued link already lets the GPU start the next command buffer the
4237+
// instant the current completes; the residual gap is cb-to-cb scheduling latency that
4238+
// no queue depth removes, while every extra depth discards one more speculative
4239+
// token's GPU work at a stop. Greedy chaining keeps any depth correct-by-construction
4240+
// (a stop is a pure position/counter rewind, ring-safe — the identity tests cover the
4241+
// wrapped-ring rollback shape), so this stays a documented tunable.
4242+
const liveSubmitAheadDepth = 1
4243+
4244+
// generateSubmitAheadLinks runs the chained live decode with up to liveSubmitAheadDepth
4245+
// speculative links in flight: each link's command buffer commits while older links
4246+
// execute — its input is the previous link's on-GPU embed, so no host value is needed —
4247+
// and the host encode overlaps the GPU. A stop with speculative links in flight unwinds
4248+
// them (wait, position back by the queue length, one paged-KV truncate). A declined link
4249+
// here is a structural fault, not a fallback case: its tail never wrote xA, so a further
4250+
// link would compute on stale input.
42384251
func (s *ArchSession) generateSubmitAheadLinks(gen []int32, maxNew, eosID int, suppress []int32, sc *plGPUScratch, yield func(int32) bool) ([]int32, bool, error) {
4239-
pending, ok, err := s.stepGreedyLiveCommit(suppress, sc)
4240-
if err != nil {
4241-
return gen, false, err
4252+
queue := make([]pendingLiveLink, 0, liveSubmitAheadDepth)
4253+
discardQueue := func() {
4254+
for _, link := range queue {
4255+
s.discardLiveLink(link)
4256+
}
4257+
queue = queue[:0]
42424258
}
4243-
if !ok {
4244-
return gen, false, core.NewError("native.ArchSession.generateSubmitAheadLinks: chain declined mid-stream")
4259+
push := func() error {
4260+
link, ok, err := s.stepGreedyLiveCommit(suppress, sc)
4261+
if err != nil {
4262+
discardQueue()
4263+
return err
4264+
}
4265+
if !ok {
4266+
discardQueue()
4267+
return core.NewError("native.ArchSession.generateSubmitAheadLinks: chain declined mid-stream")
4268+
}
4269+
queue = append(queue, link)
4270+
return nil
42454271
}
4246-
for {
4247-
var next pendingLiveLink
4248-
haveNext := false
4249-
if len(gen)+1 < maxNew {
4250-
var nok bool
4251-
if next, nok, err = s.stepGreedyLiveCommit(suppress, sc); err != nil {
4252-
s.discardLiveLink(pending)
4272+
for len(queue) < liveSubmitAheadDepth && len(gen)+len(queue) < maxNew {
4273+
if err := push(); err != nil {
4274+
return gen, false, err
4275+
}
4276+
}
4277+
for len(queue) > 0 {
4278+
// Commit the NEXT link before waiting the oldest: the host encode overlaps
4279+
// the queued links' GPU execution (waiting first would idle the GPU through
4280+
// every encode — measured 132.5 vs 155.0 tok/s on the real 26B).
4281+
if len(gen)+len(queue) < maxNew {
4282+
if err := push(); err != nil {
42534283
return gen, false, err
42544284
}
4255-
if !nok {
4256-
s.discardLiveLink(pending)
4257-
return gen, false, core.NewError("native.ArchSession.generateSubmitAheadLinks: chain declined mid-stream")
4258-
}
4259-
haveNext = true
42604285
}
4286+
pending := queue[0]
4287+
queue = queue[1:]
42614288
tok, werr := s.waitLiveLink(pending)
42624289
if werr != nil {
4263-
if haveNext {
4264-
s.discardLiveLink(next)
4265-
}
4290+
discardQueue()
42664291
return gen, false, werr
42674292
}
42684293
gen = append(gen, tok)
42694294
stop := (yield != nil && !yield(tok)) || (eosID >= 0 && int(tok) == eosID)
42704295
if stop {
4271-
if haveNext {
4272-
// unwind the speculative link: its step cached one token past the stop.
4273-
s.discardLiveLink(next)
4274-
s.pos--
4296+
if n := len(queue); n > 0 {
4297+
// unwind the speculative links: their steps cached tokens past the stop.
4298+
discardQueue()
4299+
s.pos -= n
42754300
if terr := s.state.truncateDevicePagedKV(s.pos); terr != nil {
42764301
return gen, true, terr
42774302
}
42784303
}
42794304
return gen, true, nil
42804305
}
4281-
if !haveNext {
4282-
return gen, false, nil // token budget reached with pending as the last wanted link
4283-
}
4284-
pending = next
42854306
}
4307+
return gen, false, nil // token budget reached
42864308
}
42874309

42884310
// stepGreedyLiveCommit encodes + COMMITS one chained live link without waiting (the

0 commit comments

Comments
 (0)