Files
redapricot/client/resume_bench_test.go
2026-08-15 17:31:35 +08:00

105 lines
3.2 KiB
Go

package client
import (
"testing"
)
// The retained region is the one real cost stream resumption adds to the send
// path: a chunk has to survive past the frame write, so it is copied. These
// pin both halves of that claim — that the copy is the only cost, and that
// disabling the feature removes it entirely rather than merely shrinking it.
func benchStream(resumable bool) *Stream {
s := &Stream{resumable: resumable}
return s
}
// BenchmarkRetainChunk measures what emit adds over a bare frame write: the
// trim-and-append into the retained region. Compare the two variants; the delta
// is the per-byte copy the feature costs.
func BenchmarkRetainChunk(b *testing.B) {
chunk := make([]byte, DataChunkSize)
window := int64(DefaultStreamWindow)
b.Run("resume-on", func(b *testing.B) {
s := benchStream(true)
b.SetBytes(int64(len(chunk)))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
// Model the steady state: credit trails one window behind, so the
// buffer trims about as fast as it grows and stays bounded.
acked := s.un.end() - window
if acked < 0 {
acked = 0
}
s.un.advance(acked)
s.un.append(chunk)
}
if got := int64(s.un.length()); got > window+int64(len(chunk)) {
b.Fatalf("retained region grew past one window: %d", got)
}
})
b.Run("resume-off", func(b *testing.B) {
s := benchStream(false)
b.SetBytes(int64(len(chunk)))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
if s.resumable {
s.un.advance(0)
s.un.append(chunk)
}
}
})
}
// TestResumeDisabledAllocatesNothing pins the off switch at the level that
// matters. It is easy for a feature flag to stop the wire behaviour while
// leaving the bookkeeping running, which would keep the memory cost and the
// per-byte copy for a user who explicitly turned it off — a partial revert that
// nobody would notice.
func TestResumeDisabledAllocatesNothing(t *testing.T) {
chunk := make([]byte, DataChunkSize)
s := benchStream(false)
allocs := testing.AllocsPerRun(1000, func() {
if s.resumable {
s.un.advance(0)
s.un.append(chunk)
}
})
if allocs != 0 {
t.Fatalf("resume disabled still allocated %.1f times per send", allocs)
}
if s.un.buf != nil {
t.Fatalf("resume disabled still allocated a retained region of %d bytes", cap(s.un.buf))
}
}
// TestRetainedRegionStaysWithinWindow is the memory bound the design rests on:
// flow control already caps outstanding bytes at one window, so the retained
// region needs no cap of its own. If that ever stopped holding, a busy stream
// would grow without limit and the hub would be the first to notice.
func TestRetainedRegionStaysWithinWindow(t *testing.T) {
const window = DefaultStreamWindow
chunk := make([]byte, DataChunkSize)
var u unackedBuf
for i := 0; i < 5000; i++ {
// A sender may never have more than one window outstanding, which is
// exactly what acquireSendWnd enforces before emit is ever reached.
if u.length()+len(chunk) > window {
u.advance(u.base() + int64(len(chunk)))
}
u.append(chunk)
if u.length() > window {
t.Fatalf("round %d: retained %d bytes for a %d-byte window", i, u.length(), window)
}
}
if cap(u.buf) > 4*window {
t.Fatalf("backing array grew to %d for a %d-byte window", cap(u.buf), window)
}
}