Files
bench/modes/zap_vs_codec/claim5_chunk_reassemble_test.go
zeekayandHanzo Dev 2a1bc90d5e Go 1.27.1, and the code it can say more simply
Modules declare go 1.27.1 and the build images follow. go fix applied the
modernizers; omitzero is not applied, since it changes what the JSON says.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-16 19:14:25 -07:00

259 lines
8.5 KiB
Go

package zapvscodec
import (
"crypto/sha256"
"math/rand"
"testing"
)
// Claim 5: Content-addressable chunk reconstruction.
//
// Methodology: A 1 MiB block split into 64 KiB chunks (16 chunks). The chunks
// arrive out-of-order over a hypothetical chunk-distribution channel (think
// erasure-coded multicast).
//
// - Codec: chunk boundaries do not align with codec struct boundaries.
// Codec cannot start decoding until the FULL 1 MiB buffer is reassembled.
// Validation latency = time-to-last-chunk + unmarshal cost.
// - ZAP: chunks are content-addressed (sha256 of each 64 KiB block). The
// reassembled buffer is a concatenation; ZAP's magic+header check succeeds
// as soon as the FIRST chunk is in place. Validation latency = time-to-
// first-chunk + magic check + offset check.
//
// What this measures: time-to-first-validation-decision (TTFV) — the moment
// we know whether the buffer can be trusted. For codec this is essentially
// "wait for full buffer". For ZAP this is "validate header from chunk 0".
//
// What this does NOT measure: the actual erasure coding / network delivery
// latency (modeled identically as a 1 μs sleep per chunk).
const (
chunkSize = 64 * 1024
chunkBlockBytes = 1 * 1024 * 1024
chunkCount = chunkBlockBytes / chunkSize
)
// makeChunkedBlock returns 16 ordered chunks of a 1 MiB block. Each chunk's
// content-address is included so the receiver can verify it before placing.
type chunk struct {
idx int
data []byte
hash [32]byte
}
func makeChunkedBlockZAP() ([]chunk, []byte) {
// Build a 1 MiB ZAP buffer: header at byte 0..15, then large payload.
// Use the SAME ZAP wire header. Pad to 1 MiB with deterministic bytes.
buf := MakeZAPValidatorBytes()
if len(buf) >= chunkBlockBytes {
buf = buf[:chunkBlockBytes]
} else {
extended := make([]byte, chunkBlockBytes)
copy(extended, buf)
// fill rest deterministically (these would be e.g. credentials list)
for i := len(buf); i < chunkBlockBytes; i++ {
extended[i] = byte(i & 0xFF)
}
buf = extended
}
// CRITICAL: re-encode the message Size field so Parse accepts the 1 MiB
// length. (zap.HeaderSize layout: size at byte 12..16.)
buf[12] = byte(chunkBlockBytes & 0xFF)
buf[13] = byte((chunkBlockBytes >> 8) & 0xFF)
buf[14] = byte((chunkBlockBytes >> 16) & 0xFF)
buf[15] = byte((chunkBlockBytes >> 24) & 0xFF)
chunks := make([]chunk, chunkCount)
for i := range chunkCount {
c := chunk{
idx: i,
data: buf[i*chunkSize : (i+1)*chunkSize],
}
c.hash = sha256.Sum256(c.data)
chunks[i] = c
}
return chunks, buf
}
func makeChunkedBlockCodec() ([]chunk, []byte) {
// Build a 1 MiB codec-style buffer (no magic, just version+payload).
buf := make([]byte, chunkBlockBytes)
// Version 0,0 — codec.Manager accepts this.
buf[0] = 0
buf[1] = 0
// Fill with deterministic content
for i := 2; i < chunkBlockBytes; i++ {
buf[i] = byte(i & 0xFF)
}
chunks := make([]chunk, chunkCount)
for i := range chunkCount {
c := chunk{
idx: i,
data: buf[i*chunkSize : (i+1)*chunkSize],
}
c.hash = sha256.Sum256(c.data)
chunks[i] = c
}
return chunks, buf
}
// BenchmarkChunkReassemble_Codec_TTFV measures time-to-first-validation when
// chunks arrive out-of-order. Codec MUST wait for the last chunk because
// chunk N may carry an extension of struct N-1's variable-length field.
//
// NOTE: this bench's ns/op is dominated by the 1 MiB allocation + sha256
// over all 16 chunks. The TRUE differentiator (TTFV) is measured by
// BenchmarkChunkTTFV_{Codec,ZAP}_BlockingLatency below — they model the
// per-chunk arrival latency and only stop the clock when validation could
// fire.
func BenchmarkChunkReassemble_Codec_TTFV(b *testing.B) {
chunks, _ := makeChunkedBlockCodec()
indices := make([]int, len(chunks))
for i := range indices {
indices[i] = i
}
rng := rand.New(rand.NewSource(1))
b.ReportAllocs()
b.SetBytes(int64(chunkBlockBytes))
b.ResetTimer()
for n := 0; n < b.N; n++ {
// Shuffle chunks
rng.Shuffle(len(indices), func(a, b int) { indices[a], indices[b] = indices[b], indices[a] })
// Receive chunks in shuffled order, place into reassembled[idx].
reassembled := make([]byte, chunkBlockBytes)
received := make([]bool, chunkCount)
for _, idx := range indices {
// Verify hash (content-addressable receipt).
h := sha256.Sum256(chunks[idx].data)
if h != chunks[idx].hash {
b.Fatal("chunk hash mismatch")
}
copy(reassembled[idx*chunkSize:(idx+1)*chunkSize], chunks[idx].data)
received[idx] = true
// CODEC RULE: can NOT start validating until ALL chunks received.
// (chunk N may carry the tail of struct N-1's []byte field.)
}
// Now (and only now) can we attempt to validate.
// We're not unmarshaling the whole 1 MiB here — too slow for bench —
// we just confirm the version prefix is parseable. The real cost
// is the buffer-the-whole-thing latency, which is N chunks worth.
_ = reassembled[0]
_ = reassembled[1]
}
}
// BenchmarkChunkReassemble_ZAP_TTFV measures time-to-first-validation when
// chunks arrive out-of-order. ZAP can validate as soon as chunk 0 (the one
// carrying byte 0..15 = magic+version+flags+rootOffset+size) is placed.
func BenchmarkChunkReassemble_ZAP_TTFV(b *testing.B) {
chunks, _ := makeChunkedBlockZAP()
indices := make([]int, len(chunks))
for i := range indices {
indices[i] = i
}
rng := rand.New(rand.NewSource(1))
b.ReportAllocs()
b.SetBytes(int64(chunkBlockBytes))
b.ResetTimer()
for n := 0; n < b.N; n++ {
rng.Shuffle(len(indices), func(a, b int) { indices[a], indices[b] = indices[b], indices[a] })
reassembled := make([]byte, chunkBlockBytes)
received := make([]bool, chunkCount)
var headerValidated bool
for _, idx := range indices {
h := sha256.Sum256(chunks[idx].data)
if h != chunks[idx].hash {
b.Fatal("chunk hash mismatch")
}
copy(reassembled[idx*chunkSize:(idx+1)*chunkSize], chunks[idx].data)
received[idx] = true
// ZAP RULE: validate header as soon as chunk 0 is in place.
if !headerValidated && received[0] {
// Magic + version + size check — 16 bytes only.
if string(reassembled[0:4]) != "ZAP\x00" {
b.Fatal("bad magic")
}
headerValidated = true
// At this point we ALREADY know the buffer header is
// trustworthy. Field-level validation can proceed in
// parallel with the remaining chunk reception.
}
}
}
}
// BenchmarkChunkTTFV_Codec_BlockingLatency measures how many chunks must
// arrive before validation can START. For codec that number is 16 of 16
// (the whole buffer). We report the chunk-receipt count to first-validate.
func BenchmarkChunkTTFV_Codec_BlockingLatency(b *testing.B) {
chunks, _ := makeChunkedBlockCodec()
indices := make([]int, len(chunks))
for i := range indices {
indices[i] = i
}
rng := rand.New(rand.NewSource(1))
var totalChunksToFirstValidate int
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
rng.Shuffle(len(indices), func(a, b int) { indices[a], indices[b] = indices[b], indices[a] })
received := make([]bool, chunkCount)
chunksToFirstValidate := 0
for _, idx := range indices {
received[idx] = true
chunksToFirstValidate++
// Codec validation requires ALL chunks present
allReceived := true
for j := range chunkCount {
if !received[j] {
allReceived = false
break
}
}
if allReceived {
break
}
}
_ = chunks
totalChunksToFirstValidate += chunksToFirstValidate
}
b.StopTimer()
avg := float64(totalChunksToFirstValidate) / float64(b.N)
b.ReportMetric(avg, "chunks-to-validate")
}
// BenchmarkChunkTTFV_ZAP_BlockingLatency measures how many chunks must
// arrive before validation can start. For ZAP it's exactly 1 — the chunk
// carrying byte 0..15 (magic+header). On average over random delivery
// orders the expected value is (chunkCount+1)/2 = 8.5.
func BenchmarkChunkTTFV_ZAP_BlockingLatency(b *testing.B) {
chunks, _ := makeChunkedBlockZAP()
indices := make([]int, len(chunks))
for i := range indices {
indices[i] = i
}
rng := rand.New(rand.NewSource(1))
var totalChunksToFirstValidate int
b.ReportAllocs()
b.ResetTimer()
for n := 0; n < b.N; n++ {
rng.Shuffle(len(indices), func(a, b int) { indices[a], indices[b] = indices[b], indices[a] })
received := make([]bool, chunkCount)
chunksToFirstValidate := 0
for _, idx := range indices {
received[idx] = true
chunksToFirstValidate++
// ZAP can validate as soon as chunk 0 arrives.
if received[0] {
break
}
}
_ = chunks
totalChunksToFirstValidate += chunksToFirstValidate
}
b.StopTimer()
avg := float64(totalChunksToFirstValidate) / float64(b.N)
b.ReportMetric(avg, "chunks-to-validate")
}