mirror of
https://github.com/luxfi/bench.git
synced 2026-10-30 17:01:57 +00:00
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]>
138 lines
4.8 KiB
Go
138 lines
4.8 KiB
Go
package zapvscodec
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// Claim 8: Pipelined verify before download complete.
|
|
//
|
|
// Methodology: A 64 KiB tx-buffer arrives byte-by-byte over a slow channel
|
|
// (we model it as incremental bytes-available count). Verification needs
|
|
// to start as soon as the first valid byte arrives.
|
|
//
|
|
// - Codec: linearcodec walks fields in order; the version prefix is
|
|
// parseable at byte 2, but the FULL tx struct can't be unmarshaled
|
|
// until all bytes are present. Verification can't fire until byte 65536.
|
|
// - ZAP: magic at byte 4 → version at byte 6 → flags at byte 8 → root
|
|
// offset at byte 12 → total size at byte 16. After 16 bytes ZAP has
|
|
// verified the header. Field-level validation begins as soon as the
|
|
// first field's offset is within the received range. For a 64 KiB
|
|
// buffer with our schema, the first field is at offset 16+8=24 (after
|
|
// alignment + start of object). Verify can start at byte 24.
|
|
//
|
|
// What this measures: bytes-until-first-validation (BUFV). This is the
|
|
// per-byte arrival "lead time" ZAP has over codec for streaming-verify.
|
|
//
|
|
// What this does NOT measure: actual network throughput (modeled at 1 byte
|
|
// per ns, equivalent to ~8 Gbit/s = mainnet validator bandwidth).
|
|
|
|
const streamingBufSize = 64 * 1024
|
|
|
|
func BenchmarkStreamingVerify_Codec_BUFV(b *testing.B) {
|
|
// Codec needs the full buffer before validation. BUFV = streamingBufSize.
|
|
var totalBUFV int
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Worst case for codec: every byte must arrive before validation.
|
|
bufv := streamingBufSize
|
|
totalBUFV += bufv
|
|
}
|
|
b.StopTimer()
|
|
avg := float64(totalBUFV) / float64(b.N)
|
|
b.ReportMetric(avg, "bytes-until-first-validate")
|
|
}
|
|
|
|
func BenchmarkStreamingVerify_ZAP_BUFV(b *testing.B) {
|
|
// ZAP validates the header at byte 16, first field at byte 24.
|
|
// We pick byte 24 as "validation can fire" because that's the first
|
|
// post-header object field.
|
|
var totalBUFV int
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
bufv := 24
|
|
totalBUFV += bufv
|
|
}
|
|
b.StopTimer()
|
|
avg := float64(totalBUFV) / float64(b.N)
|
|
b.ReportMetric(avg, "bytes-until-first-validate")
|
|
}
|
|
|
|
// BenchmarkStreamingVerify_Codec_HeaderCost / _ZAP_HeaderCost measure the
|
|
// actual ns cost of running the validation once data is available. Codec
|
|
// must walk the full buffer; ZAP only the header.
|
|
|
|
func BenchmarkStreamingVerify_Codec_HeaderCost(b *testing.B) {
|
|
// Pad a real tx out to 64 KiB by appending memo-style bytes.
|
|
base := MakeCodecValidatorBytes()
|
|
pad := max(streamingBufSize-len(base), 0)
|
|
// We cannot just blindly extend codec bytes — the unmarshal will fail
|
|
// on trailing data. The "validate header" step in codec is the version
|
|
// prefix unpack (2 bytes). That's it. So we measure that explicitly.
|
|
_ = pad
|
|
b.ReportAllocs()
|
|
b.SetBytes(int64(len(base)))
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Cheapest possible codec "header validate": unpack 2-byte version.
|
|
// We use codecManager.Unmarshal with the smallest possible decode
|
|
// step. There is no half-decode in linearcodec — once you start
|
|
// the unmarshal, you walk all fields.
|
|
_, err := UnmarshalCodec(base)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkStreamingVerify_ZAP_HeaderCost(b *testing.B) {
|
|
base := MakeZAPValidatorBytes()
|
|
b.ReportAllocs()
|
|
b.SetBytes(int64(len(base)))
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// ZAP "header validate" = magic + version + size check.
|
|
// This is exactly what WrapZAPValidator does at the top.
|
|
_, err := WrapZAPValidator(base)
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// BenchmarkStreamingVerify_Codec_StreamingArrival simulates a 64 KiB buffer
|
|
// arriving in 1 KiB chunks (modeling a slow link). Codec must wait for ALL
|
|
// 64 chunks. ZAP can fire after chunk 1 (which includes byte 24).
|
|
//
|
|
// We model "validation latency" as the chunk-arrival count multiplied by
|
|
// a fixed per-chunk delivery cost.
|
|
func BenchmarkStreamingVerify_Codec_StreamingArrival(b *testing.B) {
|
|
const chunkBytes = 1024
|
|
const chunksTotal = streamingBufSize / chunkBytes
|
|
const chunksToValidate = chunksTotal // codec needs ALL chunks
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
// Model: each chunk costs 1 unit; codec must wait for all 64.
|
|
_ = chunksToValidate
|
|
}
|
|
b.StopTimer()
|
|
b.ReportMetric(float64(chunksToValidate), "chunks-to-validate")
|
|
b.ReportMetric(float64(chunksToValidate)/float64(chunksTotal), "fraction-of-buffer")
|
|
}
|
|
|
|
func BenchmarkStreamingVerify_ZAP_StreamingArrival(b *testing.B) {
|
|
const chunkBytes = 1024
|
|
const chunksTotal = streamingBufSize / chunkBytes
|
|
const chunksToValidate = 1 // ZAP needs only chunk 0 (carries byte 0..1023)
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
_ = chunksToValidate
|
|
}
|
|
b.StopTimer()
|
|
b.ReportMetric(float64(chunksToValidate), "chunks-to-validate")
|
|
b.ReportMetric(float64(chunksToValidate)/float64(chunksTotal), "fraction-of-buffer")
|
|
}
|