From 24af316bb48cd8e6a9818c4b26086df6a2573140 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sat, 12 Sep 2026 00:37:04 -0500 Subject: [PATCH 01/22] Update Narya to latest Zen 5 verification optimizations --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 4607b760..2463c0b1 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.26.4 replace github.com/gagliardetto/binary => github.com/palmerlao/binary v0.0.0-20250617062159-3054b4d33aed require ( - github.com/Overclock-Validator/narya-ed25519 v0.0.0-20260726222623-da0d045dae9d + github.com/Overclock-Validator/narya-ed25519 v0.0.0-20260730051143-c265ee966713 github.com/cespare/xxhash/v2 v2.3.0 github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 github.com/charmbracelet/bubbletea v1.3.10 diff --git a/go.sum b/go.sum index 069571ee..b5997c28 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/Overclock-Validator/gnark-crypto v0.0.0-20250309203346-2a67ed08a105 h github.com/Overclock-Validator/gnark-crypto v0.0.0-20250309203346-2a67ed08a105/go.mod h1:Poczuq3dbt+CwyTKgOjGaEwJOMP7YxQobF7QhgNcguk= github.com/Overclock-Validator/narya-ed25519 v0.0.0-20260726222623-da0d045dae9d h1:ipaL+9MHKeI8QIfWneId0VLa+STLRM1e6MnvZhQyhPU= github.com/Overclock-Validator/narya-ed25519 v0.0.0-20260726222623-da0d045dae9d/go.mod h1:B7/xqV/5NtGJa8OlZAa9TRMHgeIE+VEJNiPzMP4FrIg= +github.com/Overclock-Validator/narya-ed25519 v0.0.0-20260730051143-c265ee966713 h1:nRAD+snanlR/sX4W5rkrxr6+sYBZ3Hl2xZQ6HCpM0Hc= +github.com/Overclock-Validator/narya-ed25519 v0.0.0-20260730051143-c265ee966713/go.mod h1:B7/xqV/5NtGJa8OlZAa9TRMHgeIE+VEJNiPzMP4FrIg= github.com/Overclock-Validator/solana-snapshot-finder-go v0.0.0-20260223201452-d8363b514fc0 h1:elgavEQb8l7Zn3gS3Y+2/98PlUylOWdlM3V1VumQ7mA= github.com/Overclock-Validator/solana-snapshot-finder-go v0.0.0-20260223201452-d8363b514fc0/go.mod h1:XbqbvMA2NKeosY0w3WdBOpCg2eYJesBjfE4cNt9HSE8= github.com/Overclock-Validator/wide v0.0.0-20250221123529-f80959d02044 h1:ph9gnWIY116AWT/iCfXoPe9/cn2aWx2uJBuLdf/LyEE= From 5085453f4ddf6b59895ea44762a416dcc0b3c66f Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sat, 12 Sep 2026 01:20:54 -0500 Subject: [PATCH 02/22] Overlap Turbine signature verification with shred arrival --- cmd/mithril/node/node.go | 19 +- cmd/mithril/node/sigverify_reporter.go | 3 +- config.example.toml | 35 +- docs/transaction_sigverify_streaming.md | 96 ++++ pkg/block/block.go | 17 +- pkg/block/block_test.go | 7 +- pkg/metrics/metrics.go | 13 +- pkg/replay/block.go | 5 + pkg/sigverify/config_policy_test.go | 63 +++ pkg/sigverify/sigverify.go | 70 ++- pkg/statsd/statsd.go | 21 + pkg/statsd/statsd_test.go | 9 + pkg/turbine/assembler.go | 43 +- pkg/turbine/cancellation_regression_test.go | 67 +-- pkg/turbine/entries.go | 116 +++-- pkg/turbine/entries_prefetch_test.go | 146 ++++++ pkg/turbine/entry_prefetch.go | 343 +++++++++++++ pkg/turbine/entry_prefetch_benchmark_test.go | 217 ++++++++ pkg/turbine/entry_prefetch_bounds_test.go | 50 ++ pkg/turbine/entry_prefetch_test.go | 360 +++++++++++++ pkg/turbine/receiver.go | 30 +- .../receiver_prefetch_lifecycle_test.go | 116 +++++ pkg/turbine/transaction_verifier.go | 348 +++++++++---- ...ransaction_verifier_flow_benchmark_test.go | 479 ++++++++++++++++++ pkg/turbine/transaction_verifier_test.go | 298 ++++++++++- 25 files changed, 2733 insertions(+), 238 deletions(-) create mode 100644 docs/transaction_sigverify_streaming.md create mode 100644 pkg/sigverify/config_policy_test.go create mode 100644 pkg/turbine/entries_prefetch_test.go create mode 100644 pkg/turbine/entry_prefetch.go create mode 100644 pkg/turbine/entry_prefetch_benchmark_test.go create mode 100644 pkg/turbine/entry_prefetch_bounds_test.go create mode 100644 pkg/turbine/entry_prefetch_test.go create mode 100644 pkg/turbine/receiver_prefetch_lifecycle_test.go create mode 100644 pkg/turbine/transaction_verifier_flow_benchmark_test.go diff --git a/cmd/mithril/node/node.go b/cmd/mithril/node/node.go index c452d494..e23d9682 100644 --- a/cmd/mithril/node/node.go +++ b/cmd/mithril/node/node.go @@ -560,6 +560,12 @@ func init() { Run.Flags().StringVar(&snapshot.SnapshotIndexTempDir, "snapshot-index-temp-dir", "", "Optional directory for snapshot index shard logs/SST staging") Run.Flags().StringVar(&sigverify.Cfg.Backend, "sigverify-backend", sigverify.Defaults().Backend, "ed25519 verification backend: auto|r51|generic|stdlib") + Run.Flags().IntVar(&sigverify.Cfg.Workers, "sigverify-workers", 0, + "Turbine transaction signature verification workers (0 = min(2, GOMAXPROCS))") + Run.Flags().IntVar(&sigverify.Cfg.BatchTarget, "sigverify-batch-target", sigverify.Defaults().BatchTarget, + "Turbine transaction signature batch target: 4 or 8 (available short batches run immediately)") + Run.Flags().BoolVar(&sigverify.Cfg.DisableShredOverlap, "sigverify-disable-shred-overlap", false, + "Defer Turbine transaction decoding and signature verification until all block shreds arrive") Run.Flags().BoolVar(&sbpf.UsePool, "use-pool", true, "Disable to allocate fresh slices") Run.Flags().IntVar(&accountsdb.StoreAccountsWorkers, "store-accounts-workers", 128, "Number of workers to write account updates") Run.Flags().IntVar(&accountsdb.ProgramCacheMaxMB, "program-cache-max-mb", accountsdb.DefaultProgramCacheMaxMB, "Maximum approximate SBPF program cache size in MiB") @@ -1127,10 +1133,17 @@ func initConfigAndBindFlags(cmd *cobra.Command) error { // later: narya pins its backend on first use, and selecting it explicitly // doubles as a startup health check, so a machine that cannot run the // requested backend fails now instead of at the first block. - sigverify.Cfg.Backend = getString("sigverify-backend", "tuning.sigverify_backend") + backendKey := "sigverify.backend" + if !config.IsSet(backendKey) { + backendKey = "tuning.sigverify_backend" // older configuration files + } + sigverify.Cfg.Backend = getString("sigverify-backend", backendKey) + sigverify.Cfg.Workers = getInt("sigverify-workers", "sigverify.workers") + sigverify.Cfg.BatchTarget = getInt("sigverify-batch-target", "sigverify.batch_target") + sigverify.Cfg.DisableShredOverlap = getBool("sigverify-disable-shred-overlap", "sigverify.disable_shred_overlap") resolved, err := sigverify.Configure(sigverify.Cfg) if err != nil { - return fmt.Errorf("tuning.sigverify_backend: %w", err) + return fmt.Errorf("signature verification configuration: %w", err) } resolvedSigverifyBackend = resolved sbpf.UsePool = getBool("use-pool", "tuning.use_pool") @@ -3214,6 +3227,8 @@ func printStartupInfo(commandName string) { } fmt.Printf(" Sigverify: %s%s%s %s(%s)%s\n", green, resolvedSigverifyBackend, reset, dim, sigverifyDesc, reset) + fmt.Printf(" workers=%d batch_target=%d shred_overlap=%t\n", + sigverify.TransactionWorkers(), sigverify.TransactionBatchTarget(), !sigverify.Cfg.DisableShredOverlap) } // Load state file for detailed info (only show for modes that use existing AccountsDB) diff --git a/cmd/mithril/node/sigverify_reporter.go b/cmd/mithril/node/sigverify_reporter.go index d374d93b..597a3c11 100644 --- a/cmd/mithril/node/sigverify_reporter.go +++ b/cmd/mithril/node/sigverify_reporter.go @@ -42,7 +42,8 @@ func startSigverifyReporter(ctx context.Context) { // against, and so the resolved backend is recorded even on a node that // exits before the first tick. previous := sigverify.Stats() - mlog.NamedFilef("sigverify", "startup: %s", previous) + mlog.NamedFilef("sigverify", "startup: %s workers=%d batch_target=%d shred_overlap=%t", previous, + sigverify.TransactionWorkers(), sigverify.TransactionBatchTarget(), !sigverify.Cfg.DisableShredOverlap) for { select { diff --git a/config.example.toml b/config.example.toml index 7e383308..2636e1b1 100644 --- a/config.example.toml +++ b/config.example.toml @@ -524,14 +524,6 @@ name = "mithril" # Number of borrowed accounts to preallocate in arena (0 to disable) borrowed_account_arena_size = 1024 - # ed25519 signature verification backend. - # auto - use the AVX-512 accelerated backend when the CPU has - # AVX512-IFMA (Zen 4/5, Ice Lake and newer), else portable - # r51 - force the accelerated backend; startup fails without AVX512-IFMA - # generic - force the portable pure-Go backend - # stdlib - use Go’s crypto/ed25519 implementation after the mandatory strict rejection checks. - sigverify_backend = "auto" - # Enable/disable pool allocator for slices use_pool = true @@ -574,6 +566,33 @@ name = "mithril" # Filename to write CPU profile (for offline analysis with go tool pprof) # cpu_profile_path = "/mnt/mithril-data/profiling/cpu.pprof" +# ============================================================================ +# [sigverify] - Transaction Signature Verification +# ============================================================================ + +[sigverify] + # ed25519 backend: auto selects AVX-512 IFMA when available, else portable. + # r51 requires AVX-512 IFMA; generic and stdlib force portable backends. + # Strict signature checks are always enabled. The older + # tuning.sigverify_backend key remains supported when this key is absent. + backend = "auto" + + # Shared Turbine transaction verification workers; 0 = min(2, GOMAXPROCS). + # Leaves execution and shred/consensus processing room to run concurrently. + # This does not change validator.tpu_sigverify_workers or replay's fallback pool. + workers = 0 + + # Signature lanes per group: 4 or 8 (0 also means 8). Transactions stay + # indivisible, so a multisignature transaction may exceed this target. + # Ready short groups run immediately; no timer waits for more shreds. + batch_target = 8 + + # Decode complete entry batches and verify while later shreds arrive. + # Set true to compare against completion-only verification. + # Early work reserves at most 8 slots and 64 MiB of encoded component bytes; + # decoded transactions and Go bookkeeping use additional heap memory. + disable_shred_overlap = false + # ============================================================================ # [debug] - Debug Logging # ============================================================================ diff --git a/docs/transaction_sigverify_streaming.md b/docs/transaction_sigverify_streaming.md new file mode 100644 index 00000000..fc29745b --- /dev/null +++ b/docs/transaction_sigverify_streaming.md @@ -0,0 +1,96 @@ +# Transaction signature verification during shred arrival + +Turbine verifies transaction signatures as complete entry batches arrive. The +default shared transaction pool has two workers and targets eight signature +lanes. A ready batch containing four transactions runs immediately; there is no +timer or minimum occupancy requirement. The same policy handles live reception +and repair catch-up without a mode transition or a 200 ms batching delay. + +## Order of work + +1. The receiver authenticates the shred and performs existing assembly/FEC + recovery. The packet reader advances a contiguous data-shred frontier and + attempts a nonblocking background enqueue. +2. Two background preparation workers copy and decode complete DATA_COMPLETE + entry batches. A batch may span several FEC sets. They submit its immutable + transactions to the shared signature pool while later shreds arrive. +3. Signature groups contain available transactions up to the configured lane + target. Transactions with multiple signatures remain indivisible. A rolling + per-request window refills when any group finishes, without a wave barrier. +4. Once the full slot is assembled, completion rechecks exact batch bytes, + processes all Alpenglow markers and FEC roots, and constructs the final ordered + block. It submits any transactions not already covered and joins signature + work for the retained batches before marking the block verified. +5. The existing replay pipeline receives the verified block. This change does + not execute transactions before full-slot admission, alter execution batching, + or move parent-dependent validation ahead of its required state. + +The 200 ms slot interval provides an opportunity to overlap work. It is not a +mandatory local wait: replay can continue as soon as the block is available and +its checks complete. If all shreds arrive in a burst during catch-up, full-slot +completion uses the same efficient groups with whatever early work was able to +start. Four workers can improve catch-up latency but occupy more cores at once. + +## Bounds and correctness + +- At most eight slot generations hold early preparation reservations, with a + combined 64 MiB budget for raw component bytes and a 1 MiB per-component limit. + Decoded transaction objects add heap overhead. Saturation skips optional early + work; normal full-slot verification still covers every retained transaction. +- One queued/active preparation token per generation coalesces packet arrivals. + Verifier requests and each request's outstanding groups are also bounded. + Request admission can wait behind existing requests; this is not a strict + replay-head priority scheduler. +- No transaction decoding or verifier admission occurs under the assembler mutex or on the + packet reader. A gap prevents early component decoding until recovery or + arrival closes it. Duplicate shreds do not create duplicate requests. +- Cached results belong to one generation, shred range and exact byte sequence. + Reset/eviction cancels that generation. Reservations remain charged until + admitted readers have relinquished their transaction buffers. +- `UpdateParent` can discard an optimistic prefix. Parse and marker checks still + cover that prefix, while its transaction signature verdict is discarded along + with its transactions. Retained signatures must all pass before replay. +- Cancellation is not an invalid-signature verdict. A retry on the same slot + generation verifies transactions again if an earlier request was canceled. + +## Configuration and observability + +```toml +[sigverify] +backend = "auto" +workers = 0 # min(2, GOMAXPROCS) +batch_target = 8 # 4 or 8; short groups never wait to fill +disable_shred_overlap = false +``` + +Equivalent CLI flags are `--sigverify-workers`, `--sigverify-batch-target` and +`--sigverify-disable-shred-overlap`. These settings apply to Turbine transaction +signatures; TPU, shred signatures, consensus BLS and replay's fallback verifier +retain their existing configuration. + +Use `TurbineFullToReady` to measure residual wall time after the slot becomes +complete. `TurbineEarlyVerifiedTransactions` counts retained transactions whose +verification finished before full assembly. `TurbineTransactionSigverify` now +measures completion's outstanding-signature join/fallback work. The remaining +wait for an already claimed background preparation job, including +any outstanding admission delay, appears in `TurbineEarlyPreparationWait`, +separately from active completion decoding. It does not sum every background +admission wait. Early parse and signature durations are component sums observed +at completion; +a discarded prefix still being verified is not included. These durations +overlap reception and each other and must not be added as sequential stages +or interpreted as CPU time. + +## Benchmark scope + +`BenchmarkTransactionVerificationFlow` compares two/four workers and targets +four/eight using catch-up, synthetic 200 ms component arrivals, and sparse +four/seven/eight-transaction components. It supports captured public transaction +fixtures and generated, distinct valid transactions of exactly 228 or 1,232 wire +bytes. Decoding and fixture construction are outside those pool measurements. + +The execution contention probe runs the real transfer load/execute benchmark +alongside signature work on the same eight physical cores. Its separate +processes measure hardware/OS contention, excluding shared Go scheduler/heap +effects, full-block dependency planning, commit and live network timing. Pool +throughput alone is insufficient evidence of end-to-end replay improvement. diff --git a/pkg/block/block.go b/pkg/block/block.go index 23bfd1f0..459fd68d 100644 --- a/pkg/block/block.go +++ b/pkg/block/block.go @@ -14,15 +14,30 @@ import ( "github.com/gagliardetto/solana-go/rpc" ) -// TurbineIngressTimings is the per-slot decomposition carried only by a +// TurbineIngressTimings carries per-slot observations only on a // trusted in-memory Turbine block. Durations never serialize with Block. type TurbineIngressTimings struct { ShredCollection time.Duration CompletionQueueDelay time.Duration BlockDecode time.Duration + // Completion-only parse and outstanding-signature join/verification time. TransactionParse time.Duration TransactionSigverify time.Duration ReplayAdmission time.Duration + // Early durations sum completed prefetched component work, including an + // optimistic prefix later discarded, and overlap reception and each other. + // EarlyTransactionSigverify includes queueing through future completion; + // neither early duration is CPU time or an additive pipeline wall stage. + EarlyTransactionParse time.Duration + EarlyTransactionSigverify time.Duration + // Completion wait for already-claimed background parsing/submission. + // Recorded separately from BlockDecode's active completion work. + EarlyPreparationWait time.Duration + // Only retained transactions whose verification finished by ShredFullNanos. + EarlyVerifiedTransactions uint64 + // FullToReady is wall time from full shred assembly to replay-ready completion. + // It contains completion queueing, decode and outstanding verification waits. + FullToReady time.Duration } var transactionDerivedStateInitMu sync.Mutex diff --git a/pkg/block/block_test.go b/pkg/block/block_test.go index 2ffcd302..31ac89e6 100644 --- a/pkg/block/block_test.go +++ b/pkg/block/block_test.go @@ -11,7 +11,12 @@ func TestTransactionSignaturesVerifiedMarkerIsNotSerialized(t *testing.T) { original.MarkTransactionSignaturesVerified() admissionStart := time.Now() original.MarkTurbineReplayAdmissionStart(admissionStart) - ingress := TurbineIngressTimings{ShredCollection: 12 * time.Millisecond, TransactionSigverify: 34 * time.Millisecond} + ingress := TurbineIngressTimings{ + ShredCollection: 12 * time.Millisecond, TransactionSigverify: 34 * time.Millisecond, + EarlyTransactionParse: 2 * time.Millisecond, EarlyTransactionSigverify: 56 * time.Millisecond, + EarlyPreparationWait: 3 * time.Millisecond, + EarlyVerifiedTransactions: 80, FullToReady: 35 * time.Millisecond, + } original.MarkTurbineIngressTimings(ingress) if got, ok := original.TurbineIngressTimings(); !ok || got != ingress { t.Fatalf("ingress timings = %+v, %t; want %+v, true", got, ok, ingress) diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 10548fdd..fee3c92f 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -94,15 +94,26 @@ type AccountLoader struct { SysvarCachePublicationEpochRejects uint64 } -// TurbineIngress is the exact per-slot pre-replay pipeline decomposition. +// TurbineIngress records per-slot pre-replay pipeline observations. // It is written to replay_timings.jsonl without high-cardinality metric labels. type TurbineIngress struct { ShredCollection Timing CompletionQueueDelay Timing BlockDecode Timing + // Completion-only parse and outstanding-signature join/verification time. TransactionParse Timing TransactionSigverify Timing ReplayAdmission Timing + // Summed completed prefetched component durations, including any discarded + // optimistic prefix. Overlap reception; not CPU time or additive wall stages. + // Early sigverify includes queueing. + EarlyTransactionParse Timing + EarlyTransactionSigverify Timing + // Completion wait for claimed background parsing/submission, outside BlockDecode. + EarlyPreparationWait Timing + EarlyVerifiedTransactions uint64 + // FullToReady contains the completion stages above, excluding admission. + FullToReady Timing } // VoteRewardDetails decomposes RewardCertificatePreflight and diff --git a/pkg/replay/block.go b/pkg/replay/block.go index c45c6cb9..ce618a4d 100644 --- a/pkg/replay/block.go +++ b/pkg/replay/block.go @@ -2795,6 +2795,11 @@ func ReplayBlocks( record.TransactionParse.AddTiming(ingressTimings.TransactionParse) record.TransactionSigverify.AddTiming(ingressTimings.TransactionSigverify) record.ReplayAdmission.AddTiming(ingressTimings.ReplayAdmission) + record.EarlyTransactionParse.AddTiming(ingressTimings.EarlyTransactionParse) + record.EarlyTransactionSigverify.AddTiming(ingressTimings.EarlyTransactionSigverify) + record.EarlyPreparationWait.AddTiming(ingressTimings.EarlyPreparationWait) + record.EarlyVerifiedTransactions = ingressTimings.EarlyVerifiedTransactions + record.FullToReady.AddTiming(ingressTimings.FullToReady) } start := time.Now() diff --git a/pkg/sigverify/config_policy_test.go b/pkg/sigverify/config_policy_test.go new file mode 100644 index 00000000..52e8948c --- /dev/null +++ b/pkg/sigverify/config_policy_test.go @@ -0,0 +1,63 @@ +package sigverify + +import ( + "runtime" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestResolveConfigPolicyDefaultsAndOverrides(t *testing.T) { + zero, err := ResolveConfig(Config{}) + require.NoError(t, err) + require.Equal(t, BackendAuto, zero.Backend) + require.Equal(t, min(2, runtime.GOMAXPROCS(0)), zero.Workers) + require.Equal(t, 8, zero.BatchTarget) + require.False(t, zero.DisableShredOverlap) + defaults, err := ResolveConfig(Defaults()) + require.NoError(t, err) + require.Equal(t, zero, defaults) + + override := Config{Backend: BackendGeneric, Workers: 4, BatchTarget: 4, DisableShredOverlap: true} + resolved, err := ResolveConfig(override) + require.NoError(t, err) + require.Equal(t, override, resolved) +} + +func TestInvalidPolicyDoesNotLatchBackend(t *testing.T) { + if !inChild() { + out, err := runConfigureChild(t, t.Name()) + require.NoError(t, err, "child output:\n%s", out) + require.Contains(t, out, "PASS") + return + } + before := Cfg + for _, cfg := range []Config{ + {Backend: BackendGeneric, Workers: -1}, + {Backend: BackendGeneric, BatchTarget: -1}, + {Backend: BackendGeneric, BatchTarget: 3}, + {Backend: BackendGeneric, BatchTarget: 16}, + } { + _, err := Configure(cfg) + require.Error(t, err) + require.Equal(t, before, Cfg, "invalid policy must not become live") + require.Empty(t, configuredBackend, "invalid policy must not latch the backend") + } + _, err := Configure(Config{Backend: BackendGeneric, Workers: 2, BatchTarget: 4}) + require.NoError(t, err, "valid configuration must remain possible after rejection") + require.Equal(t, 2, TransactionWorkers()) + require.Equal(t, 4, TransactionBatchTarget()) +} + +func TestTransactionPolicyWithoutConfigure(t *testing.T) { + if !inChild() { + out, err := runConfigureChild(t, t.Name()) + require.NoError(t, err, "child output:\n%s", out) + require.Contains(t, out, "PASS") + return + } + Cfg = Config{} + require.Equal(t, min(2, runtime.GOMAXPROCS(0)), TransactionWorkers()) + require.Equal(t, 8, TransactionBatchTarget()) + require.False(t, Cfg.DisableShredOverlap) +} diff --git a/pkg/sigverify/sigverify.go b/pkg/sigverify/sigverify.go index fae00447..a0502712 100644 --- a/pkg/sigverify/sigverify.go +++ b/pkg/sigverify/sigverify.go @@ -20,6 +20,7 @@ package sigverify import ( "fmt" + "runtime" "sync" narya "github.com/Overclock-Validator/narya-ed25519/ed25519" @@ -40,15 +41,60 @@ const ( BackendStdlib = "stdlib" ) -// Config selects the verification backend. It is deliberately tiny: the -// library's own defaults are good, and every knob here is a consensus-visible -// or performance-visible choice that an operator should have to state. +// Config selects the backend and the Turbine transaction-verification policy. +// Worker and batching settings do not change the TPU or fallback replay pools. type Config struct { - Backend string + Backend string + Workers int + BatchTarget int + DisableShredOverlap bool } // Defaults returns the configuration used when the operator sets nothing. -func Defaults() Config { return Config{Backend: BackendAuto} } +func Defaults() Config { return Config{Backend: BackendAuto, BatchTarget: BatchTarget} } + +// ResolveConfig validates every setting before the one-shot backend selection. +// Zero workers selects at most two transaction-verification workers; zero batch +// target uses eight signatures. Available short batches are never held to fill. +func ResolveConfig(cfg Config) (Config, error) { + if cfg.Backend == "" { + cfg.Backend = BackendAuto + } + switch cfg.Backend { + case BackendAuto, BackendR51, BackendGeneric, BackendStdlib: + default: + return Config{}, fmt.Errorf("sigverify.backend must be one of %q, %q, %q, %q; got %q", BackendAuto, BackendR51, BackendGeneric, BackendStdlib, cfg.Backend) + } + if cfg.Workers < 0 { + return Config{}, fmt.Errorf("sigverify.workers must be >= 0; got %d", cfg.Workers) + } + if cfg.Workers == 0 { + cfg.Workers = min(2, max(1, runtime.GOMAXPROCS(0))) + } + if cfg.BatchTarget == 0 { + cfg.BatchTarget = BatchTarget + } + if cfg.BatchTarget != 4 && cfg.BatchTarget != 8 { + return Config{}, fmt.Errorf("sigverify.batch_target must be 4 or 8 (0 uses 8); got %d", cfg.BatchTarget) + } + return cfg, nil +} + +// TransactionWorkers also supports callers that do not run node Configure. +func TransactionWorkers() int { + if Cfg.Workers > 0 { + return Cfg.Workers + } + return min(2, max(1, runtime.GOMAXPROCS(0))) +} + +// TransactionBatchTarget also supports the zero configuration outside startup. +func TransactionBatchTarget() int { + if Cfg.BatchTarget == 4 { + return 4 + } + return BatchTarget +} // Cfg is the live configuration, set once by Configure during startup and // read-only afterwards. It follows the same shape as replay.TrailingVerifierCfg. @@ -64,10 +110,6 @@ var Cfg = Defaults() // underlying library pins its backend on first use and a late switch would // leave the process in a state neither caller asked for. func Configure(cfg Config) (string, error) { - if cfg.Backend == "" { - cfg.Backend = Defaults().Backend - } - configureMu.Lock() defer configureMu.Unlock() @@ -79,12 +121,10 @@ func Configure(cfg Config) (string, error) { // Validate before publishing anything. Assigning Cfg first would leave a // rejected backend name visible to Backend() and to the startup log. - switch cfg.Backend { - case BackendAuto, BackendR51, BackendGeneric, BackendStdlib: - default: - return "", fmt.Errorf( - "sigverify.backend must be one of %q, %q, %q, %q; got %q", - BackendAuto, BackendR51, BackendGeneric, BackendStdlib, cfg.Backend) + var err error + cfg, err = ResolveConfig(cfg) + if err != nil { + return "", err } resolved, err := installBackend(cfg.Backend) diff --git a/pkg/statsd/statsd.go b/pkg/statsd/statsd.go index bbf3eea9..828d3cec 100644 --- a/pkg/statsd/statsd.go +++ b/pkg/statsd/statsd.go @@ -124,6 +124,13 @@ var ( TurbineBlockDecode = Metric{"turbine_block_decode_duration_seconds"} TurbineTransactionParse = Metric{"turbine_transaction_parse_duration_seconds"} TurbineTransactionSigverify = Metric{"turbine_transaction_sigverify_duration_seconds"} + // Early durations sum elapsed component work, including verifier queueing; + // they overlap shred collection and are neither CPU nor pipeline wall time. + TurbineEarlyTransactionParse = Metric{"turbine_early_transaction_parse_duration_seconds"} + TurbineEarlyTransactionSigverify = Metric{"turbine_early_transaction_sigverify_elapsed_seconds"} + TurbineEarlyPreparationWait = Metric{"turbine_early_preparation_wait_seconds"} + TurbineEarlyVerifiedTransactions = Metric{"turbine_early_verified_transactions_total"} + TurbineFullToReady = Metric{"turbine_full_to_ready_duration_seconds"} // ReplaySigverifyGroup times one drained group of transaction signatures // and ReplaySigverifyGroupSignatures counts how many signatures were in it. // The pair is what tells an operator whether batching is actually happening: @@ -245,6 +252,11 @@ var MetricToType = map[Metric]metricType{ TurbineBlockDecode: TimingT, TurbineTransactionParse: TimingT, TurbineTransactionSigverify: TimingT, + TurbineEarlyTransactionParse: TimingT, + TurbineEarlyTransactionSigverify: TimingT, + TurbineEarlyPreparationWait: TimingT, + TurbineEarlyVerifiedTransactions: CountT, + TurbineFullToReady: TimingT, ReplaySigverifyGroup: TimingT, ReplaySigverifyGroupSignatures: CountT, TurbineReplayAdmission: TimingT, @@ -353,6 +365,11 @@ var MetricToLabels = map[Metric][]string{ TurbineBlockDecode: {}, TurbineTransactionParse: {}, TurbineTransactionSigverify: {}, + TurbineEarlyTransactionParse: {}, + TurbineEarlyTransactionSigverify: {}, + TurbineEarlyPreparationWait: {}, + TurbineEarlyVerifiedTransactions: {}, + TurbineFullToReady: {}, ReplaySigverifyGroup: {}, ReplaySigverifyGroupSignatures: {}, TurbineReplayAdmission: {}, @@ -390,6 +407,10 @@ var MetricToBuckets = map[Metric][]float64{ TurbineBlockDecode: turbinePipelineDurationBuckets, TurbineTransactionParse: turbinePipelineDurationBuckets, TurbineTransactionSigverify: turbinePipelineDurationBuckets, + TurbineEarlyTransactionParse: turbinePipelineDurationBuckets, + TurbineEarlyTransactionSigverify: turbinePipelineDurationBuckets, + TurbineEarlyPreparationWait: turbinePipelineDurationBuckets, + TurbineFullToReady: turbinePipelineDurationBuckets, ReplaySigverifyGroup: turbinePipelineDurationBuckets, TurbineReplayAdmission: turbinePipelineDurationBuckets, AlpenglowVoteRewards: turbinePipelineDurationBuckets, diff --git a/pkg/statsd/statsd_test.go b/pkg/statsd/statsd_test.go index 830ea99e..6ec3cd0c 100644 --- a/pkg/statsd/statsd_test.go +++ b/pkg/statsd/statsd_test.go @@ -203,6 +203,10 @@ func TestTurbinePipelineDurationMetricsUseSecondsAndBoundedSchema(t *testing.T) TurbineBlockDecode, TurbineTransactionParse, TurbineTransactionSigverify, + TurbineEarlyTransactionParse, + TurbineEarlyTransactionSigverify, + TurbineEarlyPreparationWait, + TurbineFullToReady, TurbineReplayAdmission, } duration := 25 * time.Millisecond @@ -228,6 +232,11 @@ func TestTurbinePipelineDurationMetricsUseSecondsAndBoundedSchema(t *testing.T) } } +func TestTurbineEarlyVerifiedTransactionsHasBoundedCountSchema(t *testing.T) { + assert.Equal(t, CountT, MetricToType[TurbineEarlyVerifiedTransactions]) + assert.Equal(t, []string{}, MetricToLabels[TurbineEarlyVerifiedTransactions]) +} + func TestBlockProductionMetricLabelsStayBounded(t *testing.T) { assert.Equal(t, []string{"outcome", "reason"}, MetricToLabels[BlockProductionLeaderSlots]) assert.Equal(t, []string{"outcome", "terminal", "cause"}, MetricToLabels[BlockProductionLeaderSlotTerminals]) diff --git a/pkg/turbine/assembler.go b/pkg/turbine/assembler.go index 9b69f437..b558e058 100644 --- a/pkg/turbine/assembler.go +++ b/pkg/turbine/assembler.go @@ -72,6 +72,7 @@ type SlotAssembler struct { // Configured before ingestion; tests may replace it with a blocking probe. // Production uses the process-wide bounded transaction verifier. verifyTransactions func(context.Context, *block.Block) error + entryPrefetch *entryPrefetchPool } type SlotRepairRequest struct { @@ -106,7 +107,10 @@ type slotState struct { fullAt time.Time repairedShreds int // completing makes the immutable full state a single-owner generation token. - completing bool + completing bool + batchScan, batchStart uint32 + completeBatches []shredBatchRange + prefetch *slotEntryPrefetch // Assembly failures for this slot (mixed variants/signatures, FEC layout // conflicts, ...). A slot frozen below completion while repair responses // flow is usually poisoned state — the latest error names the poison. @@ -174,7 +178,6 @@ func NewSlotAssembler() *SlotAssembler { priorityRepairSlots: make(map[uint64]struct{}), partialShredObs: make(map[uint64]PartialShredObservation), encoders: make(map[fecLayout]reedsolomon.Encoder), - verifyTransactions: validateBlockTransactionsContext, } } @@ -307,6 +310,7 @@ func (a *SlotAssembler) addShredFrom(shred *Shred, fromRepair bool) (*slotComple } } + a.prefetchEntriesLocked(state) if !state.complete() { return nil, nil } @@ -374,19 +378,27 @@ func (a *SlotAssembler) processCompletion(ctx context.Context, work *slotComplet } decodeStartedAt := time.Now() - var decodeTimings entryDecodeTimings + decodeTimings := entryDecodeTimings{ctx: ctx} + if work.state.prefetch != nil { + decodeTimings.prefetched = work.state.prefetch.batches + } blk, parentInfo, roots, err := work.state.decodeBlock(&decodeTimings) decodeTotal := time.Since(decodeStartedAt) - decodeOnly := decodeTotal - decodeTimings.transactionParse + decodeOnly := decodeTotal - decodeTimings.transactionParse - decodeTimings.prefetchWait if decodeOnly < 0 { decodeOnly = 0 } timings.BlockDecode = decodeOnly timings.TransactionParse = decodeTimings.transactionParse + timings.EarlyPreparationWait = decodeTimings.prefetchWait _ = statsd.Duration(statsd.TurbineBlockDecode, timings.BlockDecode, nil) _ = statsd.Duration(statsd.TurbineTransactionParse, timings.TransactionParse, nil) processed := processedSlotCompletion{block: blk, parentInfo: parentInfo, roots: roots, err: err, timings: timings} if err != nil { + if ctx.Err() != nil { + processed.canceled = true + processed.err = nil + } return processed } if ctx.Err() != nil { @@ -395,7 +407,12 @@ func (a *SlotAssembler) processCompletion(ctx context.Context, work *slotComplet } sigverifyStartedAt := time.Now() - processed.err = work.verifyTransactions(ctx, blk) + if work.state.prefetch != nil && len(decodeTimings.retained) > 0 { + processed.err = verifyDecodedEntryBatches(ctx, blk, decodeTimings.retained, work.state.prefetch.pool.verifier) + } else { + processed.err = work.verifyTransactions(ctx, blk) + } + earlyEntryTimings(&decodeTimings, work.state.fullAt, &processed.timings) processed.timings.TransactionSigverify = time.Since(sigverifyStartedAt) _ = statsd.Duration(statsd.TurbineTransactionSigverify, processed.timings.TransactionSigverify, nil) if ctx.Err() != nil { @@ -406,6 +423,12 @@ func (a *SlotAssembler) processCompletion(ctx context.Context, work *slotComplet if processed.err == nil { blk.MarkTransactionSignaturesVerified() processed.completionReadyAt = time.Now() + processed.timings.FullToReady = processed.completionReadyAt.Sub(work.state.fullAt) + _ = statsd.Duration(statsd.TurbineFullToReady, processed.timings.FullToReady, nil) + _ = statsd.Duration(statsd.TurbineEarlyPreparationWait, processed.timings.EarlyPreparationWait, nil) + _ = statsd.Duration(statsd.TurbineEarlyTransactionParse, processed.timings.EarlyTransactionParse, nil) + _ = statsd.Duration(statsd.TurbineEarlyTransactionSigverify, processed.timings.EarlyTransactionSigverify, nil) + _ = statsd.Count(statsd.TurbineEarlyVerifiedTransactions, int64(processed.timings.EarlyVerifiedTransactions), nil) } return processed } @@ -420,6 +443,11 @@ func (a *SlotAssembler) finalizeCompletion(work *slotCompletionWork, processed p a.mu.Unlock() return nil, nil } + if processed.canceled { + state.completing = false + a.mu.Unlock() + return nil, nil + } if processed.err != nil { // Retain a deterministic decode/verification failure on the live full // state so catchup diagnostics report poison instead of a missing slot. @@ -434,6 +462,7 @@ func (a *SlotAssembler) finalizeCompletion(work *slotCompletionWork, processed p if !a.acceptAlpenglowBlockIDLocked(blk) { a.trackNonCanonicalBlockIDLocked(blk) a.recordPartialObsLocked(state) + a.releasePrefetchLocked(state) delete(a.slots, state.slot) a.mu.Unlock() if work.reportNonCanonical { @@ -442,6 +471,7 @@ func (a *SlotAssembler) finalizeCompletion(work *slotCompletionWork, processed p return nil, nil } + a.releasePrefetchLocked(state) delete(a.slots, state.slot) a.completedSlots[state.slot] = struct{}{} a.trackBlockIDLocked(blk) @@ -542,6 +572,7 @@ func (a *SlotAssembler) ResetSlot(slot uint64) { defer a.mu.Unlock() a.recordPartialObsLocked(a.slots[slot]) + a.releasePrefetchLocked(a.slots[slot]) delete(a.slots, slot) delete(a.completedSlots, slot) } @@ -643,6 +674,7 @@ func (a *SlotAssembler) pruneOldSlotsLocked() { for slot, state := range a.slots { if slot < minSlot && !state.completing { a.recordPartialObsLocked(state) + a.releasePrefetchLocked(a.slots[slot]) delete(a.slots, slot) a.evictedSlots++ } @@ -695,6 +727,7 @@ func (a *SlotAssembler) pruneOldSlotsLocked() { return } a.recordPartialObsLocked(a.slots[victim]) + a.releasePrefetchLocked(a.slots[victim]) delete(a.slots, victim) a.evictedSlots++ } diff --git a/pkg/turbine/cancellation_regression_test.go b/pkg/turbine/cancellation_regression_test.go index 1f85e4df..71993121 100644 --- a/pkg/turbine/cancellation_regression_test.go +++ b/pkg/turbine/cancellation_regression_test.go @@ -17,86 +17,52 @@ func TestTransactionVerifierCancellationDuringBlockedAdmissionJoinsAdmittedJobs( const workers = 2 blocker := verifierTestBlock(workers) target := verifierTestBlock(2 * workers) - filler := &solana.Transaction{} - release := make(chan struct{}) var releaseOnce sync.Once started := make(chan struct{}, workers) var targetFirstCalls atomic.Int32 var targetLaterCalls atomic.Int32 - - verifier := newTransactionVerifier(workers, workers, func(tx *solana.Transaction) error { + // Single-transaction groups make the blocked-admission boundary exact. + verifier := newTransactionVerifierWithBatchTarget(workers, 1, 1, func(tx *solana.Transaction) error { switch tx { case blocker.Transactions[0], blocker.Transactions[1]: started <- struct{}{} <-release case target.Transactions[0]: targetFirstCalls.Add(1) - case target.Transactions[1], target.Transactions[2], target.Transactions[3]: + default: targetLaterCalls.Add(1) } return nil }) - + defer verifier.closeAndWait() + defer releaseOnce.Do(func() { close(release) }) ctx, cancel := context.WithCancel(context.Background()) + defer cancel() blockerDone := make(chan error, 1) targetDone := make(chan error, 1) - var calls sync.WaitGroup - calls.Add(1) - go func() { - defer calls.Done() - blockerDone <- verifier.verifyBlock(blocker) - }() + go func() { blockerDone <- verifier.verifyBlock(blocker) }() for range workers { - select { - case <-started: - case <-time.After(3 * time.Second): - cancel() - releaseOnce.Do(func() { close(release) }) - calls.Wait() - verifier.closeAndWait() - t.Fatal("timed out occupying transaction verifier workers") - } + waitSignal(t, started, "occupied verifier worker") } - // Leave one queued job ahead of the target. With both workers occupied and - // a two-entry queue, the target admits transaction 0 and then blocks trying - // to admit transaction 1. Cancellation must wait for transaction 0 to drain, - // while transactions 1 and all later chunks must never reach a worker. - var fillerErr error - var fillerDone sync.WaitGroup - fillerDone.Add(1) - verifier.jobs <- transactionVerifyJob{tx: filler, err: &fillerErr, done: &fillerDone} - - calls.Add(1) - go func() { - defer calls.Done() - targetDone <- verifier.verifyBlockContext(ctx, target) - }() + // Both workers are occupied. The target's first group fills the one-entry + // queue, and its second blocks on admission. Cancellation must still join + // the first group while preventing any later transactions from running. + go func() { targetDone <- verifier.verifyBlockContext(ctx, target) }() deadline := time.Now().Add(3 * time.Second) for len(verifier.jobs) != cap(verifier.jobs) { if time.Now().After(deadline) { - cancel() - releaseOnce.Do(func() { close(release) }) - calls.Wait() - fillerDone.Wait() - verifier.closeAndWait() - t.Fatalf("transaction queue did not fill: len=%d cap=%d", len(verifier.jobs), cap(verifier.jobs)) + t.Fatal("transaction queue did not fill") } time.Sleep(time.Millisecond) } - cancel() select { case err := <-targetDone: - releaseOnce.Do(func() { close(release) }) - calls.Wait() - fillerDone.Wait() - verifier.closeAndWait() t.Fatalf("canceled verifier returned before its admitted job joined: %v", err) case <-time.After(50 * time.Millisecond): } - releaseOnce.Do(func() { close(release) }) select { case err := <-targetDone: @@ -114,13 +80,6 @@ func TestTransactionVerifierCancellationDuringBlockedAdmissionJoinsAdmittedJobs( case <-time.After(3 * time.Second): t.Fatal("blocker verification did not drain") } - fillerDone.Wait() - calls.Wait() - verifier.closeAndWait() - - if fillerErr != nil { - t.Fatalf("filler verification: %v", fillerErr) - } if got := targetFirstCalls.Load(); got != 1 { t.Fatalf("admitted target transaction calls = %d, want 1", got) } diff --git a/pkg/turbine/entries.go b/pkg/turbine/entries.go index 9f625131..333eb23a 100644 --- a/pkg/turbine/entries.go +++ b/pkg/turbine/entries.go @@ -1,6 +1,8 @@ package turbine import ( + "bytes" + "context" "encoding/binary" "fmt" "sort" @@ -40,6 +42,11 @@ type AlpenglowParentInfo struct { type entryDecodeTimings struct { transactionParse time.Duration + ctx context.Context + prefetched map[uint32]*prefetchedShredBatch + retained []*prefetchedShredBatch + all []*prefetchedShredBatch + prefetchWait time.Duration } func (e *Entry) UnmarshalWithDecoder(decoder *bin.Decoder) error { @@ -125,11 +132,7 @@ func decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds []*Shred, timings *en return shreds[i].Index < shreds[j].Index }) - type decodedEntryBatch struct { - start uint32 - entries []Entry - } - var entryBatches []decodedEntryBatch + var entryBatches []*prefetchedShredBatch var parentInfo *AlpenglowParentInfo var blockFooter *BlockFooter var batchBytes []byte @@ -147,39 +150,61 @@ func decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds []*Shred, timings *en if !shred.DataComplete() { continue } - if parent, footer, ok, err := decodeAlpenglowMarkerFromShredBatch(batchBytes, batchStart); err != nil { - return nil, nil, nil, fmt.Errorf("decode alpenglow block marker ending at shred %d: %w", shred.Index, err) - } else if ok { - if parent != nil { - parentInfo, err = mergeAlpenglowParentInfo(parentInfo, parent) + var batch *prefetchedShredBatch + if timings != nil { + if cached := timings.prefetched[batchStart]; cached != nil && cached.start == batchStart && cached.end == shred.Index { + ctx := timings.ctx + if ctx == nil { + ctx = context.Background() + } + if cached.ready != nil { + waitStarted := time.Now() + select { + case <-cached.ready: + case <-ctx.Done(): + timings.prefetchWait += time.Since(waitStarted) + return nil, nil, nil, ctx.Err() + } + timings.prefetchWait += time.Since(waitStarted) + } + if err := ctx.Err(); err != nil { + return nil, nil, nil, err + } + // Bounds alone cannot prove identity after repair or replacement. + // Read decoded fields only after the preparation channel closes. + if bytes.Equal(batchBytes, cached.raw) { + batch = cached + } + } + } + if batch == nil { + batch = decodeClosedShredBatch(batchBytes, batchStart, shred.Index) + if timings != nil { + timings.transactionParse += batch.parseDuration + } + } + if timings != nil { + timings.all = append(timings.all, batch) + } + if batch.err != nil { + return nil, nil, nil, batch.err + } + if batch.marker { + if batch.parent != nil { + var err error + parentInfo, err = mergeAlpenglowParentInfo(parentInfo, batch.parent) if err != nil { return nil, nil, nil, fmt.Errorf("merge alpenglow parent marker ending at shred %d: %w", shred.Index, err) } } - if footer != nil { - blockFooter = footer + if batch.footer != nil { + blockFooter = batch.footer } batchBytes = nil haveBatch = false continue } - parseStart := time.Now() - batchEntries, consumed, err := decodeEntryBatchPrefix(batchBytes) - if timings != nil { - timings.transactionParse += time.Since(parseStart) - } - // A zero entry count with more bytes denotes a marker. Preserve the - // rejection of unrecognized or misplaced markers in this fallback path. - if err == nil && len(batchEntries) == 0 && consumed != len(batchBytes) { - err = fmt.Errorf("entry batch has %d trailing bytes", len(batchBytes)-consumed) - } - if err != nil { - return nil, nil, nil, fmt.Errorf("decode entry batch ending at shred %d: %w", shred.Index, err) - } - entryBatches = append(entryBatches, decodedEntryBatch{ - start: batchStart, - entries: batchEntries, - }) + entryBatches = append(entryBatches, batch) // Decoded transactions retain slices into the batch buffer for instruction data. // Keep the backing array alive instead of reusing and overwriting it. batchBytes = nil @@ -199,10 +224,43 @@ func decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds []*Shred, timings *en continue } entries = append(entries, batch.entries...) + if timings != nil { + timings.retained = append(timings.retained, batch) + } } return entries, parentInfo, blockFooter, nil } +// decodeClosedShredBatch owns raw through the returned decoded transactions. +// It decodes the same padded component envelope for early and full assembly; +// marker merging and UpdateParent selection still require the full slot. +func decodeClosedShredBatch(raw []byte, start, end uint32) *prefetchedShredBatch { + batch := &prefetchedShredBatch{start: start, end: end, raw: raw} + parent, footer, marker, err := decodeAlpenglowMarkerFromShredBatch(raw, start) + if err != nil { + batch.err = fmt.Errorf("decode alpenglow block marker ending at shred %d: %w", end, err) + return batch + } + if marker { + batch.parent, batch.footer, batch.marker = parent, footer, true + return batch + } + parseStarted := time.Now() + entries, consumed, err := decodeEntryBatchPrefix(raw) + batch.parseDuration = time.Since(parseStarted) + // A zero entry count with more bytes denotes a marker. Preserve rejection + // of unknown or misplaced markers instead of treating them as an empty batch. + if err == nil && len(entries) == 0 && consumed != len(raw) { + err = fmt.Errorf("entry batch has %d trailing bytes", len(raw)-consumed) + } + if err != nil { + batch.err = fmt.Errorf("decode entry batch ending at shred %d: %w", end, err) + return batch + } + batch.entries = entries + return batch +} + func decodeEntryBatch(data []byte) ([]Entry, error) { entries, consumed, err := decodeEntryBatchPrefix(data) if err != nil { diff --git a/pkg/turbine/entries_prefetch_test.go b/pkg/turbine/entries_prefetch_test.go new file mode 100644 index 00000000..9ecdbf59 --- /dev/null +++ b/pkg/turbine/entries_prefetch_test.go @@ -0,0 +1,146 @@ +package turbine + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func decodedPrefetchForTest(shreds []*Shred) map[uint32]*prefetchedShredBatch { + cache := make(map[uint32]*prefetchedShredBatch) + var raw []byte + var start uint32 + for _, shred := range shreds { + if raw == nil { + start = shred.Index + } + raw = append(raw, shred.Data...) + if shred.DataComplete() { + batch := decodeClosedShredBatch(raw, start, shred.Index) + batch.ready = make(chan struct{}) + close(batch.ready) + cache[start] = batch + raw = nil + } + } + return cache +} + +func TestEntryDecodeReusesOnlyExactPrefetchedBytesAndBounds(t *testing.T) { + component, err := NewEntryBatch([]Entry{{Hash: solana.Hash{9}, Txns: []solana.Transaction{mustParseTransferTx(t, 21)}}}) + require.NoError(t, err) + raw, err := MarshalBlockComponent(component) + require.NoError(t, err) + for _, mode := range []string{"match", "different_bytes", "different_end", "different_start"} { + t.Run(mode, func(t *testing.T) { + shreds := paddedComponentShreds(t, raw, 0, 0xa5) + cache := decodedPrefetchForTest(shreds) + cached := cache[0] + require.NoError(t, cached.err) + cached.parseDuration = time.Hour // must not enter final parse accounting + if mode != "match" { + cached.err = errors.New("stale cached failure must be ignored") + } + switch mode { + case "different_bytes": + cached.raw[len(cached.raw)-1] ^= 1 // even differing padding invalidates reuse + case "different_end": + cached.end++ + cached.ready = make(chan struct{}) // mismatched bounds must never wait + case "different_start": + cached.start++ + cached.ready = make(chan struct{}) + } + timings := entryDecodeTimings{prefetched: cache} + entries, _, _, err := decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds, &timings) + require.NoError(t, err) + require.Len(t, entries, 1) + require.Len(t, timings.all, 1) + require.Len(t, timings.retained, 1) + if mode == "match" { + require.Same(t, cached, timings.retained[0]) + require.Same(t, &cached.entries[0].Txns[0], &entries[0].Txns[0]) + require.Zero(t, timings.transactionParse) + } else { + require.NotSame(t, cached, timings.retained[0]) + require.Less(t, timings.transactionParse, time.Hour) + } + }) + } +} + +func TestEntryDecodePrefetchPreparationWaitHonorsCancellation(t *testing.T) { + raw, err := marshalEntryBatch([]Entry{{Hash: solana.Hash{3}}}) + require.NoError(t, err) + shreds := paddedComponentShreds(t, raw, 0, 0) + cache := decodedPrefetchForTest(shreds) + cache[0].ready = make(chan struct{}) + ctx, cancel := context.WithCancel(context.Background()) + cancel() + timings := entryDecodeTimings{ctx: ctx, prefetched: cache} + _, _, _, err = decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds, &timings) + require.ErrorIs(t, err, context.Canceled) + require.Empty(t, timings.retained) + require.Zero(t, timings.transactionParse) +} + +func TestEntryDecodePrefetchPreservesUpdateParentAndIgnoresVerificationErrors(t *testing.T) { + prefix, err := NewEntryBatch([]Entry{{Hash: solana.Hash{1}, Txns: []solana.Transaction{mustParseTransferTx(t, 20)}}}) + require.NoError(t, err) + suffix, err := NewEntryBatch([]Entry{{Hash: solana.Hash{2}, Txns: []solana.Transaction{mustParseTransferTx(t, 21)}}}) + require.NoError(t, err) + components := []BlockComponent{NewBlockHeader(99, solana.Hash{9}), prefix, NewUpdateParent(98, solana.Hash{8}), suffix, NewBlockFooter(BlockFooter{BankHash: solana.Hash{7}})} + var shreds []*Shred + for _, component := range components { + raw, err := MarshalBlockComponent(component) + require.NoError(t, err) + shreds = append(shreds, paddedComponentShreds(t, raw, uint32(len(shreds)), 0xa5)...) + } + cache := decodedPrefetchForTest(shreds) + for _, start := range []uint32{32, 96} { + // Decoding must not make a signature decision, even for retained entries. + cache[start].verification = &transactionVerification{err: errors.New("signature result belongs to completion")} + cache[start].submitErr = errors.New("submission result belongs to completion") + } + timings := entryDecodeTimings{prefetched: cache} + entries, parent, footer, err := decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds, &timings) + require.NoError(t, err) + require.Len(t, entries, 1) + require.Equal(t, solana.Hash{2}, entries[0].Hash) + require.Equal(t, uint32(64), parent.ReplayFECSetIndex) + require.Equal(t, solana.Hash{7}, footer.BankHash) + require.Len(t, timings.all, 5) + require.Len(t, timings.retained, 1) + require.Same(t, cache[96], timings.retained[0]) + + // Parsing the discarded prefix remains mandatory, unlike its signatures. + cache[32].err = errors.New("malformed optimistic prefix") + timings = entryDecodeTimings{prefetched: cache} + _, _, _, err = decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds, &timings) + require.ErrorContains(t, err, "malformed optimistic prefix") +} + +func TestEntryDecodePrefetchedAgavePaddedCaptureMatchesFullDecode(t *testing.T) { + packets := agavePaddedSlot1752420Packets(t) + shreds := make([]*Shred, len(packets)) + for i, packet := range packets { + var err error + shreds[i], err = ParseShred(packet) + require.NoError(t, err) + } + wantEntries, wantParent, wantFooter, err := DecodeEntriesAndAlpenglowMarkersFromDataShreds(shreds) + require.NoError(t, err) + timings := entryDecodeTimings{prefetched: decodedPrefetchForTest(shreds)} + entries, parent, footer, err := decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds, &timings) + require.NoError(t, err) + require.Equal(t, wantEntries, entries) + require.Equal(t, wantParent, parent) + require.Equal(t, wantFooter, footer) + require.Len(t, timings.all, 4) + require.Len(t, timings.retained, 2) + require.Zero(t, timings.transactionParse) +} diff --git a/pkg/turbine/entry_prefetch.go b/pkg/turbine/entry_prefetch.go new file mode 100644 index 00000000..5816b1e8 --- /dev/null +++ b/pkg/turbine/entry_prefetch.go @@ -0,0 +1,343 @@ +package turbine + +import ( + "context" + "errors" + "sync" + "time" + + "github.com/Overclock-Validator/mithril/pkg/block" + "github.com/gagliardetto/solana-go" +) + +const ( + entryPrefetchSlots = 8 + // Covers a large block of maximum-size transactions while bounding the + // extra retained encoded bytes across all in-flight generations. + entryPrefetchBytes = 64 << 20 + entryPrefetchBatchBytes = 1 << 20 +) + +type shredBatchRange struct{ start, end uint32 } + +// A result belongs to one exact DATA_COMPLETE range in one slot generation. +// Fields are immutable after ready closes; signature readers own its decoded +// transactions until verification.done closes. +type prefetchedShredBatch struct { + start, end uint32 + raw []byte + entries []Entry + parent *AlpenglowParentInfo + footer *BlockFooter + marker bool + parseDuration time.Duration + err error + ready chan struct{} + verification *transactionVerification + submittedAt time.Time + submitErr error +} + +type slotEntryPrefetch struct { + pool *entryPrefetchPool + ctx context.Context + cancel context.CancelFunc + batches map[uint32]*prefetchedShredBatch + next int + queued, released bool + bytes int +} + +// All scheduling and accounting use assembler.mu. The packet reader only +// advances a contiguous frontier and attempts a nonblocking, coalesced enqueue. +// Decoding and bounded verifier admission run on separate background workers. +type entryPrefetchPool struct { + a *SlotAssembler + ctx context.Context + cancel context.CancelFunc + verifier *transactionVerifier + jobs chan *slotState + workers, cleanup sync.WaitGroup + slots, bytes int + closed bool + close sync.Once +} + +func newEntryPrefetchPool(ctx context.Context, a *SlotAssembler, verifier *transactionVerifier) *entryPrefetchPool { + ctx, cancel := context.WithCancel(ctx) + p := &entryPrefetchPool{a: a, ctx: ctx, cancel: cancel, verifier: verifier, jobs: make(chan *slotState, entryPrefetchSlots)} + a.mu.Lock() + a.entryPrefetch = p + a.mu.Unlock() + p.workers.Add(2) + for i := 0; i < 2; i++ { + go p.run() + } + return p +} + +func (a *SlotAssembler) prefetchEntriesLocked(s *slotState) { + p := a.entryPrefetch + if p == nil || p.closed || p.ctx.Err() != nil { + return + } + // Each index is visited once, even when one batch spans many FEC sets or + // arrives out of order. A gap never yields a speculative partial decode. + for s.batchScan < maxDataShredsPerSlot { + sh := s.shreds[s.batchScan] + if sh == nil { + break + } + if sh.DataComplete() { + s.completeBatches = append(s.completeBatches, shredBatchRange{s.batchStart, s.batchScan}) + s.batchStart = s.batchScan + 1 + } + s.batchScan++ + } + if s.prefetch == nil && len(s.completeBatches) > 0 && p.slots < entryPrefetchSlots { + ctx, cancel := context.WithCancel(p.ctx) + s.prefetch = &slotEntryPrefetch{pool: p, ctx: ctx, cancel: cancel, batches: make(map[uint32]*prefetchedShredBatch)} + p.slots++ + } + p.enqueueLocked(s) +} + +func (p *entryPrefetchPool) enqueueLocked(s *slotState) { + f := s.prefetch + if f == nil || f.pool != p || f.queued || f.released || s.completing || p.closed || f.next >= len(s.completeBatches) { + return + } + select { + case p.jobs <- s: + f.queued = true + default: + } +} + +func (p *entryPrefetchPool) run() { + defer p.workers.Done() + for s := range p.jobs { + p.a.mu.Lock() + f := s.prefetch + if p.closed || f.released || f.ctx.Err() != nil || p.a.slots[s.slot] != s || s.completing { + f.queued = false + p.a.mu.Unlock() + continue + } + var batch *prefetchedShredBatch + var shreds []*Shred + var rawSize int + for f.next < len(s.completeBatches) { + r := s.completeBatches[f.next] + size := 0 + for i := r.start; i <= r.end; i++ { + size += len(s.shreds[i].Data) + if size > entryPrefetchBatchBytes { + break + } + } + if size > entryPrefetchBatchBytes { + f.next++ + continue + } + if p.bytes+size > entryPrefetchBytes { + break + } + f.next++ + p.bytes += size + f.bytes += size + rawSize = size + batch = &prefetchedShredBatch{start: r.start, end: r.end, ready: make(chan struct{})} + f.batches[r.start] = batch + shreds = make([]*Shred, 0, r.end-r.start+1) + for i := r.start; i <= r.end; i++ { + shreds = append(shreds, s.shreds[i]) + } + break + } + if batch == nil { + f.queued = false + p.a.mu.Unlock() + continue + } + p.a.mu.Unlock() + + raw := make([]byte, 0, rawSize) + for _, sh := range shreds { + raw = append(raw, sh.Data...) + } + decoded := decodeClosedShredBatch(raw, batch.start, batch.end) + ready := batch.ready + batch.raw, batch.entries = decoded.raw, decoded.entries + batch.parent, batch.footer, batch.marker = decoded.parent, decoded.footer, decoded.marker + batch.parseDuration, batch.err = decoded.parseDuration, decoded.err + if batch.err == nil && !batch.marker && f.ctx.Err() == nil { + txs := entryBatchTransactions(batch.entries) + if len(txs) > 0 { + batch.submittedAt = time.Now() + batch.verification, batch.submitErr = p.verifier.submitTransactions(f.ctx, txs) + } + } + close(ready) + p.a.mu.Lock() + f.queued = false + p.enqueueLocked(s) + p.a.mu.Unlock() + } +} + +func entryBatchTransactions(entries []Entry) []*solana.Transaction { + var txs []*solana.Transaction + for i := range entries { + for j := range entries[i].Txns { + txs = append(txs, &entries[i].Txns[j]) + } + } + return txs +} + +// Keep reservations until canceled readers have actually relinquished their +// buffers. Repeated resets cannot evade the memory or slot bounds. +func (a *SlotAssembler) releasePrefetchLocked(s *slotState) { + if s == nil || s.prefetch == nil || s.prefetch.released { + return + } + f := s.prefetch + f.released = true + f.cancel() + p := f.pool + p.cleanup.Add(1) + go func() { + defer p.cleanup.Done() + for _, b := range f.batches { + <-b.ready + if b.verification != nil { + b.verification.wait() + } + } + p.a.mu.Lock() + p.slots-- + p.bytes -= f.bytes + p.a.mu.Unlock() + }() +} + +func (p *entryPrefetchPool) closeAndWait() { + p.close.Do(func() { + p.cancel() + p.a.mu.Lock() + p.closed = true + if p.a.entryPrefetch == p { + p.a.entryPrefetch = nil + } + for _, s := range p.a.slots { + if s.prefetch != nil && s.prefetch.pool == p { + p.a.releasePrefetchLocked(s) + } + } + close(p.jobs) + p.a.mu.Unlock() + p.workers.Wait() + p.cleanup.Wait() + }) +} + +// Reuse only retained entry results. UpdateParent may intentionally discard an +// invalid optimistic prefix. Unprefetched transactions form one immediately +// available request, overlapping any early requests still running. +func verifyDecodedEntryBatches(ctx context.Context, blk *block.Block, batches []*prefetchedShredBatch, verifier *transactionVerifier) error { + type pending struct { + future *transactionVerification + offset int + } + var early []pending + var missing []*solana.Transaction + var indices []int + offset := 0 + for _, b := range batches { + count := 0 + for _, e := range b.entries { + count += len(e.Txns) + } + reusable := b.verification != nil + if reusable { + select { + case <-b.verification.done: + // Cancellation is not a signature verdict. A completion canceled + // after preparation may be retried on this same slot generation. + reusable = !errors.Is(b.verification.err, context.Canceled) && !errors.Is(b.verification.err, context.DeadlineExceeded) + default: + } + } + if reusable { + early = append(early, pending{b.verification, offset}) + } else { + missing = append(missing, blk.Transactions[offset:offset+count]...) + for i := 0; i < count; i++ { + indices = append(indices, offset+i) + } + } + offset += count + } + var fallback *transactionVerification + var err error + if len(missing) > 0 { + fallback, err = verifier.submitTransactions(ctx, missing) + } + firstIndex := len(blk.Transactions) + firstErr := err + for _, p := range early { + i, e := p.future.waitContext(ctx) + if e != nil && (firstErr == nil || (i >= 0 && p.offset+i < firstIndex)) { + firstErr = e + if i >= 0 { + firstIndex = p.offset + i + } + } + } + if fallback != nil { + i, e := fallback.waitContext(ctx) + if e != nil && (firstErr == nil || (i >= 0 && indices[i] < firstIndex)) { + firstErr = e + if i >= 0 { + firstIndex = indices[i] + } + } + } + if ctx.Err() != nil { + return ctx.Err() + } + if firstErr != nil && firstIndex < len(blk.Transactions) { + return formatTransactionVerificationError(blk, firstIndex, firstErr) + } + return firstErr +} + +func earlyEntryTimings(t *entryDecodeTimings, fullAt time.Time, timings *block.TurbineIngressTimings) { + for _, b := range t.all { + if b.ready == nil { + continue + } + timings.EarlyTransactionParse += b.parseDuration + if b.verification != nil { + select { + case <-b.verification.done: + timings.EarlyTransactionSigverify += b.verification.finishedAt.Sub(b.submittedAt) + default: + } + } + } + for _, b := range t.retained { + if b.verification != nil { + select { + case <-b.verification.done: + if b.verification.err == nil && !b.verification.finishedAt.After(fullAt) { + for _, e := range b.entries { + timings.EarlyVerifiedTransactions += uint64(len(e.Txns)) + } + } + default: + } + } + } +} diff --git a/pkg/turbine/entry_prefetch_benchmark_test.go b/pkg/turbine/entry_prefetch_benchmark_test.go new file mode 100644 index 00000000..4a0f14e7 --- /dev/null +++ b/pkg/turbine/entry_prefetch_benchmark_test.go @@ -0,0 +1,217 @@ +package turbine + +import ( + "context" + "crypto/ed25519" + "encoding/binary" + "fmt" + "testing" + "time" + + "github.com/Overclock-Validator/mithril/pkg/block" + "github.com/Overclock-Validator/mithril/pkg/sigverify" + "github.com/gagliardetto/solana-go" +) + +// BenchmarkEntryPrefetchAssembly runs actual assembler ingestion, component +// decoding, final Merkle identity checks, transaction verification, and final +// publication. Source transactions use the same generated 228/1232-byte or +// captured fixtures as BenchmarkTransactionVerificationFlow. Generation, +// packet parsing and shred-signature authentication happen outside the timer. +// There is no network I/O, loss/recovery, replay, PoH or footer-bankhash execution. +// +// Complete component bursts are scheduled across 200 ms for tip scenarios; +// catchup offers all shreds immediately. This is a workload model, not a +// measured cluster arrival distribution. A 60 KiB signed-transaction budget +// defines components; actual entry overhead and FEC padding are generated. +// Pools persist across iterations; the identical fixture slot is reset between +// samples outside the timer. Use fixed counts (e.g. -benchtime=3x). +func BenchmarkEntryPrefetchAssembly(b *testing.B) { + flowConfigureBackend(b) + for _, source := range flowBenchmarkFixtures(b) { + b.Run(source.name, func(b *testing.B) { + fixture := makeAssemblyFlowFixture(b, source.blk) + for _, workers := range []int{2, 4} { + b.Run(fmt.Sprintf("workers_%d", workers), func(b *testing.B) { + for _, target := range []int{4, 8} { + b.Run(fmt.Sprintf("target_%d", target), func(b *testing.B) { + for _, arrival := range []struct { + name string + span time.Duration + }{{"catchup", 0}, {"tip_200ms", 200 * time.Millisecond}} { + b.Run(arrival.name, func(b *testing.B) { + for _, overlap := range []bool{false, true} { + name := "overlap_off" + if overlap { + name = "overlap_on" + } + b.Run(name, func(b *testing.B) { + runAssemblyFlowBenchmark(b, source.blk, fixture, workers, target, arrival.span, overlap) + }) + } + }) + } + }) + } + }) + } + }) + } +} + +type assemblyFlowFixture struct { + components [][]*Shred + blockID solana.Hash + parentID solana.Hash + bankhash solana.Hash + shreds int +} + +func makeAssemblyFlowFixture(tb testing.TB, source *block.Block) assemblyFlowFixture { + tb.Helper() + fixture := assemblyFlowFixture{parentID: solana.Hash{31}, bankhash: solana.Hash{47}} + var seed [ed25519.SeedSize]byte + seed[0] = 193 // Public deterministic benchmark leader, not validator material. + leader := solana.PrivateKey(ed25519.NewKeyFromSeed(seed[:])) + public := solana.PublicKeyFromBytes(ed25519.PrivateKey(leader).Public().(ed25519.PublicKey)) + generator := ShredGenerator{Slot: 100, ParentSlot: 99, Version: 7, ReferenceTick: 63} + var nextData, nextCode uint32 + var chained solana.Hash + var roots []solana.Hash + appendComponent := func(component BlockComponent, last bool) { + raw, err := MarshalBlockComponent(component) + if err != nil { + tb.Fatal(err) + } + generated, dataEnd, codeEnd, err := generator.makeShredsFromData(leader, raw, last, chained, nextData, nextCode) + if err != nil { + tb.Fatal(err) + } + chained, nextData, nextCode = generated.chainedMerkleRoot, dataEnd, codeEnd + roots = append(roots, generated.fecSetRoots...) + var data []*Shred + for _, packet := range generated.packets { + shred, err := ParseShred(packet) + if err != nil { + tb.Fatal(err) + } + if shred.Type != ShredTypeData { + continue + } + if err := shred.VerifySignature(public); err != nil { + tb.Fatal(err) + } + data = append(data, shred) + } + fixture.components = append(fixture.components, data) + fixture.shreds += len(data) + } + appendComponent(NewBlockHeader(99, fixture.parentID), false) + for i, txs := range flowComponents(source.Transactions, 60*1024) { + entry := Entry{NumHashes: 1, Txns: make([]solana.Transaction, len(txs))} + binary.LittleEndian.PutUint64(entry.Hash[:], uint64(i+1)) + for j, tx := range txs { + entry.Txns[j] = *tx + } + appendComponent(BlockComponent{EntryBatch: []Entry{entry}}, false) + } + appendComponent(NewBlockFooter(BlockFooter{BankHash: fixture.bankhash}), true) + if nextData > maxDataShredsPerSlot { + tb.Fatalf("fixture requires %d data shreds; assembler limit is %d", nextData, maxDataShredsPerSlot) + } + fixture.blockID = DoubleMerkleBlockID(99, fixture.parentID, roots) + return fixture +} + +func runAssemblyFlowBenchmark(b *testing.B, source *block.Block, fixture assemblyFlowFixture, workers, target int, span time.Duration, overlap bool) { + v := newTransactionVerifierWithBatchTarget(workers, 2*workers*target, target, nil) + defer v.closeAndWait() + if err := v.verifyBlock(source); err != nil { + b.Fatal(err) + } + a := NewSlotAssembler() + a.verifyTransactions = v.verifyBlockContext + a.SetKnownAlpenglowBlockID(99, fixture.parentID) + a.SetKnownAlpenglowBlockID(100, fixture.blockID) + if overlap { + prefetch := newEntryPrefetchPool(context.Background(), a, v) + defer prefetch.closeAndWait() + } + var ready, parse, preparation, joins, arrivals []time.Duration + var early uint64 + var cpu float64 + before := sigverify.Stats() + b.ReportAllocs() + b.ResetTimer() + for range b.N { + b.StopTimer() + a.ResetSlot(100) + cpuStarted := flowCPUSeconds(b) + b.StartTimer() + started := time.Now() + var work *slotCompletionWork + for componentIndex, component := range fixture.components { + if span > 0 { + time.Sleep(time.Until(started.Add(flowArrivalOffset(componentIndex, len(fixture.components), span)))) + } + for _, shred := range component { + candidate, err := a.addShredFrom(shred, false) + if err != nil { + b.Fatal(err) + } + if candidate != nil { + if work != nil { + b.Fatal("slot claimed completion more than once") + } + work = candidate + } + } + } + if work == nil { + b.Fatal("all generated data shreds did not complete the slot") + } + processed := a.processCompletion(context.Background(), work) + completed, err := a.finalizeCompletion(work, processed) + b.StopTimer() + cpu += flowCPUSeconds(b) - cpuStarted + if err != nil || completed == nil { + b.Fatalf("completion failed: block=%v error=%v", completed != nil, err) + } + if !completed.TransactionSignaturesVerified() || len(completed.Transactions) != len(source.Transactions) || + !completed.HasAlpenglowBlockID || solana.Hash(completed.AlpenglowBlockID) != fixture.blockID || + !completed.HasExpectedBankhash || completed.ExpectedBankhash != fixture.bankhash { + b.Fatal("completion changed transaction coverage or authenticated block metadata") + } + ready = append(ready, processed.timings.FullToReady) + parse = append(parse, processed.timings.TransactionParse) + preparation = append(preparation, processed.timings.EarlyPreparationWait) + joins = append(joins, processed.timings.TransactionSigverify) + arrivals = append(arrivals, processed.timings.ShredCollection) + early += processed.timings.EarlyVerifiedTransactions + } + after := sigverify.Stats() + b.ReportMetric(cpu*1000/float64(b.N), "cpu-ms/block") + b.ReportMetric(cpu/b.Elapsed().Seconds(), "avg_cpu_cores") + b.ReportMetric(float64(early)/float64(b.N), "early_verified_tx/block") + b.ReportMetric(float64(len(source.Transactions)), "tx/block") + b.ReportMetric(float64(fixture.shreds), "data_shreds/block") + b.ReportMetric(float64(len(fixture.components)), "components/block") + if batches := after.Batches - before.Batches; batches > 0 { + b.ReportMetric(float64(after.Signatures-before.Signatures)/float64(batches), "mean_width") + } + flowReportPercentiles(b, ready, "full_to_ready") + flowReportPercentiles(b, parse, "completion_parse") + flowReportPercentiles(b, preparation, "preparation_wait") + flowReportPercentiles(b, joins, "completion_sigverify") + flowReportPercentiles(b, arrivals, "collection") + if after.InternalFaultFallbacks != before.InternalFaultFallbacks { + b.Fatal("signature verifier used an internal fault fallback") + } + var signatures uint64 + for _, tx := range source.Transactions { + signatures += uint64(len(tx.Signatures)) + } + if got, want := after.Signatures-before.Signatures, signatures*uint64(b.N); got != want { + b.Fatalf("verified %d signatures; want %d, exactly once per retained transaction", got, want) + } +} diff --git a/pkg/turbine/entry_prefetch_bounds_test.go b/pkg/turbine/entry_prefetch_bounds_test.go new file mode 100644 index 00000000..189ff25c --- /dev/null +++ b/pkg/turbine/entry_prefetch_bounds_test.go @@ -0,0 +1,50 @@ +package turbine + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestEntryPrefetchByteBoundsFallBackToCompleteVerification(t *testing.T) { + for _, mode := range []string{"budget_full", "oversized_component"} { + t.Run(mode, func(t *testing.T) { + v := newTransactionVerifier(2, 16, nil) + defer v.closeAndWait() + a := NewSlotAssembler() + p := newEntryPrefetchPool(context.Background(), a, v) + defer p.closeAndWait() + if mode == "budget_full" { + // Model other generations owning the entire encoded-byte budget. + a.mu.Lock() + p.bytes = entryPrefetchBytes + a.mu.Unlock() + defer func() { a.mu.Lock(); p.bytes -= entryPrefetchBytes; a.mu.Unlock() }() + } + raw := prefetchTestPayload(t, verifierSignedTransactions(t, 3)) + if mode == "oversized_component" { + // Ordinary entry components permit trailing FEC padding. The + // complete decoder must accept this even though prefetch skips it. + raw = append(raw, make([]byte, entryPrefetchBatchBytes+1-len(raw))...) + } + const slot = 400 + batches := prefetchTestShreds(t, slot, raw, buildAlpenglowEndingTick(t)) + require.Nil(t, feedPrefetchShreds(t, a, batches[0])) + require.Eventually(t, func() bool { + a.mu.Lock() + defer a.mu.Unlock() + f := a.slots[slot].prefetch + return f != nil && !f.queued && len(f.batches) == 0 + }, 3*time.Second, time.Millisecond) + blk := feedPrefetchShreds(t, a, batches[1]) + require.NotNil(t, blk) + require.Len(t, blk.Transactions, 3) + require.True(t, blk.TransactionSignaturesVerified()) + timings, ok := blk.TurbineIngressTimings() + require.True(t, ok) + require.Zero(t, timings.EarlyVerifiedTransactions) + }) + } +} diff --git a/pkg/turbine/entry_prefetch_test.go b/pkg/turbine/entry_prefetch_test.go new file mode 100644 index 00000000..e22f514c --- /dev/null +++ b/pkg/turbine/entry_prefetch_test.go @@ -0,0 +1,360 @@ +package turbine + +import ( + "context" + "errors" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/Overclock-Validator/mithril/pkg/block" + "github.com/Overclock-Validator/mithril/pkg/txverify" + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func prefetchTestPayload(t *testing.T, txs []*solana.Transaction) []byte { + t.Helper() + entry := Entry{NumHashes: 1, Hash: solana.Hash{7}, Txns: make([]solana.Transaction, len(txs))} + for i, tx := range txs { + entry.Txns[i] = *tx + } + raw, err := marshalEntryBatch([]Entry{entry}) + require.NoError(t, err) + return raw +} + +func prefetchTestShreds(t *testing.T, slot uint64, payloads ...[]byte) [][]*Shred { + t.Helper() + gen := ShredGenerator{Slot: slot, ParentSlot: slot - 1, Version: 1} + var nextData, nextCode uint32 + var root solana.Hash + batches := make([][]*Shred, len(payloads)) + for i, raw := range payloads { + packets, nextRoot, data, code, err := gen.MakeShredsFromData(testShredLeader(t), raw, i == len(payloads)-1, root, nextData, nextCode) + require.NoError(t, err) + root, nextData, nextCode = nextRoot, data, code + for _, packet := range packets { + shred, err := ParseShred(packet) + require.NoError(t, err) + if shred.Type == ShredTypeData { + batches[i] = append(batches[i], shred) + } + } + } + return batches +} + +func feedPrefetchShreds(t *testing.T, a *SlotAssembler, shreds []*Shred) *block.Block { + t.Helper() + var result *block.Block + for _, shred := range shreds { + blk, err := a.AddShred(shred) + require.NoError(t, err) + if blk != nil { + require.Nil(t, result) + result = blk + } + } + return result +} + +func waitPrefetchedBatch(t *testing.T, a *SlotAssembler, slot uint64, start uint32) *prefetchedShredBatch { + t.Helper() + var batch *prefetchedShredBatch + require.Eventually(t, func() bool { + a.mu.Lock() + defer a.mu.Unlock() + if s := a.slots[slot]; s != nil && s.prefetch != nil { + batch = s.prefetch.batches[start] + } + return batch != nil + }, 3*time.Second, time.Millisecond) + waitSignal(t, batch.ready, "prefetched component preparation") + require.NoError(t, batch.err) + require.NoError(t, batch.submitErr) + return batch +} + +func TestEntryPrefetchVerifiesBeforeLastShredAndReusesResults(t *testing.T) { + var calls atomic.Int32 + v := newTransactionVerifier(2, 16, func(tx *solana.Transaction) error { + calls.Add(1) + return txverify.VerifyTransaction(tx) + }) + defer v.closeAndWait() + a := NewSlotAssembler() + p := newEntryPrefetchPool(context.Background(), a, v) + defer p.closeAndWait() + const slot = 100 + batches := prefetchTestShreds(t, slot, + prefetchTestPayload(t, verifierSignedTransactions(t, 3)), + prefetchTestPayload(t, verifierSignedTransactions(t, 4))) + require.Nil(t, feedPrefetchShreds(t, a, batches[0])) + cached := waitPrefetchedBatch(t, a, slot, 0) + require.NotNil(t, cached.verification) + _, err := cached.verification.wait() + require.NoError(t, err) + require.Equal(t, int32(3), calls.Load()) + for range 5 { + require.Nil(t, feedPrefetchShreds(t, a, batches[0])) + } + blk := feedPrefetchShreds(t, a, batches[1]) + require.NotNil(t, blk) + require.Len(t, blk.Transactions, 7) + require.True(t, blk.TransactionSignaturesVerified()) + require.Equal(t, int32(7), calls.Load(), "cached transactions must not be verified twice") + timings, ok := blk.TurbineIngressTimings() + require.True(t, ok) + require.Equal(t, uint64(3), timings.EarlyVerifiedTransactions) + require.LessOrEqual(t, cached.verification.finishedAt.UnixNano(), blk.ShredFullNanos) +} + +func TestEntryPrefetchWaitsForGapAcrossMultipleFECSets(t *testing.T) { + var calls atomic.Int32 + v := newTransactionVerifier(2, 16, func(*solana.Transaction) error { calls.Add(1); return nil }) + defer v.closeAndWait() + a := NewSlotAssembler() + p := newEntryPrefetchPool(context.Background(), a, v) + defer p.closeAndWait() + const slot = 104 + batches := prefetchTestShreds(t, slot, + prefetchTestPayload(t, verifierSignedTransactions(t, 300)), buildAlpenglowEndingTick(t)) + require.Greater(t, len(batches[0]), dataShredsPerFECBlock) + // Arrival of DATA_COMPLETE and later FEC sets cannot bypass the hole. + for i := len(batches[0]) - 1; i >= 0; i-- { + if i != 1 { + require.Nil(t, feedPrefetchShreds(t, a, batches[0][i:i+1])) + } + } + a.mu.Lock() + require.Empty(t, a.slots[slot].completeBatches) + require.Nil(t, a.slots[slot].prefetch) + a.mu.Unlock() + require.Zero(t, calls.Load()) + require.Nil(t, feedPrefetchShreds(t, a, batches[0][1:2])) + cached := waitPrefetchedBatch(t, a, slot, 0) + _, err := cached.verification.wait() + require.NoError(t, err) + require.Equal(t, int32(300), calls.Load()) + blk := feedPrefetchShreds(t, a, batches[1]) + require.NotNil(t, blk) + require.Equal(t, int32(300), calls.Load()) +} + +func TestEntryPrefetchResetKeepsOldReservationsUntilReadersJoin(t *testing.T) { + txs := verifierSignedTransactions(t, 2) + txs[0].Signatures[0][9] ^= 0x40 + oldSignature := txs[0].Signatures[0] + started := make(chan struct{}) + release := make(chan struct{}) + var releaseOnce sync.Once + v := newTransactionVerifier(1, 8, func(tx *solana.Transaction) error { + if tx.Signatures[0] == oldSignature { + close(started) + <-release + } + return txverify.VerifyTransaction(tx) + }) + defer v.closeAndWait() + a := NewSlotAssembler() + p := newEntryPrefetchPool(context.Background(), a, v) + defer p.closeAndWait() + defer releaseOnce.Do(func() { close(release) }) + const slot = 108 + old := prefetchTestShreds(t, slot, prefetchTestPayload(t, txs[:1]), buildAlpenglowEndingTick(t)) + fresh := prefetchTestShreds(t, slot, prefetchTestPayload(t, txs[1:]), buildAlpenglowEndingTick(t)) + require.Nil(t, feedPrefetchShreds(t, a, old[0])) + waitSignal(t, started, "old generation verifier") + oldBatch := waitPrefetchedBatch(t, a, slot, 0) + a.ResetSlot(slot) + a.mu.Lock() + require.Equal(t, 1, p.slots) + require.Positive(t, p.bytes) + a.mu.Unlock() + require.Nil(t, feedPrefetchShreds(t, a, fresh[0])) + newBatch := waitPrefetchedBatch(t, a, slot, 0) + require.NotSame(t, oldBatch, newBatch) + a.mu.Lock() + require.Equal(t, 2, p.slots) + a.mu.Unlock() + releaseOnce.Do(func() { close(release) }) + _, err := oldBatch.verification.wait() + require.ErrorIs(t, err, context.Canceled) + _, err = newBatch.verification.wait() + require.NoError(t, err) + blk := feedPrefetchShreds(t, a, fresh[1]) + require.NotNil(t, blk) + require.Equal(t, txs[1].Signatures[0], blk.Transactions[0].Signatures[0]) + require.Eventually(t, func() bool { + a.mu.Lock() + defer a.mu.Unlock() + return p.slots == 0 && p.bytes == 0 + }, 3*time.Second, time.Millisecond) +} + +func TestEntryPrefetchSaturationDoesNotBlockShredAdmissionAndShutdownJoins(t *testing.T) { + started := make(chan struct{}) + release := make(chan struct{}) + var first, releaseOnce sync.Once + v := newTransactionVerifier(1, 8, func(*solana.Transaction) error { + first.Do(func() { close(started) }) + <-release + return nil + }) + defer v.closeAndWait() + a := NewSlotAssembler() + p := newEntryPrefetchPool(context.Background(), a, v) + defer p.closeAndWait() + defer releaseOnce.Do(func() { close(release) }) + payload := prefetchTestPayload(t, verifierSignedTransactions(t, 1)) + for i := 0; i < entryPrefetchSlots+1; i++ { + batches := prefetchTestShreds(t, uint64(200+i), payload, buildAlpenglowEndingTick(t)) + admitted := make(chan struct{}) + go func() { + defer close(admitted) + for _, sh := range batches[0] { + _, err := a.addShredFrom(sh, false) + if err != nil { + t.Errorf("admit shred: %v", err) + } + } + }() + waitSignal(t, admitted, "nonblocking ingress while prefetch saturated") + } + waitSignal(t, started, "blocked verifier") + a.mu.Lock() + require.Equal(t, entryPrefetchSlots, p.slots) + require.LessOrEqual(t, p.bytes, entryPrefetchBytes) + require.Nil(t, a.slots[200+entryPrefetchSlots].prefetch) + a.mu.Unlock() + done := make(chan struct{}) + go func() { p.closeAndWait(); close(done) }() + select { + case <-done: + t.Fatal("early pool released buffers before signature worker joined") + case <-time.After(20 * time.Millisecond): + } + releaseOnce.Do(func() { close(release) }) + waitSignal(t, done, "saturated pool shutdown") + a.mu.Lock() + require.Zero(t, p.slots) + require.Zero(t, p.bytes) + require.Nil(t, a.entryPrefetch) + a.mu.Unlock() +} + +func TestEntryPrefetchInvalidRetainedTransactionFailsClosed(t *testing.T) { + txs := verifierSignedTransactions(t, 3) + txs[1].Signatures[0][3] ^= 0x80 + v := newTransactionVerifier(2, 16, nil) + defer v.closeAndWait() + a := NewSlotAssembler() + p := newEntryPrefetchPool(context.Background(), a, v) + defer p.closeAndWait() + batches := prefetchTestShreds(t, 300, prefetchTestPayload(t, txs), buildAlpenglowEndingTick(t)) + require.Nil(t, feedPrefetchShreds(t, a, batches[0])) + cached := waitPrefetchedBatch(t, a, 300, 0) + index, err := cached.verification.wait() + require.Equal(t, 1, index) + require.ErrorContains(t, err, "invalid signature") + var finalErr error + for _, sh := range batches[1] { + blk, err := a.AddShred(sh) + require.Nil(t, blk) + if err != nil { + finalErr = err + } + } + require.ErrorContains(t, finalErr, "transaction 1") + require.False(t, a.SlotCompleted(300)) +} + +func TestEntryPrefetchUpdateParentDiscardsInvalidOptimisticPrefix(t *testing.T) { + txs := verifierSignedTransactions(t, 2) + txs[0].Signatures[0][3] ^= 0x80 + v := newTransactionVerifier(2, 16, nil) + defer v.closeAndWait() + a := NewSlotAssembler() + p := newEntryPrefetchPool(context.Background(), a, v) + defer p.closeAndWait() + const slot = 304 + batches := prefetchTestShreds(t, slot, + prefetchTestPayload(t, txs[:1]), + testAlpenglowParentMarkerBytes(blockMarkerVariantUpdateParent, slot-2, solana.Hash{12}), + prefetchTestPayload(t, txs[1:])) + require.Nil(t, feedPrefetchShreds(t, a, batches[0])) + cached := waitPrefetchedBatch(t, a, slot, 0) + _, err := cached.verification.wait() + require.ErrorContains(t, err, "invalid signature") + require.Nil(t, feedPrefetchShreds(t, a, batches[1])) + blk := feedPrefetchShreds(t, a, batches[2]) + require.NotNil(t, blk) + require.Len(t, blk.Transactions, 1) + require.Equal(t, txs[1].Signatures[0], blk.Transactions[0].Signatures[0]) + require.Equal(t, uint64(slot-2), blk.SourceParentSlot) + require.True(t, blk.TransactionSignaturesVerified()) +} + +func TestEntryPrefetchCanceledCompletionCanRetrySameGeneration(t *testing.T) { + started := make(chan struct{}) + release := make(chan struct{}) + var first, releaseOnce sync.Once + v := newTransactionVerifier(1, 8, func(tx *solana.Transaction) error { + first.Do(func() { close(started); <-release }) + return txverify.VerifyTransaction(tx) + }) + defer v.closeAndWait() + a := NewSlotAssembler() + p := newEntryPrefetchPool(context.Background(), a, v) + defer p.closeAndWait() + defer releaseOnce.Do(func() { close(release) }) + const slot = 308 + batches := prefetchTestShreds(t, slot, + prefetchTestPayload(t, verifierSignedTransactions(t, 3)), buildAlpenglowEndingTick(t)) + require.Nil(t, feedPrefetchShreds(t, a, batches[0])) + waitSignal(t, started, "early verification") + cached := waitPrefetchedBatch(t, a, slot, 0) + var work *slotCompletionWork + for _, sh := range batches[1] { + var err error + work, err = a.addShredFrom(sh, false) + require.NoError(t, err) + } + require.NotNil(t, work) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + canceled := make(chan struct{}) + originalCancel := cached.verification.cancel + cached.verification.cancel = func() { close(canceled); originalCancel() } + done := make(chan processedSlotCompletion, 1) + go func() { done <- a.processCompletion(ctx, work) }() + // The completion has no expensive decode left and blocks joining this + // one already-prepared future; cancel while it owns those transactions. + time.Sleep(20 * time.Millisecond) + cancel() + waitSignal(t, canceled, "completion canceled its signature request") + releaseOnce.Do(func() { close(release) }) + var processed processedSlotCompletion + select { + case processed = <-done: + case <-time.After(3 * time.Second): + t.Fatal("canceled completion failed to join") + } + require.True(t, processed.canceled) + _, err := a.finalizeCompletion(work, processed) + require.NoError(t, err) + _, err = cached.verification.wait() + require.True(t, errors.Is(err, context.Canceled)) + a.mu.Lock() + retry := a.claimCompletionLocked(a.slots[slot], false) + a.mu.Unlock() + require.NotNil(t, retry) + processed = a.processCompletion(context.Background(), retry) + require.NoError(t, processed.err) + blk, err := a.finalizeCompletion(retry, processed) + require.NoError(t, err) + require.NotNil(t, blk) + require.True(t, blk.TransactionSignaturesVerified()) +} diff --git a/pkg/turbine/receiver.go b/pkg/turbine/receiver.go index d042131a..69b67190 100644 --- a/pkg/turbine/receiver.go +++ b/pkg/turbine/receiver.go @@ -12,6 +12,7 @@ import ( "github.com/Overclock-Validator/mithril/pkg/block" "github.com/Overclock-Validator/mithril/pkg/gossip" + "github.com/Overclock-Validator/mithril/pkg/sigverify" "github.com/gagliardetto/solana-go" ) @@ -546,7 +547,6 @@ func (r *UDPReceiver) Run(ctx context.Context) error { r.retransmitter.Run(runCtx) }() } - r.signalReady(nil) completionPool := newSlotCompletionPool(r.assembler, &r.slotResetMu, r.startPendingBlock, defaultSlotCompletionWorkers(), slotCompletionQueueDepth) r.completionPool = completionPool completionDone := make(chan struct{}) @@ -555,6 +555,18 @@ func (r *UDPReceiver) Run(ctx context.Context) error { r.consumeCompletionResults(runCtx, completionPool.results) }() + // Install the bounded decode/verification stage before any packet reader + // or spool hydrator can expose a DATA_COMPLETE component. Custom verifiers + // used by callers/tests retain the whole-block completion path. + var entryPrefetch *entryPrefetchPool + r.assembler.mu.Lock() + useEntryPrefetch := !sigverify.Cfg.DisableShredOverlap && r.assembler.verifyTransactions == nil + r.assembler.mu.Unlock() + if useEntryPrefetch { + entryPrefetch = newEntryPrefetchPool(runCtx, r.assembler, getDefaultTransactionVerifier()) + } + r.signalReady(nil) + go func() { <-runCtx.Done() _ = liveConn.Close() @@ -562,8 +574,13 @@ func (r *UDPReceiver) Run(ctx context.Context) error { _ = repairConn.Close() } }() + var repairDone chan struct{} if repairConn != nil { - go r.repairClient.run(runCtx, repairConn, r.assembler) + repairDone = make(chan struct{}) + go func() { + defer close(repairDone) + r.repairClient.run(runCtx, repairConn, r.assembler) + }() } var hydratorDone chan struct{} if r.spool != nil { @@ -604,6 +621,15 @@ func (r *UDPReceiver) Run(ctx context.Context) error { if retransmitDone != nil { <-retransmitDone } + if repairDone != nil { + <-repairDone + } + // Readers and hydration can no longer add components. Cancel and join + // the early stage before draining completion workers, while + // all transaction objects and the spool are still owned by this receiver. + if entryPrefetch != nil { + entryPrefetch.closeAndWait() + } // No submitters remain. Drain queued work while the spool hook and result // consumer are alive, then close results and join it before channel close. completionPool.closeAndWait() diff --git a/pkg/turbine/receiver_prefetch_lifecycle_test.go b/pkg/turbine/receiver_prefetch_lifecycle_test.go new file mode 100644 index 00000000..9a4311a4 --- /dev/null +++ b/pkg/turbine/receiver_prefetch_lifecycle_test.go @@ -0,0 +1,116 @@ +package turbine + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/Overclock-Validator/mithril/pkg/block" + "github.com/Overclock-Validator/mithril/pkg/gossip" + "github.com/Overclock-Validator/mithril/pkg/sigverify" + "github.com/stretchr/testify/require" +) + +func TestReceiverEntryPrefetchLifecycle(t *testing.T) { + for _, tc := range []struct { + name string + disabled bool + custom bool + wantPool bool + }{ + {name: "default", wantPool: true}, + {name: "disabled", disabled: true}, + {name: "custom verifier", custom: true}, + } { + t.Run(tc.name, func(t *testing.T) { + previous := sigverify.Cfg.DisableShredOverlap + sigverify.Cfg.DisableShredOverlap = tc.disabled + defer func() { sigverify.Cfg.DisableShredOverlap = previous }() + r := NewUDPReceiver("127.0.0.1:0") + if tc.custom { + r.assembler.verifyTransactions = func(context.Context, *block.Block) error { return nil } + } + ctx, cancel := context.WithCancel(context.Background()) + done := make(chan struct{}) + var runErr error + go func() { runErr = r.Run(ctx); close(done) }() + defer func() { + cancel() + select { + case <-done: + case <-time.After(3 * time.Second): + t.Error("receiver did not join on cleanup") + } + }() + select { + case err := <-r.Ready(): + require.NoError(t, err) + case <-time.After(3 * time.Second): + t.Fatal("receiver did not become ready") + } + r.assembler.mu.Lock() + attached := r.assembler.entryPrefetch != nil + r.assembler.mu.Unlock() + require.Equal(t, tc.wantPool, attached, "pool must be attached before readiness") + cancel() + waitSignal(t, done, "receiver shutdown") + require.NoError(t, runErr) + r.assembler.mu.Lock() + detached := r.assembler.entryPrefetch == nil + r.assembler.mu.Unlock() + require.True(t, detached, "shutdown must detach early workers from the assembler") + }) + } +} + +func TestReceiverStartupFailureDoesNotAttachEntryPrefetch(t *testing.T) { + r := NewUDPReceiver("invalid::udp::address") + require.Error(t, r.Run(context.Background())) + require.Error(t, <-r.Ready()) + require.Nil(t, r.assembler.entryPrefetch) + _, blocksOpen := <-r.Blocks() + require.False(t, blocksOpen) +} + +func TestReceiverShutdownJoinsRepairScheduler(t *testing.T) { + r := NewUDPReceiver("127.0.0.1:0") + started := make(chan struct{}) + release := make(chan struct{}) + var first sync.Once + var releaseOnce sync.Once + require.NoError(t, r.SetRepairPeerSource(nil, func() []gossip.RepairPeer { + first.Do(func() { close(started) }) + <-release + return nil + })) + ctx, cancel := context.WithCancel(context.Background()) + done := make(chan struct{}) + var runErr error + go func() { runErr = r.Run(ctx); close(done) }() + defer func() { + releaseOnce.Do(func() { close(release) }) + cancel() + select { + case <-done: + case <-time.After(3 * time.Second): + t.Error("receiver did not join on cleanup") + } + }() + select { + case err := <-r.Ready(): + require.NoError(t, err) + case <-time.After(3 * time.Second): + t.Fatal("receiver did not become ready") + } + waitSignal(t, started, "repair scheduler peer lookup") + cancel() + select { + case <-done: + t.Fatal("receiver returned while repair scheduler was still running") + case <-time.After(20 * time.Millisecond): + } + releaseOnce.Do(func() { close(release) }) + waitSignal(t, done, "repair scheduler joined") + require.NoError(t, runErr) +} diff --git a/pkg/turbine/transaction_verifier.go b/pkg/turbine/transaction_verifier.go index e64e3669..f8a1d27d 100644 --- a/pkg/turbine/transaction_verifier.go +++ b/pkg/turbine/transaction_verifier.go @@ -3,8 +3,8 @@ package turbine import ( "context" "fmt" - "runtime" "sync" + "time" "github.com/Overclock-Validator/mithril/pkg/block" "github.com/Overclock-Validator/mithril/pkg/sigverify" @@ -12,105 +12,251 @@ import ( "github.com/gagliardetto/solana-go" ) -var errNilTransaction = fmt.Errorf("nil transaction") +var ( + errNilTransaction = fmt.Errorf("nil transaction") + errTransactionVerifierClosed = fmt.Errorf("transaction verifier closed") +) +// A job is formed before admission, from transactions which are already +// available. Workers never wait for more transactions to fill a vector group. type transactionVerifyJob struct { - tx *solana.Transaction - err *error - done *sync.WaitGroup + txs []*solana.Transaction + errs []error + start int + done chan<- *transactionVerifyJob } -type transactionVerifier struct { - jobs chan transactionVerifyJob - verify func(*solana.Transaction) error - workers int - // wave is how many transactions verifyBlockContext admits at once. It is - // workers * sigverify.BatchTarget so each worker can actually accumulate a - // full vector group rather than being handed one transaction at a time. - wave int - close sync.Once +// transactionVerification owns an asynchronous request until done closes. +// Transactions submitted to it must remain immutable until wait returns. +type transactionVerification struct { + done chan struct{} + cancel context.CancelFunc + index int + err error + finishedAt time.Time +} - worker sync.WaitGroup +func (r *transactionVerification) wait() (int, error) { + return r.waitContext(context.Background()) } -func newTransactionVerifier(workers, queueDepth int, verify func(*solana.Transaction) error) *transactionVerifier { - if workers < 1 { - workers = 1 +// waitContext cancels further admission when ctx is canceled, but joins every +// admitted job before returning. The caller may then safely release or mutate +// the transaction objects, including their backing message byte slices. +func (r *transactionVerification) waitContext(ctx context.Context) (int, error) { + if ctx == nil { + ctx = context.Background() } - wave := workers * sigverify.BatchTarget - if queueDepth < 1 { - queueDepth = 1 + select { + case <-r.done: + case <-ctx.Done(): + r.cancel() + <-r.done + return -1, ctx.Err() } + if err := ctx.Err(); err != nil { + return -1, err + } + return r.index, r.err +} + +type transactionVerifier struct { + jobs chan *transactionVerifyJob + verify func(*solana.Transaction) error + workers int + batchTarget int + // Each accepted request owns at most workers outstanding groups. The + // admission semaphore also bounds asynchronous request goroutines; callers + // apply backpressure before handing off another decoded component. + requests chan struct{} + request sync.WaitGroup + mu sync.Mutex + closed bool + stopped chan struct{} + close sync.Once + worker sync.WaitGroup +} + +func newTransactionVerifier(workers, queueDepth int, verify func(*solana.Transaction) error) *transactionVerifier { + return newTransactionVerifierWithBatchTarget(workers, queueDepth, sigverify.BatchTarget, verify) +} + +// queueDepth is a transaction budget, rounded up to whole groups. batchTarget +// counts signature lanes: multi-signature transactions stay indivisible and +// may exceed the target. Four/eight targets can be compared without changing +// the admission or cancellation policy. +func newTransactionVerifierWithBatchTarget(workers, queueDepth, batchTarget int, verify func(*solana.Transaction) error) *transactionVerifier { + workers = max(1, workers) + batchTarget = max(1, min(batchTarget, sigverify.BatchTarget)) + queueGroups := max(1, (queueDepth+batchTarget-1)/batchTarget) v := &transactionVerifier{ - jobs: make(chan transactionVerifyJob, queueDepth), - verify: verify, - workers: workers, - wave: wave, + jobs: make(chan *transactionVerifyJob, queueGroups), + verify: verify, + workers: workers, + batchTarget: batchTarget, + requests: make(chan struct{}, 2*workers), + stopped: make(chan struct{}), } v.worker.Add(workers) for i := 0; i < workers; i++ { go func() { defer v.worker.Done() - // Worker-local scratch reused across groups. - var ( - group []transactionVerifyJob - scr verifyScratch - ) + var batch txverify.BatchVerifier for job := range v.jobs { - group = sigverify.Drain(group, job, v.jobs, - sigverify.FairShare(len(v.jobs), v.workers, sigverify.BatchTarget)) - v.verifyGroup(group, &scr) - // Do not keep finished jobs reachable through the scratch. - clear(group) + v.verifyGroup(job, &batch) } }() } return v } -// verifyScratch is one worker's reusable buffers. -type verifyScratch struct { - txs []*solana.Transaction - errs []error - batch txverify.BatchVerifier -} - -// verifyGroup verifies a drained group and releases every job in it. -// -// Releasing happens in a defer covering the whole group, so no caller can be -// left waiting on a job that was drained into a batch which then failed — -// a stranded job would hang verifyBlockContext's done.Wait() forever. -func (v *transactionVerifier) verifyGroup(group []transactionVerifyJob, scr *verifyScratch) { - defer func() { - for _, job := range group { - job.done.Done() - } - }() - - // An injected verifier is a per-transaction function and stays that way; - // only the default path can batch. This seam is used by tests. +// verifyGroup releases its job even if signature verification panics. The +// request's bounded completion channel always has room for every pending job. +func (v *transactionVerifier) verifyGroup(job *transactionVerifyJob, batch *txverify.BatchVerifier) { + defer func() { job.done <- job }() if v.verify != nil { - for _, job := range group { - *job.err = verifyTransactionSafely(v.verify, job.tx) + for i, tx := range job.txs { + if tx == nil { + job.errs[i] = errNilTransaction + } else { + job.errs[i] = verifyTransactionSafely(v.verify, tx) + } } return } + verifyBatchSafely(batch, job.txs, job.errs) +} - scr.txs = scr.txs[:0] - for _, job := range group { - scr.txs = append(scr.txs, job.tx) +// submitTransactions admits one immutable decoded component or complete block. +// Admission is bounded and may block: call it from a decode/completion worker, +// never the UDP reader or while holding the assembler mutex. Cancellation of +// ctx stops further groups but still joins every admitted group. +func (v *transactionVerifier) submitTransactions(ctx context.Context, txs []*solana.Transaction) (*transactionVerification, error) { + if ctx == nil { + ctx = context.Background() + } + if err := ctx.Err(); err != nil { + return nil, err } - if cap(scr.errs) < len(scr.txs) { - scr.errs = make([]error, len(scr.txs)) + select { + case v.requests <- struct{}{}: + case <-ctx.Done(): + return nil, ctx.Err() + case <-v.stopped: + return nil, errTransactionVerifierClosed } - scr.errs = scr.errs[:len(scr.txs)] - verifyBatchSafely(&scr.batch, scr.txs, scr.errs) + v.mu.Lock() + if v.closed { + v.mu.Unlock() + <-v.requests + return nil, errTransactionVerifierClosed + } + v.request.Add(1) + v.mu.Unlock() + ctx, cancel := context.WithCancel(ctx) + r := &transactionVerification{done: make(chan struct{}), cancel: cancel, index: -1} + go func() { + defer v.request.Done() + defer func() { <-v.requests }() + defer cancel() + r.index, r.err = v.verifyTransactions(ctx, txs) + r.finishedAt = time.Now() + close(r.done) + }() + return r, nil +} + +// verifyTransactions keeps a rolling window instead of waiting for an entire +// worker wave. A slow group cannot idle workers whose earlier groups finished. +// One caller can queue at most workers groups, so a large catch-up block cannot +// put all its transactions ahead of a newly available component. +func (v *transactionVerifier) verifyTransactions(ctx context.Context, txs []*solana.Transaction) (int, error) { + if len(txs) == 0 { + return -1, ctx.Err() + } + window := min(v.workers, len(txs)) + completed := make(chan *transactionVerifyJob, window) + groups := make([]transactionVerifyJob, window) + errs := make([]error, window*v.batchTarget) + free := make([]*transactionVerifyJob, window) + for i := range groups { + groups[i].errs = errs[i*v.batchTarget : (i+1)*v.batchTarget] + groups[i].done = completed + free[i] = &groups[i] + } - for i, job := range group { - *job.err = scr.errs[i] + nextIndex, active := 0, 0 + failureIndex := -1 + var failure error + var pending *transactionVerifyJob + ctxDone := ctx.Done() + stopped := false + for active > 0 || (!stopped && nextIndex < len(txs)) { + if !stopped && ctx.Err() != nil { + stopped = true + ctxDone = nil + } + if stopped && active == 0 { + break + } + if !stopped && pending == nil && nextIndex < len(txs) && len(free) > 0 { + pending = free[len(free)-1] + free = free[:len(free)-1] + end := transactionVerifyGroupEnd(txs, nextIndex, v.batchTarget) + pending.start = nextIndex + pending.txs = txs[nextIndex:end] + pending.errs = pending.errs[:end-nextIndex] + clear(pending.errs) + } + var admission chan *transactionVerifyJob + if !stopped && pending != nil { + admission = v.jobs + } + select { + case admission <- pending: + nextIndex += len(pending.txs) + active++ + pending = nil + case job := <-completed: + active-- + for i, err := range job.errs { + if err != nil && (failureIndex < 0 || job.start+i < failureIndex) { + failureIndex, failure = job.start+i, err + stopped = true + } + } + job.txs = nil + clear(job.errs) + free = append(free, job) + case <-ctxDone: + stopped = true + ctxDone = nil + } + } + if err := ctx.Err(); err != nil { + return -1, err + } + return failureIndex, failure +} + +func transactionVerifyGroupEnd(txs []*solana.Transaction, start, target int) int { + end, signatures := start, 0 + for end < len(txs) && end-start < target { + count := 1 + if txs[end] != nil { + count = max(1, len(txs[end].Signatures)) + } + if end > start && signatures+count > target { + break + } + signatures += count + end++ + if signatures >= target { + break + } } - clear(scr.txs) + return end } // verifyBatchSafely mirrors verifyTransactionSafely: a panic in the verifier @@ -134,6 +280,11 @@ func (v *transactionVerifier) closeAndWait() { return } v.close.Do(func() { + v.mu.Lock() + v.closed = true + close(v.stopped) + v.mu.Unlock() + v.request.Wait() close(v.jobs) v.worker.Wait() }) @@ -162,47 +313,15 @@ func (v *transactionVerifier) verifyBlockContext(ctx context.Context, blk *block if blk == nil || len(blk.Transactions) == 0 { return nil } - // Admit one worker-wave at a time. A monster block still occupies every - // verifier lane, but cannot park tens of thousands of jobs ahead of a newly - // completed small block in the shared bounded queue. - for chunkStart := 0; chunkStart < len(blk.Transactions); chunkStart += v.wave { - if err := ctx.Err(); err != nil { - return err - } - chunkEnd := min(chunkStart+v.wave, len(blk.Transactions)) - errs := make([]error, chunkEnd-chunkStart) - var done sync.WaitGroup - for txIdx := chunkStart; txIdx < chunkEnd; txIdx++ { - if err := ctx.Err(); err != nil { - done.Wait() - return err - } - tx := blk.Transactions[txIdx] - errIdx := txIdx - chunkStart - if tx == nil { - errs[errIdx] = errNilTransaction - continue - } - done.Add(1) - select { - case v.jobs <- transactionVerifyJob{tx: tx, err: &errs[errIdx], done: &done}: - case <-ctx.Done(): - done.Done() - done.Wait() - return ctx.Err() - } - } - done.Wait() - if err := ctx.Err(); err != nil { - return err - } - for errIdx, err := range errs { - if err != nil { - return formatTransactionVerificationError(blk, chunkStart+errIdx, err) - } - } + request, err := v.submitTransactions(ctx, blk.Transactions) + if err != nil { + return err + } + index, err := request.wait() + if err != nil && index >= 0 { + return formatTransactionVerificationError(blk, index, err) } - return nil + return err } func formatTransactionVerificationError(blk *block.Block, txIdx int, err error) error { @@ -226,12 +345,17 @@ var ( defaultTransactionVerifier *transactionVerifier ) -func validateBlockTransactionsContext(ctx context.Context, blk *block.Block) error { +func getDefaultTransactionVerifier() *transactionVerifier { defaultTransactionVerifierOnce.Do(func() { - workers := max(1, (runtime.GOMAXPROCS(0)+1)/2) - defaultTransactionVerifier = newTransactionVerifier(workers, 2*workers*sigverify.BatchTarget, nil) + workers := sigverify.TransactionWorkers() + target := sigverify.TransactionBatchTarget() + defaultTransactionVerifier = newTransactionVerifierWithBatchTarget(workers, 2*workers*target, target, nil) }) - return defaultTransactionVerifier.verifyBlockContext(ctx, blk) + return defaultTransactionVerifier +} + +func validateBlockTransactionsContext(ctx context.Context, blk *block.Block) error { + return getDefaultTransactionVerifier().verifyBlockContext(ctx, blk) } func validateBlockTransactions(blk *block.Block) error { diff --git a/pkg/turbine/transaction_verifier_flow_benchmark_test.go b/pkg/turbine/transaction_verifier_flow_benchmark_test.go new file mode 100644 index 00000000..0c8ce131 --- /dev/null +++ b/pkg/turbine/transaction_verifier_flow_benchmark_test.go @@ -0,0 +1,479 @@ +package turbine + +import ( + "context" + "crypto/ed25519" + "encoding/base64" + "encoding/binary" + "encoding/json" + "fmt" + "os" + "path/filepath" + "sort" + "strconv" + "sync" + "syscall" + "testing" + "time" + + "github.com/Overclock-Validator/mithril/pkg/block" + "github.com/Overclock-Validator/mithril/pkg/sigverify" + "github.com/Overclock-Validator/mithril/pkg/txverify" + "github.com/gagliardetto/solana-go" +) + +// BenchmarkTransactionVerificationFlow measures the real verifier pool under +// simulated transaction availability, not actual network reception or replay. +// Decode and fixture generation are outside the timer. "tip" spreads complete +// components over 200 ms; this is an explicit workload model, not a claim about +// the cluster's observed component sizes or arrival distribution. No component +// waits for another component to fill a verification batch. +// +// Optional environment: +// +// MITHRIL_SIGVERIFY_FLOW_BACKEND=r51 (use -run '^$' in a fresh test process) +// MITHRIL_SIGVERIFY_FLOW_FIXTURES=/path/to/fixtures (block-*.json, base64 txs) +// MITHRIL_SIGVERIFY_FLOW_COUNT=33760 (generated count or captured prefix limit) +// +// Without captured fixtures, use reproducible signed 228-byte and 1232-byte +// legacy memo transactions. They are signature workloads, not replay fixtures. +// Use -benchtime=3x or another fixed count when comparing configurations so the +// deliberate arrival waits do not change the number of observations. +func BenchmarkTransactionVerificationFlow(b *testing.B) { + flowConfigureBackend(b) + for _, fixture := range flowBenchmarkFixtures(b) { + b.Run(fixture.name, func(b *testing.B) { + for _, workers := range []int{2, 4} { + b.Run(fmt.Sprintf("workers_%d", workers), func(b *testing.B) { + for _, target := range []int{4, 8} { + b.Run(fmt.Sprintf("target_%d", target), func(b *testing.B) { + for _, scenario := range flowScenarios(fixture.blk) { + b.Run(scenario.name, func(b *testing.B) { + flowRunBenchmark(b, workers, target, fixture.blk, scenario) + }) + } + }) + } + }) + } + }) + } +} + +type flowFixture struct { + name string + blk *block.Block +} + +type flowScenario struct { + name string + components [][]*solana.Transaction + arrivalSpan time.Duration + overlap bool +} + +func flowScenarios(blk *block.Block) []flowScenario { + // 60 KiB is a benchmark parameter only. Include signed transaction bytes; + // actual serialized entry/component overhead and shred recovery are omitted. + components := flowComponents(blk.Transactions, 60*1024) + scenarios := []flowScenario{ + {name: "catchup", components: [][]*solana.Transaction{blk.Transactions}}, + {name: "tip_200ms_after_complete", components: components, arrivalSpan: 200 * time.Millisecond}, + {name: "tip_200ms_overlap", components: components, arrivalSpan: 200 * time.Millisecond, overlap: true}, + } + // Sparse components expose tail behavior without requiring microsecond + // timer precision to deliver tens of thousands of tiny arrival events. + for _, width := range []int{4, 7, 8} { + txs := blk.Transactions[:min(256, len(blk.Transactions))] + var small [][]*solana.Transaction + for start := 0; start < len(txs); start += width { + small = append(small, txs[start:min(start+width, len(txs))]) + } + scenarios = append(scenarios, flowScenario{ + name: fmt.Sprintf("sparse_%dtx_200ms_overlap", width), components: small, + arrivalSpan: 200 * time.Millisecond, overlap: true, + }) + } + return scenarios +} + +func flowComponents(txs []*solana.Transaction, bytesPerComponent int) [][]*solana.Transaction { + var components [][]*solana.Transaction + start, size := 0, 0 + for i, tx := range txs { + wireSize, err := txverify.TransactionWireSize(tx) + if err != nil { + panic(err) // fixtures are validated before entering this helper + } + if i > start && size+wireSize > bytesPerComponent { + components = append(components, txs[start:i]) + start, size = i, 0 + } + size += wireSize + } + if start < len(txs) { + components = append(components, txs[start:]) + } + return components +} + +type flowObservation struct { + latencies []time.Duration + submits []time.Duration + feedLags []time.Duration + residual time.Duration + err error +} + +func flowArrivalOffset(index, count int, span time.Duration) time.Duration { + if count <= 1 { + return span + } + return time.Duration(int64(span) * int64(index) / int64(count-1)) +} + +func flowObserve(v *transactionVerifier, blk *block.Block, scenario flowScenario) flowObservation { + observation := flowObservation{ + latencies: make([]time.Duration, len(scenario.components)), + submits: make([]time.Duration, len(scenario.components)), + feedLags: make([]time.Duration, len(scenario.components)), + } + started := time.Now() + finalArrival := started.Add(scenario.arrivalSpan) + if !scenario.overlap { + time.Sleep(time.Until(finalArrival)) + submitStarted := time.Now() + future, err := v.submitTransactions(context.Background(), blk.Transactions) + submitDuration := time.Since(submitStarted) + if err != nil { + observation.err = err + return observation + } + _, observation.err = future.wait() + finished := future.finishedAt + for i := range observation.latencies { + available := started.Add(flowArrivalOffset(i, len(scenario.components), scenario.arrivalSpan)) + observation.latencies[i] = finished.Sub(available) + observation.submits[i] = submitDuration + observation.feedLags[i] = submitStarted.Sub(available) + } + observation.residual = max(0, finished.Sub(finalArrival)) + return observation + } + + var waiters sync.WaitGroup + errs := make([]error, len(scenario.components)) + finished := make([]time.Time, len(scenario.components)) + for i, txs := range scenario.components { + available := started.Add(flowArrivalOffset(i, len(scenario.components), scenario.arrivalSpan)) + time.Sleep(time.Until(available)) + submitStarted := time.Now() + observation.feedLags[i] = submitStarted.Sub(available) + future, err := v.submitTransactions(context.Background(), txs) + observation.submits[i] = time.Since(submitStarted) + if err != nil { + errs[i] = err + break + } + waiters.Add(1) + go func() { + defer waiters.Done() + _, errs[i] = future.wait() + finished[i] = future.finishedAt + observation.latencies[i] = finished[i].Sub(available) + }() + } + waiters.Wait() + for _, completed := range finished { + observation.residual = max(observation.residual, completed.Sub(finalArrival)) + } + for _, err := range errs { + if err != nil { + observation.err = err + break + } + } + return observation +} + +func flowRunBenchmark(b *testing.B, workers, target int, blk *block.Block, scenario flowScenario) { + v := newTransactionVerifierWithBatchTarget(workers, 2*workers*8, target, nil) + defer v.closeAndWait() + if err := v.verifyBlock(blk); err != nil { + b.Fatal(err) + } + flowBenchmarkGate(b) + var signatureCount int + for _, component := range scenario.components { + for _, tx := range component { + signatureCount += len(tx.Signatures) + } + } + var latencies, submits, feedLags, residuals []time.Duration + before := sigverify.Stats() + cpuBefore := flowCPUSeconds(b) + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + observation := flowObserve(v, blk, scenario) + if observation.err != nil { + b.Fatal(observation.err) + } + latencies = append(latencies, observation.latencies...) + submits = append(submits, observation.submits...) + feedLags = append(feedLags, observation.feedLags...) + residuals = append(residuals, observation.residual) + } + b.StopTimer() + cpuSeconds := flowCPUSeconds(b) - cpuBefore + after := sigverify.Stats() + b.ReportMetric(1000*cpuSeconds/float64(b.N), "cpu-ms/block") + b.ReportMetric(cpuSeconds/b.Elapsed().Seconds(), "avg_cpu_cores") + b.ReportMetric(float64(b.N*signatureCount)/b.Elapsed().Seconds(), "signatures/s") + b.ReportMetric(float64(signatureCount), "signatures/block") + b.ReportMetric(float64(len(scenario.components)), "components/block") + b.ReportMetric(float64(after.Signatures-before.Signatures)/float64(after.Batches-before.Batches), "mean_width") + flowReportPercentiles(b, latencies, "ready") + flowReportPercentiles(b, submits, "submit") + flowReportPercentiles(b, feedLags, "feed_lag") + flowReportPercentiles(b, residuals, "residual") + if after.InternalFaultFallbacks != before.InternalFaultFallbacks { + b.Fatal("signature verifier used an internal fault fallback") + } + if want := uint64(b.N * signatureCount); after.Signatures-before.Signatures != want { + b.Fatalf("verified signature count = %d, want %d", after.Signatures-before.Signatures, want) + } +} + +// The optional rendezvous is outside the timer. An external contention runner +// starts the real execution probe after seeing READY, then creates START. Both +// paths are explicit files in that runner's output directory. This is only for +// coordination; ordinary benchmark invocations do no filesystem polling. +func flowBenchmarkGate(b *testing.B) { + b.Helper() + ready, start := os.Getenv("MITHRIL_SIGVERIFY_FLOW_READY"), os.Getenv("MITHRIL_SIGVERIFY_FLOW_START") + if ready == "" && start == "" { + return + } + if ready == "" || start == "" { + b.Fatal("set both MITHRIL_SIGVERIFY_FLOW_READY and MITHRIL_SIGVERIFY_FLOW_START") + } + // Go's first N=1 calibration and warmup must finish before the external + // execution probe starts. The runner selects a fixed N greater than one. + if b.N == 1 { + return + } + if err := os.WriteFile(ready, []byte(b.Name()+"\n"), 0600); err != nil { + b.Fatal(err) + } + deadline := time.Now().Add(30 * time.Second) + for { + if _, err := os.Stat(start); err == nil { + return + } else if !os.IsNotExist(err) { + b.Fatal(err) + } + if time.Now().After(deadline) { + b.Fatal("contention runner did not release benchmark within 30 seconds") + } + time.Sleep(time.Millisecond) + } +} + +func flowReportPercentiles(b *testing.B, values []time.Duration, prefix string) { + sort.Slice(values, func(i, j int) bool { return values[i] < values[j] }) + for _, percentile := range []int{50, 95} { + index := max(0, (len(values)*percentile+99)/100-1) + b.ReportMetric(float64(values[index])/float64(time.Millisecond), fmt.Sprintf("%s_p%d-ms", prefix, percentile)) + } +} + +func flowCPUSeconds(tb testing.TB) float64 { + tb.Helper() + var usage syscall.Rusage + if err := syscall.Getrusage(syscall.RUSAGE_SELF, &usage); err != nil { + tb.Fatal(err) + } + return float64(usage.Utime.Sec+usage.Stime.Sec) + float64(usage.Utime.Usec+usage.Stime.Usec)/1e6 +} + +var flowBackendOnce sync.Once +var flowBackendError error + +func flowConfigureBackend(tb testing.TB) { + tb.Helper() + flowBackendOnce.Do(func() { + if backend := os.Getenv("MITHRIL_SIGVERIFY_FLOW_BACKEND"); backend != "" { + _, flowBackendError = sigverify.Configure(sigverify.Config{Backend: backend}) + } + }) + if flowBackendError != nil { + tb.Fatal(flowBackendError) + } +} + +func flowBenchmarkFixtures(tb testing.TB) []flowFixture { + tb.Helper() + count := 33760 + limit := false + if value := os.Getenv("MITHRIL_SIGVERIFY_FLOW_COUNT"); value != "" { + var err error + count, err = strconv.Atoi(value) + if err != nil || count < 1 { + tb.Fatal("MITHRIL_SIGVERIFY_FLOW_COUNT must be a positive integer") + } + limit = true + } + if dir := os.Getenv("MITHRIL_SIGVERIFY_FLOW_FIXTURES"); dir != "" { + paths, err := filepath.Glob(filepath.Join(dir, "block-*.json")) + if err != nil || len(paths) == 0 { + tb.Fatalf("captured fixtures: %v, files=%d", err, len(paths)) + } + var fixtures []flowFixture + for _, path := range paths { + data, err := os.ReadFile(path) + if err != nil { + tb.Fatal(err) + } + var captured struct { + Slot uint64 + Transactions []string + } + if err := json.Unmarshal(data, &captured); err != nil { + tb.Fatal(err) + } + blk := &block.Block{Slot: captured.Slot} + for i, encoded := range captured.Transactions { + if limit && i == count { + break + } + wire, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + tb.Fatal(err) + } + tx, err := solana.TransactionFromBytes(wire) + if err != nil { + tb.Fatal(err) + } + if err := txverify.SanitizeTransaction(tx); err != nil { + tb.Fatal(err) + } + blk.Transactions = append(blk.Transactions, tx) + } + if len(blk.Transactions) == 0 { + tb.Fatalf("fixture %s contains no transactions", path) + } + fixtures = append(fixtures, flowFixture{fmt.Sprintf("captured_%d", blk.Slot), blk}) + } + return fixtures + } + var fixtures []flowFixture + for _, wireSize := range []int{228, txverify.MaxLegacyTransactionSize} { + blk := &block.Block{Slot: 1, Transactions: make([]*solana.Transaction, count)} + for i := range blk.Transactions { + blk.Transactions[i] = flowGeneratedTransaction(tb, wireSize, uint64(i)) + } + fixtures = append(fixtures, flowFixture{fmt.Sprintf("generated_%dB", wireSize), blk}) + } + return fixtures +} + +func flowGeneratedTransaction(tb testing.TB, wireSize int, index uint64) *solana.Transaction { + tb.Helper() + // Public, deterministic benchmark material, never a validator identity. + var seed [ed25519.SeedSize]byte + binary.LittleEndian.PutUint64(seed[:], index+1) + private := ed25519.NewKeyFromSeed(seed[:]) + public := solana.PublicKeyFromBytes(private.Public().(ed25519.PublicKey)) + tx := &solana.Transaction{ + Signatures: make([]solana.Signature, 1), + Message: solana.Message{ + Header: solana.MessageHeader{NumRequiredSignatures: 1, NumReadonlyUnsignedAccounts: 1}, + AccountKeys: []solana.PublicKey{public, solana.MemoProgramID}, + Instructions: []solana.CompiledInstruction{{ProgramIDIndex: 1, Accounts: []uint16{0}}}, + }, + } + binary.LittleEndian.PutUint64(tx.Message.RecentBlockhash[:], index+1) + baseSize, err := txverify.TransactionWireSize(tx) + if err != nil { + tb.Fatal(err) + } + padding := wireSize - baseSize + for attempts := 0; attempts < 3 && padding >= 0; attempts++ { + tx.Message.Instructions[0].Data = make([]byte, padding) + size, err := txverify.TransactionWireSize(tx) + if err != nil { + tb.Fatal(err) + } + if size != wireSize { + padding += wireSize - size + continue + } + for i := range tx.Message.Instructions[0].Data { + tx.Message.Instructions[0].Data[i] = 'a' + } + message, err := txverify.MessageBytes(tx) + if err != nil { + tb.Fatal(err) + } + copy(tx.Signatures[0][:], ed25519.Sign(private, message)) + if err := txverify.SanitizeTransaction(tx); err != nil { + tb.Fatal(err) + } + return tx + } + tb.Fatalf("cannot construct a %d-byte transaction", wireSize) + return nil +} + +func TestTransactionVerificationFlowFixtureShape(t *testing.T) { + for _, size := range []int{228, 1232} { + first := flowGeneratedTransaction(t, size, 0) + second := flowGeneratedTransaction(t, size, 1) + for _, tx := range []*solana.Transaction{first, second} { + wire, err := tx.MarshalBinary() + if err != nil { + t.Fatal(err) + } + if len(wire) != size || len(tx.Signatures) != 1 { + t.Fatalf("fixture wire=%d signatures=%d, want %d bytes and one signature", len(wire), len(tx.Signatures), size) + } + if err := txverify.VerifyTransaction(tx); err != nil { + t.Fatal(err) + } + } + if first.Signatures[0] == second.Signatures[0] || first.Message.AccountKeys[0] == second.Message.AccountKeys[0] { + t.Fatal("different fixture indices reused a message signature or signer") + } + } +} + +func TestTransactionVerificationFlowComponentBoundaries(t *testing.T) { + txs := make([]*solana.Transaction, 7) + for i := range txs { + txs[i] = flowGeneratedTransaction(t, 228, uint64(i)) + } + components := flowComponents(txs, 500) + if len(components) != 4 { + t.Fatalf("got %d components, want four", len(components)) + } + next := 0 + for i, component := range components { + want := 2 + if i == 3 { + want = 1 + } + if len(component) != want { + t.Fatalf("component %d contains %d transactions, want %d", i, len(component), want) + } + for _, tx := range component { + if tx != txs[next] { + t.Fatal("component split changed transaction order or identity") + } + next++ + } + } + if flowArrivalOffset(0, 4, 200*time.Millisecond) != 0 || flowArrivalOffset(3, 4, 200*time.Millisecond) != 200*time.Millisecond { + t.Fatal("availability schedule does not span the requested interval") + } +} diff --git a/pkg/turbine/transaction_verifier_test.go b/pkg/turbine/transaction_verifier_test.go index 8eb19c63..602fa704 100644 --- a/pkg/turbine/transaction_verifier_test.go +++ b/pkg/turbine/transaction_verifier_test.go @@ -1,9 +1,12 @@ package turbine import ( + "context" + "crypto/ed25519" "errors" "fmt" "strings" + "sync" "sync/atomic" "testing" "time" @@ -43,12 +46,12 @@ func TestTransactionVerifierBoundsConcurrencyAndQueue(t *testing.T) { return nil }) defer verifier.closeAndWait() - if cap(verifier.jobs) != 2*workers { - t.Fatalf("queue capacity = %d, want %d", cap(verifier.jobs), 2*workers) + if got, want := cap(verifier.jobs), 1; got != want { + t.Fatalf("group queue capacity = %d, want %d", got, want) } done := make(chan error, 1) - go func() { done <- verifier.verifyBlock(verifierTestBlock(12)) }() + go func() { done <- verifier.verifyBlock(verifierTestBlock(24)) }() deadline := time.After(3 * time.Second) for active.Load() != workers { select { @@ -76,7 +79,7 @@ func TestTransactionVerifierBoundsConcurrencyAndQueue(t *testing.T) { } func TestTransactionVerifierReturnsLowestFailingIndex(t *testing.T) { - blk := verifierTestBlock(6) + blk := verifierTestBlock(24) lowErr := errors.New("low index failure") highErr := errors.New("high index failure") verifier := newTransactionVerifier(4, 8, func(tx *solana.Transaction) error { @@ -84,7 +87,7 @@ func TestTransactionVerifierReturnsLowestFailingIndex(t *testing.T) { case blk.Transactions[1]: time.Sleep(10 * time.Millisecond) return lowErr - case blk.Transactions[3]: + case blk.Transactions[9]: return highErr default: return nil @@ -132,8 +135,8 @@ func TestTransactionVerifierRejectsNilAtDeterministicIndex(t *testing.T) { // Every transaction in a block must be verified and joined, whatever the count. // Workers group transactions, so a count that divides badly into groups must -// not leave a remainder waiting for company: verifyBlockContext joins each -// wave with done.Wait(), and a stranded job would hang it forever. +// not leave a remainder waiting for company: every tail is dispatched as +// soon as it is available, without waiting for another request. // // A counting verifier is injected so the assertion is on what was actually // verified, not merely on returning without error. @@ -161,3 +164,284 @@ func TestTransactionVerifierVerifiesEveryTransactionForAwkwardCounts(t *testing. }) } } + +func TestTransactionVerifierRefillsWhileEarlierGroupIsBlocked(t *testing.T) { + blk := verifierTestBlock(24) + started := make(chan struct{}) + release := make(chan struct{}) + refilled := make(chan struct{}) + v := newTransactionVerifierWithBatchTarget(2, 16, 8, func(tx *solana.Transaction) error { + switch tx { + case blk.Transactions[0]: + close(started) + <-release + case blk.Transactions[16]: + close(refilled) + } + return nil + }) + defer v.closeAndWait() + defer close(release) + r, err := v.submitTransactions(context.Background(), blk.Transactions) + require.NoError(t, err) + waitSignal(t, started, "slow first group") + waitSignal(t, refilled, "rolling refill before first group finishes") + select { + case <-r.done: + t.Fatal("request finished without joining its blocked group") + default: + } +} + +func TestTransactionVerifierPartialTailStartsWithoutAnotherSubmission(t *testing.T) { + for _, target := range []int{4, 8} { + t.Run(fmt.Sprintf("target=%d", target), func(t *testing.T) { + seen := make(chan struct{}, 3) + v := newTransactionVerifierWithBatchTarget(2, 16, target, func(*solana.Transaction) error { + seen <- struct{}{} + return nil + }) + defer v.closeAndWait() + r, err := v.submitTransactions(context.Background(), verifierTestBlock(3).Transactions) + require.NoError(t, err) + for range 3 { + waitSignal(t, seen, "available partial batch transaction") + } + _, err = r.wait() + require.NoError(t, err) + require.False(t, r.finishedAt.IsZero()) + }) + } +} + +func TestTransactionVerifierLargeRequestDoesNotQueuePastSmallRequest(t *testing.T) { + large := verifierTestBlock(800) + small := verifierTestBlock(1) + started := make(chan struct{}) + release := make(chan struct{}) + var releaseOnce sync.Once + var largeCalls atomic.Int32 + var callsBeforeSmall atomic.Int32 + v := newTransactionVerifierWithBatchTarget(1, 16, 8, func(tx *solana.Transaction) error { + if tx == small.Transactions[0] { + callsBeforeSmall.Store(largeCalls.Load()) + return nil + } + largeCalls.Add(1) + if tx == large.Transactions[0] { + close(started) + <-release + } + return nil + }) + defer v.closeAndWait() + defer releaseOnce.Do(func() { close(release) }) + largeRequest, err := v.submitTransactions(context.Background(), large.Transactions) + require.NoError(t, err) + waitSignal(t, started, "large request first group") + smallRequest, err := v.submitTransactions(context.Background(), small.Transactions) + require.NoError(t, err) + require.Eventually(t, func() bool { return len(v.jobs) == 1 }, 3*time.Second, time.Millisecond) + releaseOnce.Do(func() { close(release) }) + _, err = smallRequest.wait() + require.NoError(t, err) + require.Equal(t, int32(8), callsBeforeSmall.Load(), "large request may only stay one group ahead") + _, err = largeRequest.wait() + require.NoError(t, err) + require.Equal(t, int32(800), largeCalls.Load()) +} + +func TestTransactionVerifierAsyncAdmissionAppliesCancelableBackpressure(t *testing.T) { + started := make(chan struct{}) + release := make(chan struct{}) + var first sync.Once + v := newTransactionVerifierWithBatchTarget(1, 8, 8, func(*solana.Transaction) error { + first.Do(func() { close(started) }) + <-release + return nil + }) + defer v.closeAndWait() + defer close(release) + for range 2 { + _, err := v.submitTransactions(context.Background(), verifierTestBlock(1).Transactions) + require.NoError(t, err) + } + waitSignal(t, started, "occupied request slots") + require.Equal(t, cap(v.requests), len(v.requests)) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + result := make(chan error, 1) + go func() { + _, err := v.submitTransactions(ctx, verifierTestBlock(1).Transactions) + result <- err + }() + select { + case err := <-result: + t.Fatalf("unbounded request admitted instead of waiting: %v", err) + case <-time.After(20 * time.Millisecond): + } + cancel() + select { + case err := <-result: + require.ErrorIs(t, err, context.Canceled) + case <-time.After(3 * time.Second): + t.Fatal("request admission ignored cancellation") + } +} + +func TestTransactionVerificationWaitContextCancelsAndJoins(t *testing.T) { + started := make(chan struct{}) + release := make(chan struct{}) + var releaseOnce sync.Once + var seen atomic.Int32 + v := newTransactionVerifierWithBatchTarget(1, 8, 8, func(*solana.Transaction) error { + if seen.Add(1) == 1 { + close(started) + <-release + } + return nil + }) + defer v.closeAndWait() + defer releaseOnce.Do(func() { close(release) }) + r, err := v.submitTransactions(context.Background(), verifierTestBlock(800).Transactions) + require.NoError(t, err) + waitSignal(t, started, "first admitted group") + ctx, cancel := context.WithCancel(context.Background()) + cancel() + result := make(chan error, 1) + go func() { _, err := r.waitContext(ctx); result <- err }() + select { + case err := <-result: + t.Fatalf("wait returned while transactions still owned by worker: %v", err) + case <-time.After(20 * time.Millisecond): + } + releaseOnce.Do(func() { close(release) }) + select { + case err := <-result: + require.ErrorIs(t, err, context.Canceled) + case <-time.After(3 * time.Second): + t.Fatal("canceled request did not join") + } + require.Equal(t, int32(8), seen.Load(), "cancellation must stop later group admission") +} + +func TestTransactionVerifierCloseRacesAdmissionWithoutStrandingRequests(t *testing.T) { + v := newTransactionVerifier(2, 16, func(*solana.Transaction) error { return nil }) + var callers sync.WaitGroup + for range 32 { + callers.Go(func() { + r, err := v.submitTransactions(context.Background(), verifierTestBlock(17).Transactions) + if err != nil { + if !errors.Is(err, errTransactionVerifierClosed) { + t.Errorf("submit error: %v", err) + } + return + } + _, err = r.wait() + if err != nil { + t.Errorf("admitted request error: %v", err) + } + }) + } + v.closeAndWait() + callers.Wait() + _, err := v.submitTransactions(context.Background(), verifierTestBlock(1).Transactions) + require.ErrorIs(t, err, errTransactionVerifierClosed) +} + +func verifierSignedTransactions(t *testing.T, count int) []*solana.Transaction { + t.Helper() + seed := make([]byte, ed25519.SeedSize) + seed[0] = 71 // Deterministic test-only key; never a validator identity. + key := ed25519.NewKeyFromSeed(seed) + var public solana.PublicKey + copy(public[:], key[32:]) + txs := make([]*solana.Transaction, count) + for i := range txs { + tx := &solana.Transaction{ + Message: solana.Message{ + Header: solana.MessageHeader{NumRequiredSignatures: 1}, + AccountKeys: []solana.PublicKey{public}, + RecentBlockhash: solana.Hash{byte(i), byte(i >> 8)}, + }, + Signatures: make([]solana.Signature, 1), + } + message, err := tx.Message.MarshalBinary() + require.NoError(t, err) + copy(tx.Signatures[0][:], ed25519.Sign(key, message)) + txs[i] = tx + } + return txs +} + +func TestTransactionVerifierRejectsEveryInvalidSignatureLane(t *testing.T) { + v := newTransactionVerifier(2, 16, nil) + defer v.closeAndWait() + for invalid := range 8 { + t.Run(fmt.Sprintf("lane=%d", invalid), func(t *testing.T) { + txs := verifierSignedTransactions(t, 8) + txs[invalid].Signatures[0][13] ^= 0x40 + r, err := v.submitTransactions(context.Background(), txs) + require.NoError(t, err) + index, err := r.wait() + require.ErrorContains(t, err, "invalid signature") + require.Equal(t, invalid, index) + }) + } + r, err := v.submitTransactions(context.Background(), verifierSignedTransactions(t, 17)) + require.NoError(t, err) + _, err = r.wait() + require.NoError(t, err, "valid transactions must still verify after invalid lanes") +} + +func TestTransactionVerifierKeepsMultisignatureTransactionsIntactAcrossTargets(t *testing.T) { + counts := []int{2, 2, 1, 4, 9, 3, 2, 1} + txs := make([]*solana.Transaction, len(counts)) + for i, count := range counts { + keys := make([]ed25519.PrivateKey, count) + public := make([]solana.PublicKey, count) + for signer := range keys { + seed := make([]byte, ed25519.SeedSize) + seed[0], seed[1] = 83, byte(signer) + keys[signer] = ed25519.NewKeyFromSeed(seed) + copy(public[signer][:], keys[signer][32:]) + } + tx := &solana.Transaction{ + Message: solana.Message{ + Header: solana.MessageHeader{ + NumRequiredSignatures: uint8(count), + NumReadonlySignedAccounts: uint8(count - 1), + }, + AccountKeys: public, + RecentBlockhash: solana.Hash{byte(i)}, + }, + Signatures: make([]solana.Signature, count), + } + message, err := tx.Message.MarshalBinary() + require.NoError(t, err) + for signer, key := range keys { + copy(tx.Signatures[signer][:], ed25519.Sign(key, message)) + } + txs[i] = tx + } + for _, target := range []int{4, 8} { + t.Run(fmt.Sprintf("target=%d", target), func(t *testing.T) { + v := newTransactionVerifierWithBatchTarget(2, 16, target, nil) + defer v.closeAndWait() + r, err := v.submitTransactions(context.Background(), txs) + require.NoError(t, err) + _, err = r.wait() + require.NoError(t, err) + + // Corrupt a non-first signer after an oversized (nine-signature) + // transaction. Results must still map to the original tx index. + txs[6].Signatures[1][11] ^= 0x20 + r, err = v.submitTransactions(context.Background(), txs) + require.NoError(t, err) + index, err := r.wait() + require.ErrorContains(t, err, "invalid signature") + require.Equal(t, 6, index) + txs[6].Signatures[1][11] ^= 0x20 + }) + } +} From 443ab5170af009874faacf1268a646e2dec6d4fa Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sat, 12 Sep 2026 01:26:45 -0500 Subject: [PATCH 03/22] Document Zen 5 streaming verification and execution contention results --- .../2026-09-12-zen5/BENCHMARKS.md | 113 + .../2026-09-12-zen5/README.md | 152 + .../2026-09-12-zen5/assembly-1232.log | 29 + .../2026-09-12-zen5/assembly-captured.log | 29 + .../2026-09-12-zen5/assembly-comparison.json | 1602 +++++ .../2026-09-12-zen5/binary-sha256.txt | 3 + .../2026-09-12-zen5/captured-pool-flow.log | 113 + .../2026-09-12-zen5/execution-comparison.json | 1653 +++++ .../2026-09-12-zen5/execution-contention.log | 162 + .../2026-09-12-zen5/execution-raw.log | 576 ++ .../2026-09-12-zen5/final-commands.jsonl | 6 + .../2026-09-12-zen5/final-source-update.json | 30 + .../generated-1232-pool-flow.log | 77 + .../2026-09-12-zen5/health-after-final.txt | 4 + .../2026-09-12-zen5/local-replay.log | 1 + .../2026-09-12-zen5/local-turbine-race.log | 1 + .../2026-09-12-zen5/local-unit.log | 7 + .../2026-09-12-zen5/native-final-race.log | 1 + .../2026-09-12-zen5/native-final-unit.log | 6 + .../2026-09-12-zen5/pool-comparison.json | 5503 +++++++++++++++++ .../run_execution_contention.py | 231 + docs/transaction_sigverify_streaming.md | 3 + 22 files changed, 10302 insertions(+) create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/BENCHMARKS.md create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/README.md create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/assembly-1232.log create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/assembly-captured.log create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/assembly-comparison.json create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/binary-sha256.txt create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/captured-pool-flow.log create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/execution-comparison.json create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/execution-contention.log create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/execution-raw.log create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/final-commands.jsonl create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/final-source-update.json create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/generated-1232-pool-flow.log create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/health-after-final.txt create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/local-replay.log create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/local-turbine-race.log create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/local-unit.log create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/native-final-race.log create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/native-final-unit.log create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/pool-comparison.json create mode 100644 docs/results/sigverify-streaming/2026-09-12-zen5/run_execution_contention.py diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/BENCHMARKS.md b/docs/results/sigverify-streaming/2026-09-12-zen5/BENCHMARKS.md new file mode 100644 index 00000000..454f75e3 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/BENCHMARKS.md @@ -0,0 +1,113 @@ +# Signature batching and overlap benchmark harness + +The repository adds `pkg/turbine/transaction_verifier_flow_benchmark_test.go`. +It uses the production transaction verifier with two/four workers and signature +batch targets four/eight. It verifies every signature and rejects internal native +fallbacks. Generated inputs have distinct, valid signatures and exact serialized +sizes of 228 or 1,232 bytes. Optional captured public block transactions replace +generated inputs. + +These are controlled verifier-pool measurements, **not complete validator replay +or a recorded shred-arrival trace**. Transaction decoding and fixture generation +are outside the timer. Each component is submitted as soon as its scheduled bytes +are available; partial vector groups never wait for another component. + +## Workloads + +- `catchup`: all transactions are available immediately in one request. +- `tip_200ms_after_complete`: components become available across 200 ms, then the + full block is submitted. This isolates the cost of waiting for block completion + with the same improved batching pool. +- `tip_200ms_overlap`: complete components become available across 200 ms and are + submitted immediately. Components contain at most 60 KiB of transaction bytes. + This is a synthetic workload parameter; serialized entry overhead, shred + recovery, and actual cluster component boundaries are omitted. +- `sparse_{4,7,8}tx_200ms_overlap`: at most 256 transactions arrive in tiny complete + components across 200 ms, exposing partial-batch latency without a high-rate + sub-millisecond network timer simulation. + +For each measured run, `cpu-ms/block` is process user+system CPU time summed across +cores, including pool/message preparation, scheduling, allocations and benchmark +bookkeeping. `signatures/s` in 200 ms workloads includes the deliberate network +availability waits; it is delivered workload throughput, not peak crypto speed. + +`ready_p95-ms` measures scheduled component availability to that component's +verification future completing. It is a component latency, not individual +transaction execution latency. `submit_p95-ms` measures time inside bounded pool +submission. `feed_lag_p95-ms` captures lateness between scheduled availability and +calling submission, including timer granularity, CPU contention and backlog from +an earlier blocked submission. `residual_p95-ms` measures final scheduled +availability to the final future completing. Completion timestamps come from the +pool, so a delayed observer goroutine does not inflate completion latency. + +Capture/profile workload delays alongside residual: delayed synthetic arrivals +must not be described as successfully hidden signature work. + +## Build and run on Zen 5 + +Build fresh package test executables from the candidate checkout with the native +Go toolchain; run `-test.run='^$'` when selecting `r51` explicitly so unrelated tests +cannot initialize a different backend first: + +```sh +go test -c ./pkg/turbine -o /tmp/turbine-streaming.test +go test -c ./pkg/replay -o /tmp/replay-streaming.test + +GOMAXPROCS=8 \ +MITHRIL_SIGVERIFY_FLOW_BACKEND=r51 \ +MITHRIL_SIGVERIFY_FLOW_FIXTURES=/srv/mithril-planner-pr257-trial-20260912/fixtures \ +/tmp/turbine-streaming.test \ + -test.run='^$' \ + -test.bench='^BenchmarkTransactionVerificationFlow$/^captured_2622581$' \ + -test.benchtime=3x -test.count=3 +``` + +Omit `MITHRIL_SIGVERIFY_FLOW_FIXTURES` for generated 228/1,232-byte workloads. +`MITHRIL_SIGVERIFY_FLOW_COUNT` changes generated transaction count or limits +captured fixtures to a prefix. Leave it unset for full captured blocks. A fresh +test executable is required when changing native backend. + +Fixture helper checks are `TestTransactionVerificationFlowFixtureShape` and +`TestTransactionVerificationFlowComponentBoundaries`. The former verifies actual +marshaled byte lengths, signature validity, and distinct signer identities. + +## Actual execution contention probe + +`run_execution_contention.py` runs the existing production +`LoadAndExecuteTransaction` benchmark for a signed system transfer +(`BenchmarkLoadAndExecuteTransferResultMode/lean`) before and during signature +verification. It runs two **separate processes**, both restricted to the same eight +physical cores with `GOMAXPROCS=8` each. The runner verifies that the selected CPU +IDs represent eight distinct physical cores; inspect the server's topology before +selecting IDs. + +This execution probe includes the transaction load/execute function. It excludes +account commit, dependency planning, full-block replay, and interaction through a +single shared Go scheduler/heap. It provides evidence of hardware/OS contention; +live whole-validator timings are still needed to choose the production default. + +```sh +python3 run_execution_contention.py \ + --turbine-bin /tmp/turbine-streaming.test \ + --replay-bin /tmp/replay-streaming.test \ + --fixtures /srv/mithril-planner-pr257-trial-20260912/fixtures \ + --slot 2622581 --cpus 0-7 \ + --out /tmp/mithril-execution-contention --samples 3 +``` + +The output directory must be new. Conditions (workers two/four, target four/eight, +catch-up/200 ms overlap) are deterministically shuffled within each repeated +sample. Every condition gets its own immediately preceding execution baseline. +The runner waits for the verifier's warmup/calibration to finish, releases its +explicit rendezvous, then measures concurrent execution. It refuses to use a +sample if verification load ends before the execution probe finishes. Increase +`--catchup-blocks` or `--tip-blocks` if that happens. It stops only its own benchmark +child on failure; it never interacts with the validator service. + +Raw output and commands are preserved in each sample directory. `comparison.json` +records CPU topology, run arguments, execution slowdown ratios, all signature +metrics and medians across repeats. Signature metrics describe the **whole load +window**, while the execution probe occupies a portion of that window; do not +interpret the load-window signature numbers as continuously contended results. +Background server activity remains a possible confounder and must be noted in +any conclusions. diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/README.md b/docs/results/sigverify-streaming/2026-09-12-zen5/README.md new file mode 100644 index 00000000..bf73826a --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/README.md @@ -0,0 +1,152 @@ +# Signature batching and shred-arrival overlap on Zen 5 + +Implementation: `05ded9b0` on `7layer/planner-pr257-trial-20260912`, building on +`8a94482e`. Narya is pinned to `c265ee9667131a556cd332c252b79de0860c7640`. + +The change forms signature groups before dispatch and keeps workers supplied +without wave barriers. It also decodes and verifies complete entry components +while later shreds arrive. The selected default is **two workers, target eight, +no batching timer**. Four available signatures run immediately; they do not wait +for eight. Four workers remain an explicit option for catch-up. + +## Environment and interpretation + +- Ryzen 7 9700X, eight physical cores / sixteen hardware threads; Go 1.26.4, + native Narya `r51`, `GOMAXPROCS=8`, affinity logical CPUs 0–7 (distinct cores). +- Benchmark processes ran at nice 10 while the existing Mithril validator and + Lightbringer continued running. These are shared-host measurements. +- Each assembly condition has three repetitions of three blocks. Tables show + medians of the reported per-repetition values; tail columns elsewhere are + medians of per-repetition p95s, not an aggregate percentile or confidence bound. +- Captured pool measurements include three public testnet blocks, each with + 33,760 one-signature V1 transactions, 228 wire bytes / 164 signed message bytes. + Assembly uses block 2622581's transactions in newly generated valid Merkle + shreds. The maximum-size case uses 33,760 distinct signed generated transactions + of exactly 1,232 wire bytes, including their signatures. +- Tip arrival schedules distribute complete component bursts across 200 ms. + Components use a 60 KiB transaction-byte budget, a benchmark choice rather than + a protocol requirement. Assembly generates real entry overhead and FEC padding. + This is not a recorded arrival trace or a whole-validator replay benchmark. + +## Full assembly: what remains after the final shred + +These cases run production assembly, entry decoding, final Merkle identity +checks, transaction signature verification, and block publication. Shred +construction, packet parsing, and shred-signature authentication are outside the +measurement. Transaction execution and commit are excluded. + +Both sides use the **new batching pool**. “Off” waits for all shreds before +transaction decoding/verification; “on” overlaps that work with arrivals. This +isolates overlap, rather than comparing against an older Narya or batching PR. + +| Wire bytes/tx | Workers | Full-to-ready off → on | Total arrival-to-ready off → on | CPU-ms/block off → on | +|---|---:|---:|---:|---:| +| 228 | 2 | 85.88 → **7.76 ms** | 287.68 → **208.06 ms** | 177.4 → 185.8 | +| 228 | 4 | 49.88 → **7.83 ms** | 250.71 → **207.82 ms** | 177.6 → 186.4 | +| 1,232 | 2 | 118.00 → **28.97 ms** | 318.80 → **229.37 ms** | 242.5 → 266.5 | +| 1,232 | 4 | 77.04 → **25.69 ms** | 276.59 → **227.10 ms** | 241.8 → 264.9 | + +Every row has 33,760 transactions and target eight. Actual shred collection stayed +around 200–202 ms, so the improvement did not come from delaying the synthetic +feed. With two workers, early verification covered all 33,760 captured +transactions and about 33,744 maximum-size transactions before full assembly. +Final signature joins were approximately 0.019 and 0.122 ms, respectively. +The remaining tail lies predominantly in final assembly work; it is not hidden +signature work. Additional overlap costs some CPU and retained memory. + +For all-at-once catch-up, two-worker total assembly was about 93 ms for captured +transactions and 140 ms for maximum-size transactions with overlap enabled. +Four workers yielded roughly 67 and 85 ms. Overlap itself provides little +opportunity when all data is immediately available, and some conditions showed +additional overhead. These results do not justify an automatic mode-switching +policy; the default prioritizes tip behavior. + +## Pool batching: worker and target trade-off + +Decoding is outside these measurements. CPU time sums work across cores, so it +must not be interpreted as single-thread wall time. + +| Transactions | Workers / target | Catch-up wall time | CPU-ms/block | +|---|---:|---:|---:| +| Captured 228 B | 2 / 4 | 155.92 ms | 317.8 | +| Captured 228 B | **2 / 8** | **77.79 ms** | **158.7** | +| Captured 228 B | 4 / 8 | 39.48 ms | 158.5 | +| Generated 1,232 B | 2 / 4 | 165.23 ms | 342.6 | +| Generated 1,232 B | **2 / 8** | **86.11 ms** | **180.0** | +| Generated 1,232 B | 4 / 8 | 56.22 ms | 223.0 | + +At the synthetic tip, the two-worker/eight-target pool's residual p95 was about +1.34 ms for captured transactions and 0.64 ms for maximum-size transactions. +These smaller values exclude the final assembly measured above. Scheduling lag +was around a millisecond, so small differences between worker counts are noisy. +Sparse four-transaction components dispatched immediately at width four even +with target eight. Setting target four globally roughly doubled signature CPU +work in the captured workload without a meaningful tip-latency advantage. + +## Effect on transaction execution + +The real `BenchmarkLoadAndExecuteTransferResultMode/lean` system-transfer probe +ran before and during signature load on the same eight physical cores. Each +condition has three shuffled paired samples. The two workloads use **separate +processes**, so this measures hardware/OS contention and excludes shared Go +scheduler/heap interactions, dependency planning, commit, and full replay. + +| Signature policy | Catch-up execution slowdown | Tip execution slowdown | +|---|---:|---:| +| 2 workers / target 4 | +1.9% | +7.0% | +| **2 workers / target 8** | **+21.8%** | **+1.7%** | +| 4 workers / target 4 | +1.2% | +21.4% | +| **4 workers / target 8** | **+21.5%** | **−0.9%** | + +Values are medians of paired slowdown ratios, not ratios of independently +aggregated times. Background load was variable: tip/target-eight paired ranges +were −5.1% to +3.4% for two workers and −10.1% to +17.9% for four. The small +negative median is noise, not evidence that signature work accelerates +execution. **This test does not show a consistent execution penalty from four +rather than two workers at target eight.** It does show that sustained catch-up +signature work can contend with execution at either worker count. + +Two workers are the conservative default because their tip latency is already +similar and their peak verifier concurrency is lower. Four workers are useful +when catch-up latency matters; the data do not establish that they are unsafe +or universally worse. A live same-process replay comparison would be needed to +measure scheduler, heap, planner and commit interactions. The running validator +was not switched for these tests. + +## Validation + +Native unit tests passed for Turbine, signature verification, transaction +verification, block metadata, statsd, and node configuration. Native repeated +race checks passed for the new streaming, cache, transaction-pool and receiver +paths. Local full Turbine race tests, selected package tests and vet passed. + +Regressions cover early verification before the last shred, gap/duplicate +handling, cross-FEC component boundaries, reset generations, bounded admission, +byte-budget/oversized fallback, shutdown ownership, canceled-completion retry, +exact-byte cache binding, retained invalid signatures, discarded UpdateParent +prefixes, and padded Agave decoding parity. Assembly benchmarks additionally +assert exactly one signature verification per retained transaction and matching +block identity/transaction coverage. No internal native fallback occurred. + +The local replay suite passed on rerun. Its first run hit an existing +`promotion_test.go` assertion requiring a tiny lookup duration to be positive; +the isolated test passed twenty repetitions and the full suite then passed. +No production replay or timing assertion was changed to address that flake. + +See the raw logs and JSON in this directory, the source benchmark comments, and +[BENCHMARKS.md](BENCHMARKS.md) for pool and execution-probe reproduction details. + +To reproduce the maximum-size assembly comparison on an AVX-512 IFMA machine: + +```sh +go test -c ./pkg/turbine -o /tmp/turbine-streaming.test +GOMAXPROCS=8 MITHRIL_SIGVERIFY_FLOW_BACKEND=r51 \ +MITHRIL_SIGVERIFY_FLOW_COUNT=33760 \ +taskset --cpu-list 0-7 /tmp/turbine-streaming.test \ + -test.run='^$' -test.benchtime=3x -test.count=3 \ + -test.bench='^BenchmarkEntryPrefetchAssembly$/^generated_1232B$/^workers_.*$/^target_8$/.*' +``` + +Use the captured fixture environment and `captured_2622581` sub-benchmark instead +for the short-transaction comparison. Select the physical CPU IDs appropriate +for the machine; the CPU IDs above describe this server's topology. diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/assembly-1232.log b/docs/results/sigverify-streaming/2026-09-12-zen5/assembly-1232.log new file mode 100644 index 00000000..986c1ee8 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/assembly-1232.log @@ -0,0 +1,29 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 3 132732036 ns/op 1.926 avg_cpu_cores 6.714 collection_p50-ms 6.898 collection_p95-ms 4.501 completion_parse_p50-ms 4.815 completion_parse_p95-ms 89.28 completion_sigverify_p50-ms 102.2 completion_sigverify_p95-ms 691.0 components/block 255.6 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 123.2 full_to_ready_p50-ms 133.2 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260174637 B/op 235471 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 3 134071642 ns/op 1.969 avg_cpu_cores 8.232 collection_p50-ms 8.625 collection_p95-ms 5.486 completion_parse_p50-ms 5.850 completion_parse_p95-ms 93.22 completion_sigverify_p50-ms 94.08 completion_sigverify_p95-ms 691.0 components/block 264.0 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 128.8 full_to_ready_p50-ms 132.1 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 259991864 B/op 235464 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 3 118432537 ns/op 1.989 avg_cpu_cores 5.898 collection_p50-ms 6.123 collection_p95-ms 4.634 completion_parse_p50-ms 5.372 completion_parse_p95-ms 82.63 completion_sigverify_p50-ms 82.80 completion_sigverify_p95-ms 691.0 components/block 235.6 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 111.0 full_to_ready_p50-ms 115.7 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260219832 B/op 235471 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 3 143317986 ns/op 1.993 avg_cpu_cores 10.38 collection_p50-ms 10.94 collection_p95-ms 5.302 completion_parse_p50-ms 5.596 completion_parse_p95-ms 91.17 completion_sigverify_p50-ms 96.29 completion_sigverify_p95-ms 691.0 components/block 285.7 cpu-ms/block 44160 data_shreds/block 2515 early_verified_tx/block 132.7 full_to_ready_p50-ms 136.1 full_to_ready_p95-ms 7.914 mean_width 0.007227 preparation_wait_p50-ms 0.008702 preparation_wait_p95-ms 33760 tx/block 265981290 B/op 236644 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 3 140105831 ns/op 2.039 avg_cpu_cores 9.935 collection_p50-ms 11.26 collection_p95-ms 5.330 completion_parse_p50-ms 6.534 completion_parse_p95-ms 93.04 completion_sigverify_p50-ms 94.68 completion_sigverify_p95-ms 691.0 components/block 285.6 cpu-ms/block 44160 data_shreds/block 2140 early_verified_tx/block 131.0 full_to_ready_p50-ms 131.5 full_to_ready_p95-ms 7.927 mean_width 0.005670 preparation_wait_p50-ms 0.006587 preparation_wait_p95-ms 33760 tx/block 265780592 B/op 236507 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 3 118146761 ns/op 2.052 avg_cpu_cores 8.247 collection_p50-ms 11.14 collection_p95-ms 5.074 completion_parse_p50-ms 5.303 completion_parse_p95-ms 79.37 completion_sigverify_p50-ms 79.81 completion_sigverify_p95-ms 691.0 components/block 242.4 cpu-ms/block 44160 data_shreds/block 2091 early_verified_tx/block 106.7 full_to_ready_p50-ms 116.1 full_to_ready_p95-ms 7.929 mean_width 0.003786 preparation_wait_p50-ms 0.007029 preparation_wait_p95-ms 33760 tx/block 265573218 B/op 236474 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 3 318801116 ns/op 0.7606 avg_cpu_cores 201.0 collection_p50-ms 201.6 collection_p95-ms 4.599 completion_parse_p50-ms 5.045 completion_parse_p95-ms 83.28 completion_sigverify_p50-ms 85.20 completion_sigverify_p95-ms 691.0 components/block 242.5 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 118.3 full_to_ready_p50-ms 122.6 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260036650 B/op 235461 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 3 314110925 ns/op 0.7601 avg_cpu_cores 201.6 collection_p50-ms 201.7 collection_p95-ms 4.269 completion_parse_p50-ms 4.420 completion_parse_p95-ms 82.73 completion_sigverify_p50-ms 82.83 completion_sigverify_p95-ms 691.0 components/block 238.8 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 113.3 full_to_ready_p50-ms 113.6 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260219818 B/op 235471 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 3 322511217 ns/op 0.7751 avg_cpu_cores 201.7 collection_p50-ms 202.5 collection_p95-ms 4.627 completion_parse_p50-ms 5.343 completion_parse_p95-ms 84.98 completion_sigverify_p50-ms 88.32 completion_sigverify_p95-ms 691.0 components/block 250.0 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 118.0 full_to_ready_p50-ms 126.6 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260082442 B/op 235464 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 3 226899169 ns/op 1.167 avg_cpu_cores 201.4 collection_p50-ms 201.4 collection_p95-ms 0 completion_parse_p50-ms 0.01079 completion_parse_p95-ms 0.1220 completion_sigverify_p50-ms 0.3120 completion_sigverify_p95-ms 691.0 components/block 264.7 cpu-ms/block 44160 data_shreds/block 33744 early_verified_tx/block 25.53 full_to_ready_p50-ms 26.34 full_to_ready_p95-ms 7.001 mean_width 0.1206 preparation_wait_p50-ms 0.1241 preparation_wait_p95-ms 33760 tx/block 307640389 B/op 250060 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 3 229370055 ns/op 1.162 avg_cpu_cores 200.7 collection_p50-ms 201.2 collection_p95-ms 0 completion_parse_p50-ms 0.01841 completion_parse_p95-ms 0.1002 completion_sigverify_p50-ms 0.4560 completion_sigverify_p95-ms 691.0 components/block 266.5 cpu-ms/block 44160 data_shreds/block 33728 early_verified_tx/block 29.75 full_to_ready_p50-ms 30.22 full_to_ready_p95-ms 7.001 mean_width 0.1252 preparation_wait_p50-ms 0.1260 preparation_wait_p95-ms 33760 tx/block 307397858 B/op 250045 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 3 231567222 ns/op 1.363 avg_cpu_cores 201.1 collection_p50-ms 201.2 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.1245 completion_sigverify_p50-ms 0.1492 completion_sigverify_p95-ms 691.0 components/block 315.7 cpu-ms/block 44160 data_shreds/block 33760 early_verified_tx/block 28.97 full_to_ready_p50-ms 34.61 full_to_ready_p95-ms 7.001 mean_width 0.1317 preparation_wait_p50-ms 0.1370 preparation_wait_p95-ms 33760 tx/block 307432845 B/op 250046 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_off-8 3 98393903 ns/op 2.983 avg_cpu_cores 8.190 collection_p50-ms 8.563 collection_p95-ms 4.938 completion_parse_p50-ms 5.485 completion_parse_p95-ms 55.91 completion_sigverify_p50-ms 57.00 completion_sigverify_p95-ms 691.0 components/block 293.5 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 88.12 full_to_ready_p50-ms 95.35 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260266082 B/op 235474 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_off-8 3 105755933 ns/op 2.776 avg_cpu_cores 8.602 collection_p50-ms 9.822 collection_p95-ms 6.422 completion_parse_p50-ms 7.668 completion_parse_p95-ms 52.00 completion_sigverify_p50-ms 60.88 completion_sigverify_p95-ms 691.0 components/block 293.6 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 90.69 full_to_ready_p50-ms 112.2 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260174000 B/op 235467 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_off-8 3 81434919 ns/op 3.009 avg_cpu_cores 6.353 collection_p50-ms 6.417 collection_p95-ms 4.568 completion_parse_p50-ms 4.630 completion_parse_p95-ms 44.00 completion_sigverify_p50-ms 44.05 completion_sigverify_p95-ms 691.0 components/block 245.1 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 75.94 full_to_ready_p50-ms 77.03 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260402965 B/op 235479 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_on-8 3 81921701 ns/op 3.107 avg_cpu_cores 9.142 collection_p50-ms 9.153 collection_p95-ms 4.251 completion_parse_p50-ms 4.784 completion_parse_p95-ms 41.90 completion_sigverify_p50-ms 42.10 completion_sigverify_p95-ms 691.0 components/block 254.5 cpu-ms/block 44160 data_shreds/block 3430 early_verified_tx/block 75.23 full_to_ready_p50-ms 75.61 full_to_ready_p95-ms 7.885 mean_width 0.007166 preparation_wait_p50-ms 0.01060 preparation_wait_p95-ms 33760 tx/block 267789989 B/op 237063 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_on-8 3 106211708 ns/op 2.960 avg_cpu_cores 12.72 collection_p50-ms 12.77 collection_p95-ms 6.076 completion_parse_p50-ms 6.677 completion_parse_p95-ms 49.86 completion_sigverify_p50-ms 57.70 completion_sigverify_p95-ms 691.0 components/block 314.4 cpu-ms/block 44160 data_shreds/block 3397 early_verified_tx/block 91.87 full_to_ready_p50-ms 99.87 full_to_ready_p95-ms 7.886 mean_width 0.01751 preparation_wait_p50-ms 0.01784 preparation_wait_p95-ms 33760 tx/block 267653104 B/op 237049 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_on-8 3 85283927 ns/op 2.979 avg_cpu_cores 9.315 collection_p50-ms 10.89 collection_p95-ms 3.727 completion_parse_p50-ms 5.583 completion_parse_p95-ms 41.31 completion_sigverify_p50-ms 43.80 completion_sigverify_p95-ms 691.0 components/block 254.1 cpu-ms/block 44160 data_shreds/block 3561 early_verified_tx/block 75.15 full_to_ready_p50-ms 86.25 full_to_ready_p95-ms 7.880 mean_width 0.01225 preparation_wait_p50-ms 0.01287 preparation_wait_p95-ms 33760 tx/block 267515682 B/op 237096 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_off-8 3 276365085 ns/op 0.8723 avg_cpu_cores 201.5 collection_p50-ms 201.6 collection_p95-ms 4.437 completion_parse_p50-ms 4.504 completion_parse_p95-ms 43.22 completion_sigverify_p50-ms 43.60 completion_sigverify_p95-ms 691.0 components/block 241.1 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 77.04 full_to_ready_p50-ms 77.05 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260403005 B/op 235479 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_off-8 3 280110502 ns/op 0.9259 avg_cpu_cores 200.8 collection_p50-ms 201.1 collection_p95-ms 4.666 completion_parse_p50-ms 4.728 completion_parse_p95-ms 44.11 completion_sigverify_p50-ms 56.80 completion_sigverify_p95-ms 691.0 components/block 259.4 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 77.81 full_to_ready_p50-ms 86.07 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260495133 B/op 235486 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_off-8 3 276593977 ns/op 0.8742 avg_cpu_cores 201.6 collection_p50-ms 201.7 collection_p95-ms 4.198 completion_parse_p50-ms 6.163 completion_parse_p95-ms 43.50 completion_sigverify_p50-ms 44.33 completion_sigverify_p95-ms 691.0 components/block 241.8 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 75.26 full_to_ready_p50-ms 79.84 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 259991381 B/op 235460 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_on-8 3 227100110 ns/op 1.166 avg_cpu_cores 201.2 collection_p50-ms 201.4 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.1103 completion_sigverify_p50-ms 0.1149 completion_sigverify_p95-ms 691.0 components/block 264.9 cpu-ms/block 44160 data_shreds/block 33760 early_verified_tx/block 25.08 full_to_ready_p50-ms 27.44 full_to_ready_p95-ms 7.001 mean_width 0.1228 preparation_wait_p50-ms 0.1240 preparation_wait_p95-ms 33760 tx/block 307747917 B/op 250014 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_on-8 3 241182176 ns/op 1.235 avg_cpu_cores 201.4 collection_p50-ms 202.9 collection_p95-ms 0.01357 completion_parse_p50-ms 0.04438 completion_parse_p95-ms 0.3222 completion_sigverify_p50-ms 0.4977 completion_sigverify_p95-ms 691.0 components/block 297.8 cpu-ms/block 44160 data_shreds/block 33712 early_verified_tx/block 34.46 full_to_ready_p50-ms 51.53 full_to_ready_p95-ms 7.001 mean_width 0.1261 preparation_wait_p50-ms 0.1812 preparation_wait_p95-ms 33760 tx/block 307727008 B/op 250008 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_on-8 3 226480518 ns/op 1.167 avg_cpu_cores 200.9 collection_p50-ms 201.3 collection_p95-ms 0.01037 completion_parse_p50-ms 0.01195 completion_parse_p95-ms 0.2797 completion_sigverify_p50-ms 0.3548 completion_sigverify_p95-ms 691.0 components/block 264.2 cpu-ms/block 44160 data_shreds/block 33728 early_verified_tx/block 25.69 full_to_ready_p50-ms 25.87 full_to_ready_p95-ms 7.001 mean_width 0.1226 preparation_wait_p50-ms 0.1242 preparation_wait_p95-ms 33760 tx/block 307612829 B/op 250008 allocs/op +PASS diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/assembly-captured.log b/docs/results/sigverify-streaming/2026-09-12-zen5/assembly-captured.log new file mode 100644 index 00000000..3018e8e5 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/assembly-captured.log @@ -0,0 +1,29 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_off-8 3 105356378 ns/op 1.971 avg_cpu_cores 1.468 collection_p50-ms 1.850 collection_p95-ms 8.134 completion_parse_p50-ms 8.419 completion_parse_p95-ms 91.75 completion_sigverify_p50-ms 93.70 completion_sigverify_p95-ms 128.0 components/block 207.7 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 105.0 full_to_ready_p50-ms 106.8 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 60657304 B/op 276266 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_off-8 3 86032987 ns/op 2.002 avg_cpu_cores 1.089 collection_p50-ms 1.203 collection_p95-ms 6.689 completion_parse_p50-ms 6.878 completion_parse_p95-ms 74.21 completion_sigverify_p50-ms 74.64 completion_sigverify_p95-ms 128.0 components/block 172.3 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 85.25 full_to_ready_p50-ms 86.05 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 60840701 B/op 276276 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_off-8 3 93330914 ns/op 1.983 avg_cpu_cores 1.146 collection_p50-ms 1.289 collection_p95-ms 7.575 completion_parse_p50-ms 7.927 completion_parse_p95-ms 78.84 completion_sigverify_p50-ms 82.89 completion_sigverify_p95-ms 128.0 components/block 185.0 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 91.61 full_to_ready_p50-ms 96.68 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 60612906 B/op 276264 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_on-8 3 98687304 ns/op 2.002 avg_cpu_cores 1.923 collection_p50-ms 2.397 collection_p95-ms 8.068 completion_parse_p50-ms 8.712 completion_parse_p95-ms 84.04 completion_sigverify_p50-ms 84.50 completion_sigverify_p95-ms 128.0 components/block 197.6 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 98.00 full_to_ready_p50-ms 99.33 full_to_ready_p95-ms 7.997 mean_width 0.0009020 preparation_wait_p50-ms 2.642 preparation_wait_p95-ms 33760 tx/block 63544576 B/op 276416 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_on-8 3 86908892 ns/op 2.033 avg_cpu_cores 1.582 collection_p50-ms 1.667 collection_p95-ms 6.919 completion_parse_p50-ms 7.541 completion_parse_p95-ms 71.67 completion_sigverify_p50-ms 73.28 completion_sigverify_p95-ms 128.0 components/block 176.7 cpu-ms/block 8128 data_shreds/block 89.67 early_verified_tx/block 85.58 full_to_ready_p50-ms 86.16 full_to_ready_p95-ms 7.996 mean_width 0.0007210 preparation_wait_p50-ms 0.4474 preparation_wait_p95-ms 33760 tx/block 63411160 B/op 276433 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_on-8 3 93317165 ns/op 1.995 avg_cpu_cores 1.590 collection_p50-ms 1.976 collection_p95-ms 6.820 completion_parse_p50-ms 7.778 completion_parse_p95-ms 76.41 completion_sigverify_p50-ms 85.36 completion_sigverify_p95-ms 128.0 components/block 186.2 cpu-ms/block 8128 data_shreds/block 89.67 early_verified_tx/block 90.85 full_to_ready_p50-ms 97.05 full_to_ready_p95-ms 7.998 mean_width 0.0005910 preparation_wait_p50-ms 0.0006410 preparation_wait_p95-ms 33760 tx/block 63403101 B/op 276380 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_off-8 3 290460961 ns/op 0.6306 avg_cpu_cores 200.6 collection_p50-ms 200.7 collection_p95-ms 6.281 completion_parse_p50-ms 7.566 completion_parse_p95-ms 73.61 completion_sigverify_p50-ms 88.73 completion_sigverify_p95-ms 128.0 components/block 183.2 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 84.53 full_to_ready_p50-ms 101.9 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 60656794 B/op 276264 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_off-8 3 287675866 ns/op 0.6167 avg_cpu_cores 200.6 collection_p50-ms 200.6 collection_p95-ms 6.699 completion_parse_p50-ms 6.802 completion_parse_p95-ms 75.07 completion_sigverify_p50-ms 77.73 completion_sigverify_p95-ms 128.0 components/block 177.4 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 86.16 full_to_ready_p50-ms 89.34 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 60519650 B/op 276258 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_off-8 3 287110470 ns/op 0.6140 avg_cpu_cores 200.6 collection_p50-ms 201.2 collection_p95-ms 6.565 completion_parse_p50-ms 6.620 completion_parse_p95-ms 74.50 completion_sigverify_p50-ms 77.80 completion_sigverify_p95-ms 128.0 components/block 176.3 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 85.88 full_to_ready_p50-ms 87.16 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 60656832 B/op 276264 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_on-8 3 209450474 ns/op 0.9421 avg_cpu_cores 200.7 collection_p50-ms 201.2 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01935 completion_sigverify_p50-ms 0.03406 completion_sigverify_p95-ms 128.0 components/block 197.3 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 8.638 full_to_ready_p50-ms 9.007 full_to_ready_p95-ms 7.912 mean_width 0.02519 preparation_wait_p50-ms 0.03274 preparation_wait_p95-ms 33760 tx/block 69730546 B/op 279208 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_on-8 3 208058833 ns/op 0.8929 avg_cpu_cores 200.7 collection_p50-ms 200.7 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01704 completion_sigverify_p50-ms 0.02061 completion_sigverify_p95-ms 128.0 components/block 185.8 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 7.636 full_to_ready_p50-ms 9.799 full_to_ready_p95-ms 7.912 mean_width 0.02234 preparation_wait_p50-ms 0.02347 preparation_wait_p95-ms 33760 tx/block 69690704 B/op 279231 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_on-8 3 207559046 ns/op 0.8943 avg_cpu_cores 200.6 collection_p50-ms 200.9 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01885 completion_sigverify_p50-ms 0.01951 completion_sigverify_p95-ms 128.0 components/block 185.6 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 7.755 full_to_ready_p50-ms 8.173 full_to_ready_p95-ms 7.912 mean_width 0.02086 preparation_wait_p50-ms 0.02149 preparation_wait_p95-ms 33760 tx/block 69823114 B/op 279217 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_off-8 3 55875291 ns/op 3.314 avg_cpu_cores 1.291 collection_p50-ms 1.709 collection_p95-ms 7.281 completion_parse_p50-ms 7.313 completion_parse_p95-ms 40.25 completion_sigverify_p50-ms 42.81 completion_sigverify_p95-ms 128.0 components/block 185.2 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 55.07 full_to_ready_p50-ms 55.62 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 60611714 B/op 276263 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_off-8 3 61860407 ns/op 3.216 avg_cpu_cores 1.169 collection_p50-ms 2.221 collection_p95-ms 7.116 completion_parse_p50-ms 10.84 completion_parse_p95-ms 47.89 completion_sigverify_p50-ms 48.65 completion_sigverify_p95-ms 128.0 components/block 198.9 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 62.07 full_to_ready_p50-ms 67.59 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 60473978 B/op 276254 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_off-8 3 55571155 ns/op 3.375 avg_cpu_cores 1.177 collection_p50-ms 1.290 collection_p95-ms 7.277 completion_parse_p50-ms 7.356 completion_parse_p95-ms 42.47 completion_sigverify_p50-ms 44.27 completion_sigverify_p95-ms 128.0 components/block 187.6 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 54.23 full_to_ready_p50-ms 54.69 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 60748730 B/op 276268 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_on-8 3 66703191 ns/op 3.379 avg_cpu_cores 2.562 collection_p50-ms 2.726 collection_p95-ms 8.584 completion_parse_p50-ms 10.03 completion_parse_p95-ms 50.79 completion_sigverify_p50-ms 52.88 completion_sigverify_p95-ms 128.0 components/block 225.4 cpu-ms/block 8128 data_shreds/block 448.3 early_verified_tx/block 63.43 full_to_ready_p50-ms 70.57 full_to_ready_p95-ms 7.995 mean_width 0.001262 preparation_wait_p50-ms 0.001913 preparation_wait_p95-ms 33760 tx/block 63510485 B/op 276470 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_on-8 3 67443208 ns/op 3.253 avg_cpu_cores 2.138 collection_p50-ms 2.190 collection_p95-ms 9.260 completion_parse_p50-ms 9.645 completion_parse_p95-ms 48.49 completion_sigverify_p50-ms 51.30 completion_sigverify_p95-ms 128.0 components/block 219.4 cpu-ms/block 8128 data_shreds/block 717.3 early_verified_tx/block 66.76 full_to_ready_p50-ms 66.92 full_to_ready_p95-ms 7.996 mean_width 0.0007000 preparation_wait_p50-ms 0.001174 preparation_wait_p95-ms 33760 tx/block 63344117 B/op 276427 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_on-8 3 52447666 ns/op 3.460 avg_cpu_cores 1.682 collection_p50-ms 1.766 collection_p95-ms 6.681 completion_parse_p50-ms 7.321 completion_parse_p95-ms 37.96 completion_sigverify_p50-ms 38.18 completion_sigverify_p95-ms 128.0 components/block 181.5 cpu-ms/block 8128 data_shreds/block 358.7 early_verified_tx/block 51.77 full_to_ready_p50-ms 52.04 full_to_ready_p95-ms 7.996 mean_width 0.0009730 preparation_wait_p50-ms 0.001014 preparation_wait_p95-ms 33760 tx/block 63509837 B/op 276467 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_off-8 3 251131391 ns/op 0.7197 avg_cpu_cores 200.6 collection_p50-ms 200.8 collection_p95-ms 6.767 completion_parse_p50-ms 7.056 completion_parse_p95-ms 39.38 completion_sigverify_p50-ms 39.88 completion_sigverify_p95-ms 128.0 components/block 180.7 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 50.30 full_to_ready_p50-ms 51.33 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 60474034 B/op 276254 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_off-8 3 250708659 ns/op 0.7074 avg_cpu_cores 200.7 collection_p50-ms 200.8 collection_p95-ms 6.285 completion_parse_p50-ms 6.691 completion_parse_p95-ms 39.31 completion_sigverify_p50-ms 40.26 completion_sigverify_p95-ms 128.0 components/block 177.3 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 49.88 full_to_ready_p50-ms 51.60 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 60657186 B/op 276264 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_off-8 3 250569171 ns/op 0.7089 avg_cpu_cores 200.7 collection_p50-ms 201.2 collection_p95-ms 6.188 completion_parse_p50-ms 6.469 completion_parse_p95-ms 39.20 completion_sigverify_p50-ms 40.32 completion_sigverify_p95-ms 128.0 components/block 177.6 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 49.62 full_to_ready_p50-ms 50.23 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 60748789 B/op 276269 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_on-8 3 208138109 ns/op 0.8932 avg_cpu_cores 201.2 collection_p50-ms 201.2 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01720 completion_sigverify_p50-ms 0.02235 completion_sigverify_p95-ms 128.0 components/block 185.9 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 7.853 full_to_ready_p50-ms 8.999 full_to_ready_p95-ms 7.912 mean_width 0.02114 preparation_wait_p50-ms 0.02434 preparation_wait_p95-ms 33760 tx/block 69690304 B/op 279213 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_on-8 3 207816740 ns/op 0.9928 avg_cpu_cores 200.8 collection_p50-ms 201.1 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.02023 completion_sigverify_p50-ms 0.02548 completion_sigverify_p95-ms 128.0 components/block 206.3 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 7.829 full_to_ready_p50-ms 8.333 full_to_ready_p95-ms 7.912 mean_width 0.01985 preparation_wait_p50-ms 0.02489 preparation_wait_p95-ms 33760 tx/block 69734096 B/op 279203 allocs/op +BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_on-8 3 207211686 ns/op 0.8998 avg_cpu_cores 200.3 collection_p50-ms 200.9 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01778 completion_sigverify_p50-ms 0.02121 completion_sigverify_p95-ms 128.0 components/block 186.4 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 7.442 full_to_ready_p50-ms 8.205 full_to_ready_p95-ms 7.912 mean_width 0.02017 preparation_wait_p50-ms 0.02153 preparation_wait_p95-ms 33760 tx/block 69779202 B/op 279201 allocs/op +PASS diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/assembly-comparison.json b/docs/results/sigverify-streaming/2026-09-12-zen5/assembly-comparison.json new file mode 100644 index 00000000..1e7694d3 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/assembly-comparison.json @@ -0,0 +1,1602 @@ +[ + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_off-8", + "samples": 3, + "median": { + "ns/op": 93330914.0, + "avg_cpu_cores": 1.983, + "collection_p50-ms": 1.146, + "collection_p95-ms": 1.289, + "completion_parse_p50-ms": 7.575, + "completion_parse_p95-ms": 7.927, + "completion_sigverify_p50-ms": 78.84, + "completion_sigverify_p95-ms": 82.89, + "components/block": 128.0, + "cpu-ms/block": 185.0, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 91.61, + "full_to_ready_p95-ms": 96.68, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60657304.0, + "allocs/op": 276266.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_off-8", + "iterations": 3, + "ns/op": 105356378.0, + "avg_cpu_cores": 1.971, + "collection_p50-ms": 1.468, + "collection_p95-ms": 1.85, + "completion_parse_p50-ms": 8.134, + "completion_parse_p95-ms": 8.419, + "completion_sigverify_p50-ms": 91.75, + "completion_sigverify_p95-ms": 93.7, + "components/block": 128.0, + "cpu-ms/block": 207.7, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 105.0, + "full_to_ready_p95-ms": 106.8, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60657304.0, + "allocs/op": 276266.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_off-8", + "iterations": 3, + "ns/op": 86032987.0, + "avg_cpu_cores": 2.002, + "collection_p50-ms": 1.089, + "collection_p95-ms": 1.203, + "completion_parse_p50-ms": 6.689, + "completion_parse_p95-ms": 6.878, + "completion_sigverify_p50-ms": 74.21, + "completion_sigverify_p95-ms": 74.64, + "components/block": 128.0, + "cpu-ms/block": 172.3, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 85.25, + "full_to_ready_p95-ms": 86.05, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60840701.0, + "allocs/op": 276276.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_off-8", + "iterations": 3, + "ns/op": 93330914.0, + "avg_cpu_cores": 1.983, + "collection_p50-ms": 1.146, + "collection_p95-ms": 1.289, + "completion_parse_p50-ms": 7.575, + "completion_parse_p95-ms": 7.927, + "completion_sigverify_p50-ms": 78.84, + "completion_sigverify_p95-ms": 82.89, + "components/block": 128.0, + "cpu-ms/block": 185.0, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 91.61, + "full_to_ready_p95-ms": 96.68, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60612906.0, + "allocs/op": 276264.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_on-8", + "samples": 3, + "median": { + "ns/op": 93317165.0, + "avg_cpu_cores": 2.002, + "collection_p50-ms": 1.59, + "collection_p95-ms": 1.976, + "completion_parse_p50-ms": 6.919, + "completion_parse_p95-ms": 7.778, + "completion_sigverify_p50-ms": 76.41, + "completion_sigverify_p95-ms": 84.5, + "components/block": 128.0, + "cpu-ms/block": 186.2, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 89.67, + "full_to_ready_p50-ms": 90.85, + "full_to_ready_p95-ms": 97.05, + "mean_width": 7.997, + "preparation_wait_p50-ms": 0.000721, + "preparation_wait_p95-ms": 0.4474, + "tx/block": 33760.0, + "B/op": 63411160.0, + "allocs/op": 276416.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_on-8", + "iterations": 3, + "ns/op": 98687304.0, + "avg_cpu_cores": 2.002, + "collection_p50-ms": 1.923, + "collection_p95-ms": 2.397, + "completion_parse_p50-ms": 8.068, + "completion_parse_p95-ms": 8.712, + "completion_sigverify_p50-ms": 84.04, + "completion_sigverify_p95-ms": 84.5, + "components/block": 128.0, + "cpu-ms/block": 197.6, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 98.0, + "full_to_ready_p95-ms": 99.33, + "mean_width": 7.997, + "preparation_wait_p50-ms": 0.000902, + "preparation_wait_p95-ms": 2.642, + "tx/block": 33760.0, + "B/op": 63544576.0, + "allocs/op": 276416.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_on-8", + "iterations": 3, + "ns/op": 86908892.0, + "avg_cpu_cores": 2.033, + "collection_p50-ms": 1.582, + "collection_p95-ms": 1.667, + "completion_parse_p50-ms": 6.919, + "completion_parse_p95-ms": 7.541, + "completion_sigverify_p50-ms": 71.67, + "completion_sigverify_p95-ms": 73.28, + "components/block": 128.0, + "cpu-ms/block": 176.7, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 89.67, + "full_to_ready_p50-ms": 85.58, + "full_to_ready_p95-ms": 86.16, + "mean_width": 7.996, + "preparation_wait_p50-ms": 0.000721, + "preparation_wait_p95-ms": 0.4474, + "tx/block": 33760.0, + "B/op": 63411160.0, + "allocs/op": 276433.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/catchup/overlap_on-8", + "iterations": 3, + "ns/op": 93317165.0, + "avg_cpu_cores": 1.995, + "collection_p50-ms": 1.59, + "collection_p95-ms": 1.976, + "completion_parse_p50-ms": 6.82, + "completion_parse_p95-ms": 7.778, + "completion_sigverify_p50-ms": 76.41, + "completion_sigverify_p95-ms": 85.36, + "components/block": 128.0, + "cpu-ms/block": 186.2, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 89.67, + "full_to_ready_p50-ms": 90.85, + "full_to_ready_p95-ms": 97.05, + "mean_width": 7.998, + "preparation_wait_p50-ms": 0.000591, + "preparation_wait_p95-ms": 0.000641, + "tx/block": 33760.0, + "B/op": 63403101.0, + "allocs/op": 276380.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_off-8", + "samples": 3, + "median": { + "ns/op": 287675866.0, + "avg_cpu_cores": 0.6167, + "collection_p50-ms": 200.6, + "collection_p95-ms": 200.7, + "completion_parse_p50-ms": 6.565, + "completion_parse_p95-ms": 6.802, + "completion_sigverify_p50-ms": 74.5, + "completion_sigverify_p95-ms": 77.8, + "components/block": 128.0, + "cpu-ms/block": 177.4, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 85.88, + "full_to_ready_p95-ms": 89.34, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60656794.0, + "allocs/op": 276264.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 3, + "ns/op": 290460961.0, + "avg_cpu_cores": 0.6306, + "collection_p50-ms": 200.6, + "collection_p95-ms": 200.7, + "completion_parse_p50-ms": 6.281, + "completion_parse_p95-ms": 7.566, + "completion_sigverify_p50-ms": 73.61, + "completion_sigverify_p95-ms": 88.73, + "components/block": 128.0, + "cpu-ms/block": 183.2, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 84.53, + "full_to_ready_p95-ms": 101.9, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60656794.0, + "allocs/op": 276264.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 3, + "ns/op": 287675866.0, + "avg_cpu_cores": 0.6167, + "collection_p50-ms": 200.6, + "collection_p95-ms": 200.6, + "completion_parse_p50-ms": 6.699, + "completion_parse_p95-ms": 6.802, + "completion_sigverify_p50-ms": 75.07, + "completion_sigverify_p95-ms": 77.73, + "components/block": 128.0, + "cpu-ms/block": 177.4, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 86.16, + "full_to_ready_p95-ms": 89.34, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60519650.0, + "allocs/op": 276258.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 3, + "ns/op": 287110470.0, + "avg_cpu_cores": 0.614, + "collection_p50-ms": 200.6, + "collection_p95-ms": 201.2, + "completion_parse_p50-ms": 6.565, + "completion_parse_p95-ms": 6.62, + "completion_sigverify_p50-ms": 74.5, + "completion_sigverify_p95-ms": 77.8, + "components/block": 128.0, + "cpu-ms/block": 176.3, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 85.88, + "full_to_ready_p95-ms": 87.16, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60656832.0, + "allocs/op": 276264.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_on-8", + "samples": 3, + "median": { + "ns/op": 208058833.0, + "avg_cpu_cores": 0.8943, + "collection_p50-ms": 200.7, + "collection_p95-ms": 200.9, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01885, + "completion_sigverify_p95-ms": 0.02061, + "components/block": 128.0, + "cpu-ms/block": 185.8, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 7.755, + "full_to_ready_p95-ms": 9.007, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02234, + "preparation_wait_p95-ms": 0.02347, + "tx/block": 33760.0, + "B/op": 69730546.0, + "allocs/op": 279217.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 3, + "ns/op": 209450474.0, + "avg_cpu_cores": 0.9421, + "collection_p50-ms": 200.7, + "collection_p95-ms": 201.2, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01935, + "completion_sigverify_p95-ms": 0.03406, + "components/block": 128.0, + "cpu-ms/block": 197.3, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 8.638, + "full_to_ready_p95-ms": 9.007, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02519, + "preparation_wait_p95-ms": 0.03274, + "tx/block": 33760.0, + "B/op": 69730546.0, + "allocs/op": 279208.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 3, + "ns/op": 208058833.0, + "avg_cpu_cores": 0.8929, + "collection_p50-ms": 200.7, + "collection_p95-ms": 200.7, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01704, + "completion_sigverify_p95-ms": 0.02061, + "components/block": 128.0, + "cpu-ms/block": 185.8, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 7.636, + "full_to_ready_p95-ms": 9.799, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02234, + "preparation_wait_p95-ms": 0.02347, + "tx/block": 33760.0, + "B/op": 69690704.0, + "allocs/op": 279231.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 3, + "ns/op": 207559046.0, + "avg_cpu_cores": 0.8943, + "collection_p50-ms": 200.6, + "collection_p95-ms": 200.9, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01885, + "completion_sigverify_p95-ms": 0.01951, + "components/block": 128.0, + "cpu-ms/block": 185.6, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 7.755, + "full_to_ready_p95-ms": 8.173, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02086, + "preparation_wait_p95-ms": 0.02149, + "tx/block": 33760.0, + "B/op": 69823114.0, + "allocs/op": 279217.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_off-8", + "samples": 3, + "median": { + "ns/op": 55875291.0, + "avg_cpu_cores": 3.314, + "collection_p50-ms": 1.177, + "collection_p95-ms": 1.709, + "completion_parse_p50-ms": 7.277, + "completion_parse_p95-ms": 7.356, + "completion_sigverify_p50-ms": 42.47, + "completion_sigverify_p95-ms": 44.27, + "components/block": 128.0, + "cpu-ms/block": 187.6, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 55.07, + "full_to_ready_p95-ms": 55.62, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60611714.0, + "allocs/op": 276263.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_off-8", + "iterations": 3, + "ns/op": 55875291.0, + "avg_cpu_cores": 3.314, + "collection_p50-ms": 1.291, + "collection_p95-ms": 1.709, + "completion_parse_p50-ms": 7.281, + "completion_parse_p95-ms": 7.313, + "completion_sigverify_p50-ms": 40.25, + "completion_sigverify_p95-ms": 42.81, + "components/block": 128.0, + "cpu-ms/block": 185.2, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 55.07, + "full_to_ready_p95-ms": 55.62, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60611714.0, + "allocs/op": 276263.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_off-8", + "iterations": 3, + "ns/op": 61860407.0, + "avg_cpu_cores": 3.216, + "collection_p50-ms": 1.169, + "collection_p95-ms": 2.221, + "completion_parse_p50-ms": 7.116, + "completion_parse_p95-ms": 10.84, + "completion_sigverify_p50-ms": 47.89, + "completion_sigverify_p95-ms": 48.65, + "components/block": 128.0, + "cpu-ms/block": 198.9, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 62.07, + "full_to_ready_p95-ms": 67.59, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60473978.0, + "allocs/op": 276254.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_off-8", + "iterations": 3, + "ns/op": 55571155.0, + "avg_cpu_cores": 3.375, + "collection_p50-ms": 1.177, + "collection_p95-ms": 1.29, + "completion_parse_p50-ms": 7.277, + "completion_parse_p95-ms": 7.356, + "completion_sigverify_p50-ms": 42.47, + "completion_sigverify_p95-ms": 44.27, + "components/block": 128.0, + "cpu-ms/block": 187.6, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 54.23, + "full_to_ready_p95-ms": 54.69, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60748730.0, + "allocs/op": 276268.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_on-8", + "samples": 3, + "median": { + "ns/op": 66703191.0, + "avg_cpu_cores": 3.379, + "collection_p50-ms": 2.138, + "collection_p95-ms": 2.19, + "completion_parse_p50-ms": 8.584, + "completion_parse_p95-ms": 9.645, + "completion_sigverify_p50-ms": 48.49, + "completion_sigverify_p95-ms": 51.3, + "components/block": 128.0, + "cpu-ms/block": 219.4, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 448.3, + "full_to_ready_p50-ms": 63.43, + "full_to_ready_p95-ms": 66.92, + "mean_width": 7.996, + "preparation_wait_p50-ms": 0.000973, + "preparation_wait_p95-ms": 0.001174, + "tx/block": 33760.0, + "B/op": 63509837.0, + "allocs/op": 276467.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_on-8", + "iterations": 3, + "ns/op": 66703191.0, + "avg_cpu_cores": 3.379, + "collection_p50-ms": 2.562, + "collection_p95-ms": 2.726, + "completion_parse_p50-ms": 8.584, + "completion_parse_p95-ms": 10.03, + "completion_sigverify_p50-ms": 50.79, + "completion_sigverify_p95-ms": 52.88, + "components/block": 128.0, + "cpu-ms/block": 225.4, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 448.3, + "full_to_ready_p50-ms": 63.43, + "full_to_ready_p95-ms": 70.57, + "mean_width": 7.995, + "preparation_wait_p50-ms": 0.001262, + "preparation_wait_p95-ms": 0.001913, + "tx/block": 33760.0, + "B/op": 63510485.0, + "allocs/op": 276470.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_on-8", + "iterations": 3, + "ns/op": 67443208.0, + "avg_cpu_cores": 3.253, + "collection_p50-ms": 2.138, + "collection_p95-ms": 2.19, + "completion_parse_p50-ms": 9.26, + "completion_parse_p95-ms": 9.645, + "completion_sigverify_p50-ms": 48.49, + "completion_sigverify_p95-ms": 51.3, + "components/block": 128.0, + "cpu-ms/block": 219.4, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 717.3, + "full_to_ready_p50-ms": 66.76, + "full_to_ready_p95-ms": 66.92, + "mean_width": 7.996, + "preparation_wait_p50-ms": 0.0007, + "preparation_wait_p95-ms": 0.001174, + "tx/block": 33760.0, + "B/op": 63344117.0, + "allocs/op": 276427.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/catchup/overlap_on-8", + "iterations": 3, + "ns/op": 52447666.0, + "avg_cpu_cores": 3.46, + "collection_p50-ms": 1.682, + "collection_p95-ms": 1.766, + "completion_parse_p50-ms": 6.681, + "completion_parse_p95-ms": 7.321, + "completion_sigverify_p50-ms": 37.96, + "completion_sigverify_p95-ms": 38.18, + "components/block": 128.0, + "cpu-ms/block": 181.5, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 358.7, + "full_to_ready_p50-ms": 51.77, + "full_to_ready_p95-ms": 52.04, + "mean_width": 7.996, + "preparation_wait_p50-ms": 0.000973, + "preparation_wait_p95-ms": 0.001014, + "tx/block": 33760.0, + "B/op": 63509837.0, + "allocs/op": 276467.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_off-8", + "samples": 3, + "median": { + "ns/op": 250708659.0, + "avg_cpu_cores": 0.7089, + "collection_p50-ms": 200.7, + "collection_p95-ms": 200.8, + "completion_parse_p50-ms": 6.285, + "completion_parse_p95-ms": 6.691, + "completion_sigverify_p50-ms": 39.31, + "completion_sigverify_p95-ms": 40.26, + "components/block": 128.0, + "cpu-ms/block": 177.6, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 49.88, + "full_to_ready_p95-ms": 51.33, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60657186.0, + "allocs/op": 276264.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_off-8", + "iterations": 3, + "ns/op": 251131391.0, + "avg_cpu_cores": 0.7197, + "collection_p50-ms": 200.6, + "collection_p95-ms": 200.8, + "completion_parse_p50-ms": 6.767, + "completion_parse_p95-ms": 7.056, + "completion_sigverify_p50-ms": 39.38, + "completion_sigverify_p95-ms": 39.88, + "components/block": 128.0, + "cpu-ms/block": 180.7, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 50.3, + "full_to_ready_p95-ms": 51.33, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60474034.0, + "allocs/op": 276254.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_off-8", + "iterations": 3, + "ns/op": 250708659.0, + "avg_cpu_cores": 0.7074, + "collection_p50-ms": 200.7, + "collection_p95-ms": 200.8, + "completion_parse_p50-ms": 6.285, + "completion_parse_p95-ms": 6.691, + "completion_sigverify_p50-ms": 39.31, + "completion_sigverify_p95-ms": 40.26, + "components/block": 128.0, + "cpu-ms/block": 177.3, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 49.88, + "full_to_ready_p95-ms": 51.6, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60657186.0, + "allocs/op": 276264.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_off-8", + "iterations": 3, + "ns/op": 250569171.0, + "avg_cpu_cores": 0.7089, + "collection_p50-ms": 200.7, + "collection_p95-ms": 201.2, + "completion_parse_p50-ms": 6.188, + "completion_parse_p95-ms": 6.469, + "completion_sigverify_p50-ms": 39.2, + "completion_sigverify_p95-ms": 40.32, + "components/block": 128.0, + "cpu-ms/block": 177.6, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 49.62, + "full_to_ready_p95-ms": 50.23, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 60748789.0, + "allocs/op": 276269.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_on-8", + "samples": 3, + "median": { + "ns/op": 207816740.0, + "avg_cpu_cores": 0.8998, + "collection_p50-ms": 200.8, + "collection_p95-ms": 201.1, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01778, + "completion_sigverify_p95-ms": 0.02235, + "components/block": 128.0, + "cpu-ms/block": 186.4, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 7.829, + "full_to_ready_p95-ms": 8.333, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02017, + "preparation_wait_p95-ms": 0.02434, + "tx/block": 33760.0, + "B/op": 69734096.0, + "allocs/op": 279203.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_on-8", + "iterations": 3, + "ns/op": 208138109.0, + "avg_cpu_cores": 0.8932, + "collection_p50-ms": 201.2, + "collection_p95-ms": 201.2, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.0172, + "completion_sigverify_p95-ms": 0.02235, + "components/block": 128.0, + "cpu-ms/block": 185.9, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 7.853, + "full_to_ready_p95-ms": 8.999, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02114, + "preparation_wait_p95-ms": 0.02434, + "tx/block": 33760.0, + "B/op": 69690304.0, + "allocs/op": 279213.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_on-8", + "iterations": 3, + "ns/op": 207816740.0, + "avg_cpu_cores": 0.9928, + "collection_p50-ms": 200.8, + "collection_p95-ms": 201.1, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.02023, + "completion_sigverify_p95-ms": 0.02548, + "components/block": 128.0, + "cpu-ms/block": 206.3, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 7.829, + "full_to_ready_p95-ms": 8.333, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.01985, + "preparation_wait_p95-ms": 0.02489, + "tx/block": 33760.0, + "B/op": 69734096.0, + "allocs/op": 279203.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/captured_2622581/workers_4/target_8/tip_200ms/overlap_on-8", + "iterations": 3, + "ns/op": 207211686.0, + "avg_cpu_cores": 0.8998, + "collection_p50-ms": 200.3, + "collection_p95-ms": 200.9, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01778, + "completion_sigverify_p95-ms": 0.02121, + "components/block": 128.0, + "cpu-ms/block": 186.4, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 7.442, + "full_to_ready_p95-ms": 8.205, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02017, + "preparation_wait_p95-ms": 0.02153, + "tx/block": 33760.0, + "B/op": 69779202.0, + "allocs/op": 279201.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "samples": 3, + "median": { + "ns/op": 132732036.0, + "avg_cpu_cores": 1.969, + "collection_p50-ms": 6.714, + "collection_p95-ms": 6.898, + "completion_parse_p50-ms": 4.634, + "completion_parse_p95-ms": 5.372, + "completion_sigverify_p50-ms": 89.28, + "completion_sigverify_p95-ms": 94.08, + "components/block": 691.0, + "cpu-ms/block": 255.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 123.2, + "full_to_ready_p95-ms": 132.1, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260174637.0, + "allocs/op": 235471.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 3, + "ns/op": 132732036.0, + "avg_cpu_cores": 1.926, + "collection_p50-ms": 6.714, + "collection_p95-ms": 6.898, + "completion_parse_p50-ms": 4.501, + "completion_parse_p95-ms": 4.815, + "completion_sigverify_p50-ms": 89.28, + "completion_sigverify_p95-ms": 102.2, + "components/block": 691.0, + "cpu-ms/block": 255.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 123.2, + "full_to_ready_p95-ms": 133.2, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260174637.0, + "allocs/op": 235471.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 3, + "ns/op": 134071642.0, + "avg_cpu_cores": 1.969, + "collection_p50-ms": 8.232, + "collection_p95-ms": 8.625, + "completion_parse_p50-ms": 5.486, + "completion_parse_p95-ms": 5.85, + "completion_sigverify_p50-ms": 93.22, + "completion_sigverify_p95-ms": 94.08, + "components/block": 691.0, + "cpu-ms/block": 264.0, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 128.8, + "full_to_ready_p95-ms": 132.1, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 259991864.0, + "allocs/op": 235464.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 3, + "ns/op": 118432537.0, + "avg_cpu_cores": 1.989, + "collection_p50-ms": 5.898, + "collection_p95-ms": 6.123, + "completion_parse_p50-ms": 4.634, + "completion_parse_p95-ms": 5.372, + "completion_sigverify_p50-ms": 82.63, + "completion_sigverify_p95-ms": 82.8, + "components/block": 691.0, + "cpu-ms/block": 235.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 111.0, + "full_to_ready_p95-ms": 115.7, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260219832.0, + "allocs/op": 235471.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "samples": 3, + "median": { + "ns/op": 140105831.0, + "avg_cpu_cores": 2.039, + "collection_p50-ms": 9.935, + "collection_p95-ms": 11.14, + "completion_parse_p50-ms": 5.302, + "completion_parse_p95-ms": 5.596, + "completion_sigverify_p50-ms": 91.17, + "completion_sigverify_p95-ms": 94.68, + "components/block": 691.0, + "cpu-ms/block": 285.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 2140.0, + "full_to_ready_p50-ms": 131.0, + "full_to_ready_p95-ms": 131.5, + "mean_width": 7.927, + "preparation_wait_p50-ms": 0.00567, + "preparation_wait_p95-ms": 0.007029, + "tx/block": 33760.0, + "B/op": 265780592.0, + "allocs/op": 236507.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 3, + "ns/op": 143317986.0, + "avg_cpu_cores": 1.993, + "collection_p50-ms": 10.38, + "collection_p95-ms": 10.94, + "completion_parse_p50-ms": 5.302, + "completion_parse_p95-ms": 5.596, + "completion_sigverify_p50-ms": 91.17, + "completion_sigverify_p95-ms": 96.29, + "components/block": 691.0, + "cpu-ms/block": 285.7, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 2515.0, + "full_to_ready_p50-ms": 132.7, + "full_to_ready_p95-ms": 136.1, + "mean_width": 7.914, + "preparation_wait_p50-ms": 0.007227, + "preparation_wait_p95-ms": 0.008702, + "tx/block": 33760.0, + "B/op": 265981290.0, + "allocs/op": 236644.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 3, + "ns/op": 140105831.0, + "avg_cpu_cores": 2.039, + "collection_p50-ms": 9.935, + "collection_p95-ms": 11.26, + "completion_parse_p50-ms": 5.33, + "completion_parse_p95-ms": 6.534, + "completion_sigverify_p50-ms": 93.04, + "completion_sigverify_p95-ms": 94.68, + "components/block": 691.0, + "cpu-ms/block": 285.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 2140.0, + "full_to_ready_p50-ms": 131.0, + "full_to_ready_p95-ms": 131.5, + "mean_width": 7.927, + "preparation_wait_p50-ms": 0.00567, + "preparation_wait_p95-ms": 0.006587, + "tx/block": 33760.0, + "B/op": 265780592.0, + "allocs/op": 236507.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 3, + "ns/op": 118146761.0, + "avg_cpu_cores": 2.052, + "collection_p50-ms": 8.247, + "collection_p95-ms": 11.14, + "completion_parse_p50-ms": 5.074, + "completion_parse_p95-ms": 5.303, + "completion_sigverify_p50-ms": 79.37, + "completion_sigverify_p95-ms": 79.81, + "components/block": 691.0, + "cpu-ms/block": 242.4, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 2091.0, + "full_to_ready_p50-ms": 106.7, + "full_to_ready_p95-ms": 116.1, + "mean_width": 7.929, + "preparation_wait_p50-ms": 0.003786, + "preparation_wait_p95-ms": 0.007029, + "tx/block": 33760.0, + "B/op": 265573218.0, + "allocs/op": 236474.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "samples": 3, + "median": { + "ns/op": 318801116.0, + "avg_cpu_cores": 0.7606, + "collection_p50-ms": 201.6, + "collection_p95-ms": 201.7, + "completion_parse_p50-ms": 4.599, + "completion_parse_p95-ms": 5.045, + "completion_sigverify_p50-ms": 83.28, + "completion_sigverify_p95-ms": 85.2, + "components/block": 691.0, + "cpu-ms/block": 242.5, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 118.0, + "full_to_ready_p95-ms": 122.6, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260082442.0, + "allocs/op": 235464.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 3, + "ns/op": 318801116.0, + "avg_cpu_cores": 0.7606, + "collection_p50-ms": 201.0, + "collection_p95-ms": 201.6, + "completion_parse_p50-ms": 4.599, + "completion_parse_p95-ms": 5.045, + "completion_sigverify_p50-ms": 83.28, + "completion_sigverify_p95-ms": 85.2, + "components/block": 691.0, + "cpu-ms/block": 242.5, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 118.3, + "full_to_ready_p95-ms": 122.6, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260036650.0, + "allocs/op": 235461.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 3, + "ns/op": 314110925.0, + "avg_cpu_cores": 0.7601, + "collection_p50-ms": 201.6, + "collection_p95-ms": 201.7, + "completion_parse_p50-ms": 4.269, + "completion_parse_p95-ms": 4.42, + "completion_sigverify_p50-ms": 82.73, + "completion_sigverify_p95-ms": 82.83, + "components/block": 691.0, + "cpu-ms/block": 238.8, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 113.3, + "full_to_ready_p95-ms": 113.6, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260219818.0, + "allocs/op": 235471.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 3, + "ns/op": 322511217.0, + "avg_cpu_cores": 0.7751, + "collection_p50-ms": 201.7, + "collection_p95-ms": 202.5, + "completion_parse_p50-ms": 4.627, + "completion_parse_p95-ms": 5.343, + "completion_sigverify_p50-ms": 84.98, + "completion_sigverify_p95-ms": 88.32, + "components/block": 691.0, + "cpu-ms/block": 250.0, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 118.0, + "full_to_ready_p95-ms": 126.6, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260082442.0, + "allocs/op": 235464.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "samples": 3, + "median": { + "ns/op": 229370055.0, + "avg_cpu_cores": 1.167, + "collection_p50-ms": 201.1, + "collection_p95-ms": 201.2, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.01079, + "completion_sigverify_p50-ms": 0.122, + "completion_sigverify_p95-ms": 0.312, + "components/block": 691.0, + "cpu-ms/block": 266.5, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33744.0, + "full_to_ready_p50-ms": 28.97, + "full_to_ready_p95-ms": 30.22, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1252, + "preparation_wait_p95-ms": 0.126, + "tx/block": 33760.0, + "B/op": 307432845.0, + "allocs/op": 250046.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 3, + "ns/op": 226899169.0, + "avg_cpu_cores": 1.167, + "collection_p50-ms": 201.4, + "collection_p95-ms": 201.4, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.01079, + "completion_sigverify_p50-ms": 0.122, + "completion_sigverify_p95-ms": 0.312, + "components/block": 691.0, + "cpu-ms/block": 264.7, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33744.0, + "full_to_ready_p50-ms": 25.53, + "full_to_ready_p95-ms": 26.34, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1206, + "preparation_wait_p95-ms": 0.1241, + "tx/block": 33760.0, + "B/op": 307640389.0, + "allocs/op": 250060.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 3, + "ns/op": 229370055.0, + "avg_cpu_cores": 1.162, + "collection_p50-ms": 200.7, + "collection_p95-ms": 201.2, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.01841, + "completion_sigverify_p50-ms": 0.1002, + "completion_sigverify_p95-ms": 0.456, + "components/block": 691.0, + "cpu-ms/block": 266.5, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33728.0, + "full_to_ready_p50-ms": 29.75, + "full_to_ready_p95-ms": 30.22, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1252, + "preparation_wait_p95-ms": 0.126, + "tx/block": 33760.0, + "B/op": 307397858.0, + "allocs/op": 250045.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 3, + "ns/op": 231567222.0, + "avg_cpu_cores": 1.363, + "collection_p50-ms": 201.1, + "collection_p95-ms": 201.2, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.1245, + "completion_sigverify_p95-ms": 0.1492, + "components/block": 691.0, + "cpu-ms/block": 315.7, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 28.97, + "full_to_ready_p95-ms": 34.61, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1317, + "preparation_wait_p95-ms": 0.137, + "tx/block": 33760.0, + "B/op": 307432845.0, + "allocs/op": 250046.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_off-8", + "samples": 3, + "median": { + "ns/op": 98393903.0, + "avg_cpu_cores": 2.983, + "collection_p50-ms": 8.19, + "collection_p95-ms": 8.563, + "completion_parse_p50-ms": 4.938, + "completion_parse_p95-ms": 5.485, + "completion_sigverify_p50-ms": 52.0, + "completion_sigverify_p95-ms": 57.0, + "components/block": 691.0, + "cpu-ms/block": 293.5, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 88.12, + "full_to_ready_p95-ms": 95.35, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260266082.0, + "allocs/op": 235474.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_off-8", + "iterations": 3, + "ns/op": 98393903.0, + "avg_cpu_cores": 2.983, + "collection_p50-ms": 8.19, + "collection_p95-ms": 8.563, + "completion_parse_p50-ms": 4.938, + "completion_parse_p95-ms": 5.485, + "completion_sigverify_p50-ms": 55.91, + "completion_sigverify_p95-ms": 57.0, + "components/block": 691.0, + "cpu-ms/block": 293.5, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 88.12, + "full_to_ready_p95-ms": 95.35, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260266082.0, + "allocs/op": 235474.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_off-8", + "iterations": 3, + "ns/op": 105755933.0, + "avg_cpu_cores": 2.776, + "collection_p50-ms": 8.602, + "collection_p95-ms": 9.822, + "completion_parse_p50-ms": 6.422, + "completion_parse_p95-ms": 7.668, + "completion_sigverify_p50-ms": 52.0, + "completion_sigverify_p95-ms": 60.88, + "components/block": 691.0, + "cpu-ms/block": 293.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 90.69, + "full_to_ready_p95-ms": 112.2, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260174000.0, + "allocs/op": 235467.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_off-8", + "iterations": 3, + "ns/op": 81434919.0, + "avg_cpu_cores": 3.009, + "collection_p50-ms": 6.353, + "collection_p95-ms": 6.417, + "completion_parse_p50-ms": 4.568, + "completion_parse_p95-ms": 4.63, + "completion_sigverify_p50-ms": 44.0, + "completion_sigverify_p95-ms": 44.05, + "components/block": 691.0, + "cpu-ms/block": 245.1, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 75.94, + "full_to_ready_p95-ms": 77.03, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260402965.0, + "allocs/op": 235479.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_on-8", + "samples": 3, + "median": { + "ns/op": 85283927.0, + "avg_cpu_cores": 2.979, + "collection_p50-ms": 9.315, + "collection_p95-ms": 10.89, + "completion_parse_p50-ms": 4.251, + "completion_parse_p95-ms": 5.583, + "completion_sigverify_p50-ms": 41.9, + "completion_sigverify_p95-ms": 43.8, + "components/block": 691.0, + "cpu-ms/block": 254.5, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 3430.0, + "full_to_ready_p50-ms": 75.23, + "full_to_ready_p95-ms": 86.25, + "mean_width": 7.885, + "preparation_wait_p50-ms": 0.01225, + "preparation_wait_p95-ms": 0.01287, + "tx/block": 33760.0, + "B/op": 267653104.0, + "allocs/op": 237063.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_on-8", + "iterations": 3, + "ns/op": 81921701.0, + "avg_cpu_cores": 3.107, + "collection_p50-ms": 9.142, + "collection_p95-ms": 9.153, + "completion_parse_p50-ms": 4.251, + "completion_parse_p95-ms": 4.784, + "completion_sigverify_p50-ms": 41.9, + "completion_sigverify_p95-ms": 42.1, + "components/block": 691.0, + "cpu-ms/block": 254.5, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 3430.0, + "full_to_ready_p50-ms": 75.23, + "full_to_ready_p95-ms": 75.61, + "mean_width": 7.885, + "preparation_wait_p50-ms": 0.007166, + "preparation_wait_p95-ms": 0.0106, + "tx/block": 33760.0, + "B/op": 267789989.0, + "allocs/op": 237063.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_on-8", + "iterations": 3, + "ns/op": 106211708.0, + "avg_cpu_cores": 2.96, + "collection_p50-ms": 12.72, + "collection_p95-ms": 12.77, + "completion_parse_p50-ms": 6.076, + "completion_parse_p95-ms": 6.677, + "completion_sigverify_p50-ms": 49.86, + "completion_sigverify_p95-ms": 57.7, + "components/block": 691.0, + "cpu-ms/block": 314.4, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 3397.0, + "full_to_ready_p50-ms": 91.87, + "full_to_ready_p95-ms": 99.87, + "mean_width": 7.886, + "preparation_wait_p50-ms": 0.01751, + "preparation_wait_p95-ms": 0.01784, + "tx/block": 33760.0, + "B/op": 267653104.0, + "allocs/op": 237049.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/catchup/overlap_on-8", + "iterations": 3, + "ns/op": 85283927.0, + "avg_cpu_cores": 2.979, + "collection_p50-ms": 9.315, + "collection_p95-ms": 10.89, + "completion_parse_p50-ms": 3.727, + "completion_parse_p95-ms": 5.583, + "completion_sigverify_p50-ms": 41.31, + "completion_sigverify_p95-ms": 43.8, + "components/block": 691.0, + "cpu-ms/block": 254.1, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 3561.0, + "full_to_ready_p50-ms": 75.15, + "full_to_ready_p95-ms": 86.25, + "mean_width": 7.88, + "preparation_wait_p50-ms": 0.01225, + "preparation_wait_p95-ms": 0.01287, + "tx/block": 33760.0, + "B/op": 267515682.0, + "allocs/op": 237096.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_off-8", + "samples": 3, + "median": { + "ns/op": 276593977.0, + "avg_cpu_cores": 0.8742, + "collection_p50-ms": 201.5, + "collection_p95-ms": 201.6, + "completion_parse_p50-ms": 4.437, + "completion_parse_p95-ms": 4.728, + "completion_sigverify_p50-ms": 43.5, + "completion_sigverify_p95-ms": 44.33, + "components/block": 691.0, + "cpu-ms/block": 241.8, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 77.04, + "full_to_ready_p95-ms": 79.84, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260403005.0, + "allocs/op": 235479.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_off-8", + "iterations": 3, + "ns/op": 276365085.0, + "avg_cpu_cores": 0.8723, + "collection_p50-ms": 201.5, + "collection_p95-ms": 201.6, + "completion_parse_p50-ms": 4.437, + "completion_parse_p95-ms": 4.504, + "completion_sigverify_p50-ms": 43.22, + "completion_sigverify_p95-ms": 43.6, + "components/block": 691.0, + "cpu-ms/block": 241.1, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 77.04, + "full_to_ready_p95-ms": 77.05, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260403005.0, + "allocs/op": 235479.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_off-8", + "iterations": 3, + "ns/op": 280110502.0, + "avg_cpu_cores": 0.9259, + "collection_p50-ms": 200.8, + "collection_p95-ms": 201.1, + "completion_parse_p50-ms": 4.666, + "completion_parse_p95-ms": 4.728, + "completion_sigverify_p50-ms": 44.11, + "completion_sigverify_p95-ms": 56.8, + "components/block": 691.0, + "cpu-ms/block": 259.4, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 77.81, + "full_to_ready_p95-ms": 86.07, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260495133.0, + "allocs/op": 235486.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_off-8", + "iterations": 3, + "ns/op": 276593977.0, + "avg_cpu_cores": 0.8742, + "collection_p50-ms": 201.6, + "collection_p95-ms": 201.7, + "completion_parse_p50-ms": 4.198, + "completion_parse_p95-ms": 6.163, + "completion_sigverify_p50-ms": 43.5, + "completion_sigverify_p95-ms": 44.33, + "components/block": 691.0, + "cpu-ms/block": 241.8, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 75.26, + "full_to_ready_p95-ms": 79.84, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 259991381.0, + "allocs/op": 235460.0 + } + ] + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_on-8", + "samples": 3, + "median": { + "ns/op": 227100110.0, + "avg_cpu_cores": 1.167, + "collection_p50-ms": 201.2, + "collection_p95-ms": 201.4, + "completion_parse_p50-ms": 0.01037, + "completion_parse_p95-ms": 0.01195, + "completion_sigverify_p50-ms": 0.2797, + "completion_sigverify_p95-ms": 0.3548, + "components/block": 691.0, + "cpu-ms/block": 264.9, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33728.0, + "full_to_ready_p50-ms": 25.69, + "full_to_ready_p95-ms": 27.44, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1228, + "preparation_wait_p95-ms": 0.1242, + "tx/block": 33760.0, + "B/op": 307727008.0, + "allocs/op": 250008.0 + }, + "rows": [ + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_on-8", + "iterations": 3, + "ns/op": 227100110.0, + "avg_cpu_cores": 1.166, + "collection_p50-ms": 201.2, + "collection_p95-ms": 201.4, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.1103, + "completion_sigverify_p95-ms": 0.1149, + "components/block": 691.0, + "cpu-ms/block": 264.9, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 25.08, + "full_to_ready_p95-ms": 27.44, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1228, + "preparation_wait_p95-ms": 0.124, + "tx/block": 33760.0, + "B/op": 307747917.0, + "allocs/op": 250014.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_on-8", + "iterations": 3, + "ns/op": 241182176.0, + "avg_cpu_cores": 1.235, + "collection_p50-ms": 201.4, + "collection_p95-ms": 202.9, + "completion_parse_p50-ms": 0.01357, + "completion_parse_p95-ms": 0.04438, + "completion_sigverify_p50-ms": 0.3222, + "completion_sigverify_p95-ms": 0.4977, + "components/block": 691.0, + "cpu-ms/block": 297.8, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33712.0, + "full_to_ready_p50-ms": 34.46, + "full_to_ready_p95-ms": 51.53, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1261, + "preparation_wait_p95-ms": 0.1812, + "tx/block": 33760.0, + "B/op": 307727008.0, + "allocs/op": 250008.0 + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_4/target_8/tip_200ms/overlap_on-8", + "iterations": 3, + "ns/op": 226480518.0, + "avg_cpu_cores": 1.167, + "collection_p50-ms": 200.9, + "collection_p95-ms": 201.3, + "completion_parse_p50-ms": 0.01037, + "completion_parse_p95-ms": 0.01195, + "completion_sigverify_p50-ms": 0.2797, + "completion_sigverify_p95-ms": 0.3548, + "components/block": 691.0, + "cpu-ms/block": 264.2, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33728.0, + "full_to_ready_p50-ms": 25.69, + "full_to_ready_p95-ms": 25.87, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1226, + "preparation_wait_p95-ms": 0.1242, + "tx/block": 33760.0, + "B/op": 307612829.0, + "allocs/op": 250008.0 + } + ] + } +] diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/binary-sha256.txt b/docs/results/sigverify-streaming/2026-09-12-zen5/binary-sha256.txt new file mode 100644 index 00000000..41f7f057 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/binary-sha256.txt @@ -0,0 +1,3 @@ +2cf2d2baeeaba0bf1218a36bee05dd9fc7323db5becef125baafb898e4d42b02 /srv/mithril-sigverify-streaming-20260912/bin/turbine.test +f36508c1d113ea8048ba8b516f4a62b0c15b54369efa41c6211bd4bd491c2da8 /srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test +6d72002ecbc772bafd7ac39a965403e5694b3f83c21cc65bfcf0d9321aaeb7f2 /srv/mithril-sigverify-streaming-20260912/bin/replay.test diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/captured-pool-flow.log b/docs/results/sigverify-streaming/2026-09-12-zen5/captured-pool-flow.log new file mode 100644 index 00000000..43a35eef --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/captured-pool-flow.log @@ -0,0 +1,113 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/catchup-8 3 156715891 ns/op 2.043 avg_cpu_cores 1.000 components/block 320.2 cpu-ms/block 0.0002710 feed_lag_p50-ms 0.001282 feed_lag_p95-ms 4.000 mean_width 158.0 ready_p50-ms 164.2 ready_p95-ms 158.0 residual_p50-ms 164.2 residual_p95-ms 33760 signatures/block 215422 signatures/s 0.01062 submit_p50-ms 0.02492 submit_p95-ms 7023138 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/catchup-8 3 155986070 ns/op 2.047 avg_cpu_cores 1.000 components/block 319.2 cpu-ms/block 0.0002510 feed_lag_p50-ms 0.0007810 feed_lag_p95-ms 4.000 mean_width 151.6 ready_p50-ms 171.1 ready_p95-ms 151.6 residual_p50-ms 171.1 residual_p95-ms 33760 signatures/block 216430 signatures/s 0.006532 submit_p50-ms 0.007404 submit_p95-ms 7023144 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/catchup-8 3 145764753 ns/op 2.047 avg_cpu_cores 1.000 components/block 298.4 cpu-ms/block 0.0002810 feed_lag_p50-ms 0.0006810 feed_lag_p95-ms 4.000 mean_width 145.4 ready_p50-ms 146.6 ready_p95-ms 145.4 residual_p50-ms 146.6 residual_p95-ms 33760 signatures/block 231606 signatures/s 0.004178 submit_p50-ms 0.007975 submit_p95-ms 7023133 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_after_complete-8 3 344523723 ns/op 0.8561 avg_cpu_cores 126.0 components/block 295.0 cpu-ms/block 100.2 feed_lag_p50-ms 191.4 feed_lag_p95-ms 4.000 mean_width 244.4 ready_p50-ms 335.6 ready_p95-ms 144.7 residual_p50-ms 145.2 residual_p95-ms 33760 signatures/block 97990 signatures/s 0.01582 submit_p50-ms 0.01597 submit_p95-ms 7033338 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_after_complete-8 3 352992831 ns/op 0.8764 avg_cpu_cores 126.0 components/block 309.4 cpu-ms/block 100.2 feed_lag_p50-ms 191.4 feed_lag_p95-ms 4.000 mean_width 252.4 ready_p50-ms 343.6 ready_p95-ms 144.6 residual_p50-ms 170.7 residual_p95-ms 33760 signatures/block 95639 signatures/s 0.01750 submit_p50-ms 0.01914 submit_p95-ms 7033616 B/op 67540 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_after_complete-8 3 363417787 ns/op 0.9118 avg_cpu_cores 126.0 components/block 331.4 cpu-ms/block 99.82 feed_lag_p50-ms 191.0 feed_lag_p95-ms 4.000 mean_width 263.0 ready_p50-ms 355.8 ready_p95-ms 163.8 residual_p50-ms 175.6 residual_p95-ms 33760 signatures/block 92896 signatures/s 0.02098 submit_p50-ms 0.02324 submit_p95-ms 7033530 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_overlap-8 3 201111285 ns/op 1.522 avg_cpu_cores 126.0 components/block 306.1 cpu-ms/block 0.03627 feed_lag_p50-ms 0.8607 feed_lag_p95-ms 3.956 mean_width 1.602 ready_p50-ms 2.473 ready_p95-ms 1.080 residual_p50-ms 1.219 residual_p95-ms 33760 signatures/block 167867 signatures/s 0.001032 submit_p50-ms 0.003166 submit_p95-ms 7165506 B/op 68945 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_overlap-8 3 201378639 ns/op 1.498 avg_cpu_cores 126.0 components/block 301.7 cpu-ms/block 0.2205 feed_lag_p50-ms 0.8775 feed_lag_p95-ms 3.956 mean_width 1.582 ready_p50-ms 2.532 ready_p95-ms 1.361 residual_p50-ms 1.407 residual_p95-ms 33760 signatures/block 167644 signatures/s 0.001123 submit_p50-ms 0.002805 submit_p95-ms 7167869 B/op 68945 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_overlap-8 3 201155070 ns/op 1.486 avg_cpu_cores 126.0 components/block 299.0 cpu-ms/block 0.05468 feed_lag_p50-ms 0.8310 feed_lag_p95-ms 3.956 mean_width 1.579 ready_p50-ms 2.456 ready_p95-ms 1.334 residual_p50-ms 1.380 residual_p95-ms 33760 signatures/block 167831 signatures/s 0.001042 submit_p50-ms 0.002434 submit_p95-ms 7159269 B/op 68921 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/catchup-8 3 74916706 ns/op 2.041 avg_cpu_cores 1.000 components/block 152.9 cpu-ms/block 0.0002510 feed_lag_p50-ms 0.0004810 feed_lag_p95-ms 8.000 mean_width 74.90 ready_p50-ms 75.95 ready_p95-ms 74.90 residual_p50-ms 75.95 residual_p95-ms 33760 signatures/block 450634 signatures/s 0.004738 submit_p50-ms 0.006292 submit_p95-ms 7023266 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/catchup-8 3 75787979 ns/op 2.041 avg_cpu_cores 1.000 components/block 154.7 cpu-ms/block 0.0003900 feed_lag_p50-ms 0.0005310 feed_lag_p95-ms 8.000 mean_width 75.44 ready_p50-ms 76.90 ready_p95-ms 75.44 residual_p50-ms 76.90 residual_p95-ms 33760 signatures/block 445453 signatures/s 0.006492 submit_p50-ms 0.006763 submit_p95-ms 7023258 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/catchup-8 3 83724900 ns/op 2.025 avg_cpu_cores 1.000 components/block 169.5 cpu-ms/block 0.0003300 feed_lag_p50-ms 0.0004310 feed_lag_p95-ms 8.000 mean_width 86.47 ready_p50-ms 87.41 ready_p95-ms 86.47 residual_p50-ms 87.41 residual_p95-ms 33760 signatures/block 403225 signatures/s 0.007554 submit_p50-ms 0.007595 submit_p95-ms 7023253 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_after_complete-8 3 275675193 ns/op 0.5545 avg_cpu_cores 126.0 components/block 152.9 cpu-ms/block 100.2 feed_lag_p50-ms 191.4 feed_lag_p95-ms 8.000 mean_width 175.6 ready_p50-ms 266.8 ready_p95-ms 75.25 residual_p50-ms 76.98 residual_p95-ms 33760 signatures/block 122463 signatures/s 0.01418 submit_p50-ms 0.01462 submit_p95-ms 7033472 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_after_complete-8 3 288578162 ns/op 0.6196 avg_cpu_cores 126.0 components/block 178.8 cpu-ms/block 100.2 feed_lag_p50-ms 191.4 feed_lag_p95-ms 8.000 mean_width 188.3 ready_p50-ms 279.5 ready_p95-ms 86.33 residual_p50-ms 98.29 residual_p95-ms 33760 signatures/block 116987 signatures/s 0.01635 submit_p50-ms 0.01759 submit_p95-ms 7033461 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_after_complete-8 3 275152604 ns/op 0.5544 avg_cpu_cores 126.0 components/block 152.5 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 8.000 mean_width 175.0 ready_p50-ms 266.2 ready_p95-ms 75.23 residual_p50-ms 76.00 residual_p95-ms 33760 signatures/block 122696 signatures/s 0.01576 submit_p50-ms 0.01683 submit_p95-ms 7079245 B/op 67541 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_overlap-8 3 201070435 ns/op 0.8694 avg_cpu_cores 126.0 components/block 174.8 cpu-ms/block 0.4408 feed_lag_p50-ms 0.9889 feed_lag_p95-ms 7.912 mean_width 1.219 ready_p50-ms 1.939 ready_p95-ms 1.278 residual_p50-ms 1.368 residual_p95-ms 33760 signatures/block 167901 signatures/s 0.001523 submit_p50-ms 0.005731 submit_p95-ms 7175717 B/op 68921 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_overlap-8 3 201082624 ns/op 0.7981 avg_cpu_cores 126.0 components/block 160.5 cpu-ms/block 0.5138 feed_lag_p50-ms 1.002 feed_lag_p95-ms 7.912 mean_width 1.116 ready_p50-ms 1.674 ready_p95-ms 1.053 residual_p50-ms 1.128 residual_p95-ms 33760 signatures/block 167891 signatures/s 0.001002 submit_p50-ms 0.002716 submit_p95-ms 7175933 B/op 68923 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_overlap-8 3 201301027 ns/op 0.7960 avg_cpu_cores 126.0 components/block 160.2 cpu-ms/block 0.5253 feed_lag_p50-ms 0.9638 feed_lag_p95-ms 7.912 mean_width 1.160 ready_p50-ms 1.610 ready_p95-ms 1.264 residual_p50-ms 1.409 residual_p95-ms 33760 signatures/block 167709 signatures/s 0.001052 submit_p50-ms 0.002464 submit_p95-ms 7176285 B/op 68929 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/catchup-8 3 79099409 ns/op 3.964 avg_cpu_cores 1.000 components/block 313.6 cpu-ms/block 0.0002610 feed_lag_p50-ms 0.0008310 feed_lag_p95-ms 4.000 mean_width 76.63 ready_p50-ms 85.46 ready_p95-ms 76.63 residual_p50-ms 85.46 residual_p95-ms 33760 signatures/block 426805 signatures/s 0.006302 submit_p50-ms 0.006773 submit_p95-ms 7023410 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/catchup-8 3 80922132 ns/op 3.958 avg_cpu_cores 1.000 components/block 320.3 cpu-ms/block 0.0003000 feed_lag_p50-ms 0.0006520 feed_lag_p95-ms 4.000 mean_width 77.50 ready_p50-ms 87.97 ready_p95-ms 77.50 residual_p50-ms 87.97 residual_p95-ms 33760 signatures/block 417191 signatures/s 0.006211 submit_p50-ms 0.01736 submit_p95-ms 7023410 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/catchup-8 3 75579922 ns/op 4.026 avg_cpu_cores 1.000 components/block 304.3 cpu-ms/block 0.0003910 feed_lag_p50-ms 0.0004910 feed_lag_p95-ms 4.000 mean_width 75.53 ready_p50-ms 75.76 ready_p95-ms 75.53 residual_p50-ms 75.76 residual_p95-ms 33760 signatures/block 446679 signatures/s 0.004758 submit_p50-ms 0.005430 submit_p95-ms 7023405 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_after_complete-8 3 277358928 ns/op 1.110 avg_cpu_cores 126.0 components/block 307.9 cpu-ms/block 99.99 feed_lag_p50-ms 191.2 feed_lag_p95-ms 4.000 mean_width 176.8 ready_p50-ms 268.0 ready_p95-ms 76.98 residual_p50-ms 79.21 residual_p95-ms 33760 signatures/block 121720 signatures/s 0.01168 submit_p50-ms 0.01332 submit_p95-ms 7033597 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_after_complete-8 3 281786997 ns/op 1.134 avg_cpu_cores 126.0 components/block 319.5 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 4.000 mean_width 181.5 ready_p50-ms 272.7 ready_p95-ms 77.53 residual_p50-ms 90.55 residual_p95-ms 33760 signatures/block 119807 signatures/s 0.01547 submit_p50-ms 0.01700 submit_p95-ms 7033605 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_after_complete-8 3 275851016 ns/op 1.100 avg_cpu_cores 126.0 components/block 303.6 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 4.000 mean_width 175.8 ready_p50-ms 267.0 ready_p95-ms 75.74 residual_p50-ms 76.57 residual_p95-ms 33760 signatures/block 122385 signatures/s 0.01534 submit_p50-ms 0.01584 submit_p95-ms 7033605 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_overlap-8 3 200706590 ns/op 1.498 avg_cpu_cores 126.0 components/block 300.7 cpu-ms/block 0.5031 feed_lag_p50-ms 0.9977 feed_lag_p95-ms 3.956 mean_width 1.105 ready_p50-ms 1.659 ready_p95-ms 0.5523 residual_p50-ms 1.056 residual_p95-ms 33760 signatures/block 168206 signatures/s 0.001102 submit_p50-ms 0.003366 submit_p95-ms 7200192 B/op 68942 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_overlap-8 3 200821431 ns/op 1.836 avg_cpu_cores 126.0 components/block 368.8 cpu-ms/block 0.3622 feed_lag_p50-ms 1.002 feed_lag_p95-ms 3.956 mean_width 1.224 ready_p50-ms 2.145 ready_p95-ms 0.9866 residual_p50-ms 1.055 residual_p95-ms 33760 signatures/block 168110 signatures/s 0.002164 submit_p50-ms 0.004518 submit_p95-ms 7200909 B/op 68946 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_overlap-8 3 201025437 ns/op 1.586 avg_cpu_cores 126.0 components/block 318.8 cpu-ms/block 0.4136 feed_lag_p50-ms 0.9729 feed_lag_p95-ms 3.956 mean_width 1.214 ready_p50-ms 1.677 ready_p95-ms 1.053 residual_p50-ms 1.325 residual_p95-ms 33760 signatures/block 167939 signatures/s 0.001483 submit_p50-ms 0.004278 submit_p95-ms 7195901 B/op 68936 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/catchup-8 3 38632690 ns/op 4.029 avg_cpu_cores 1.000 components/block 155.7 cpu-ms/block 0.0001700 feed_lag_p50-ms 0.0004600 feed_lag_p95-ms 8.000 mean_width 38.50 ready_p50-ms 38.98 ready_p95-ms 38.50 residual_p50-ms 38.98 residual_p95-ms 33760 signatures/block 873871 signatures/s 0.004749 submit_p50-ms 0.004749 submit_p95-ms 7023658 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/catchup-8 3 38613093 ns/op 4.016 avg_cpu_cores 1.000 components/block 155.1 cpu-ms/block 0.0002600 feed_lag_p50-ms 0.0005910 feed_lag_p95-ms 8.000 mean_width 38.29 ready_p50-ms 39.67 ready_p95-ms 38.29 residual_p50-ms 39.67 residual_p95-ms 33760 signatures/block 874315 signatures/s 0.004198 submit_p50-ms 0.005150 submit_p95-ms 7023658 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/catchup-8 3 40094161 ns/op 3.977 avg_cpu_cores 1.000 components/block 159.5 cpu-ms/block 0.0002410 feed_lag_p50-ms 0.0003910 feed_lag_p95-ms 8.000 mean_width 40.29 ready_p50-ms 40.86 ready_p95-ms 40.29 residual_p50-ms 40.86 residual_p95-ms 33760 signatures/block 842018 signatures/s 0.004117 submit_p50-ms 0.004779 submit_p95-ms 7023653 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_after_complete-8 3 240210161 ns/op 0.6517 avg_cpu_cores 126.0 components/block 156.5 cpu-ms/block 100.2 feed_lag_p50-ms 191.4 feed_lag_p95-ms 8.000 mean_width 139.7 ready_p50-ms 230.9 ready_p95-ms 39.96 residual_p50-ms 41.70 residual_p95-ms 33760 signatures/block 140544 signatures/s 0.01466 submit_p50-ms 0.01629 submit_p95-ms 7033861 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_after_complete-8 3 244046162 ns/op 0.6703 avg_cpu_cores 126.0 components/block 163.6 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 8.000 mean_width 143.7 ready_p50-ms 234.9 ready_p95-ms 41.63 residual_p50-ms 50.78 residual_p95-ms 33760 signatures/block 138334 signatures/s 0.01559 submit_p50-ms 0.1937 submit_p95-ms 7033853 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_after_complete-8 3 250358365 ns/op 0.7621 avg_cpu_cores 126.0 components/block 190.8 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 8.000 mean_width 150.0 ready_p50-ms 241.2 ready_p95-ms 50.84 residual_p50-ms 52.16 residual_p95-ms 33760 signatures/block 134847 signatures/s 0.01572 submit_p50-ms 0.01687 submit_p95-ms 7033853 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_overlap-8 3 200893667 ns/op 0.8816 avg_cpu_cores 126.0 components/block 177.1 cpu-ms/block 0.4989 feed_lag_p50-ms 0.9857 feed_lag_p95-ms 7.912 mean_width 0.9093 ready_p50-ms 1.428 ready_p95-ms 0.8878 residual_p50-ms 1.069 residual_p95-ms 33760 signatures/block 168049 signatures/s 0.001713 submit_p50-ms 0.005570 submit_p95-ms 7225490 B/op 68918 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_overlap-8 3 201019836 ns/op 0.8626 avg_cpu_cores 126.0 components/block 173.4 cpu-ms/block 0.5209 feed_lag_p50-ms 1.002 feed_lag_p95-ms 7.912 mean_width 0.9120 ready_p50-ms 1.381 ready_p95-ms 1.081 residual_p50-ms 1.125 residual_p95-ms 33760 signatures/block 167944 signatures/s 0.001563 submit_p50-ms 0.004458 submit_p95-ms 7226130 B/op 68919 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_overlap-8 3 200749032 ns/op 0.8810 avg_cpu_cores 126.0 components/block 176.9 cpu-ms/block 0.5054 feed_lag_p50-ms 0.9943 feed_lag_p95-ms 7.912 mean_width 0.9516 ready_p50-ms 1.431 ready_p95-ms 0.5820 residual_p50-ms 1.073 residual_p95-ms 33760 signatures/block 168170 signatures/s 0.001633 submit_p50-ms 0.005690 submit_p95-ms 7226581 B/op 68928 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/catchup-8 3 155918266 ns/op 2.038 avg_cpu_cores 1.000 components/block 317.8 cpu-ms/block 0.0004010 feed_lag_p50-ms 0.0004910 feed_lag_p95-ms 4.000 mean_width 156.2 ready_p50-ms 157.5 ready_p95-ms 156.2 residual_p50-ms 157.5 residual_p95-ms 33760 signatures/block 216524 signatures/s 0.007043 submit_p50-ms 0.007063 submit_p95-ms 7023138 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/catchup-8 3 144724837 ns/op 2.047 avg_cpu_cores 1.000 components/block 296.2 cpu-ms/block 0.0002410 feed_lag_p50-ms 0.0004610 feed_lag_p95-ms 4.000 mean_width 144.7 ready_p50-ms 145.3 ready_p95-ms 144.7 residual_p50-ms 145.3 residual_p95-ms 33760 signatures/block 233270 signatures/s 0.004688 submit_p50-ms 0.006221 submit_p95-ms 7023133 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/catchup-8 3 167371225 ns/op 2.049 avg_cpu_cores 1.000 components/block 343.0 cpu-ms/block 0.0002700 feed_lag_p50-ms 0.0007810 feed_lag_p95-ms 4.000 mean_width 168.7 ready_p50-ms 179.5 ready_p95-ms 168.7 residual_p50-ms 179.5 residual_p95-ms 33760 signatures/block 201707 signatures/s 0.005831 submit_p50-ms 0.006833 submit_p95-ms 7023133 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/tip_200ms_after_complete-8 3 348985373 ns/op 0.8724 avg_cpu_cores 126.0 components/block 304.4 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 4.000 mean_width 248.9 ready_p50-ms 340.1 ready_p95-ms 150.1 residual_p50-ms 152.0 residual_p95-ms 33760 signatures/block 96738 signatures/s 0.01628 submit_p50-ms 0.01804 submit_p95-ms 7033333 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/tip_200ms_after_complete-8 3 353413098 ns/op 0.8855 avg_cpu_cores 126.0 components/block 312.9 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 4.000 mean_width 253.3 ready_p50-ms 344.5 ready_p95-ms 155.7 residual_p50-ms 158.2 residual_p95-ms 33760 signatures/block 95526 signatures/s 0.02336 submit_p50-ms 0.03805 submit_p95-ms 7079125 B/op 67541 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/tip_200ms_after_complete-8 3 349811570 ns/op 0.8763 avg_cpu_cores 126.0 components/block 306.6 cpu-ms/block 99.80 feed_lag_p50-ms 191.0 feed_lag_p95-ms 4.000 mean_width 249.2 ready_p50-ms 340.4 ready_p95-ms 151.6 residual_p50-ms 153.0 residual_p95-ms 33760 signatures/block 96509 signatures/s 0.01705 submit_p50-ms 0.01774 submit_p95-ms 7033333 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/tip_200ms_overlap-8 3 200998043 ns/op 1.604 avg_cpu_cores 126.0 components/block 322.3 cpu-ms/block 0.03342 feed_lag_p50-ms 0.8444 feed_lag_p95-ms 3.956 mean_width 1.732 ready_p50-ms 3.310 ready_p95-ms 0.7673 residual_p50-ms 1.469 residual_p95-ms 33760 signatures/block 167962 signatures/s 0.001633 submit_p50-ms 0.004388 submit_p95-ms 7159493 B/op 68923 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/tip_200ms_overlap-8 3 200881915 ns/op 1.491 avg_cpu_cores 126.0 components/block 299.4 cpu-ms/block 0.2175 feed_lag_p50-ms 0.8653 feed_lag_p95-ms 3.956 mean_width 1.578 ready_p50-ms 2.468 ready_p95-ms 0.9112 residual_p50-ms 1.054 residual_p95-ms 33760 signatures/block 168059 signatures/s 0.001072 submit_p50-ms 0.002876 submit_p95-ms 7159410 B/op 68922 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/tip_200ms_overlap-8 3 200919959 ns/op 1.509 avg_cpu_cores 126.0 components/block 303.1 cpu-ms/block 0.04134 feed_lag_p50-ms 0.8647 feed_lag_p95-ms 3.956 mean_width 1.588 ready_p50-ms 2.515 ready_p95-ms 0.6775 residual_p50-ms 1.408 residual_p95-ms 33760 signatures/block 168027 signatures/s 0.001152 submit_p50-ms 0.002685 submit_p95-ms 7160456 B/op 68931 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/catchup-8 3 78536665 ns/op 2.043 avg_cpu_cores 1.000 components/block 160.4 cpu-ms/block 0.0002010 feed_lag_p50-ms 0.001353 feed_lag_p95-ms 8.000 mean_width 76.40 ready_p50-ms 84.11 ready_p95-ms 76.40 residual_p50-ms 84.11 residual_p95-ms 33760 signatures/block 429863 signatures/s 0.005971 submit_p50-ms 0.01674 submit_p95-ms 7023253 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/catchup-8 3 74931420 ns/op 2.045 avg_cpu_cores 1.000 components/block 153.2 cpu-ms/block 0.0002610 feed_lag_p50-ms 0.0004910 feed_lag_p95-ms 8.000 mean_width 73.86 ready_p50-ms 77.19 ready_p95-ms 73.86 residual_p50-ms 77.19 residual_p95-ms 33760 signatures/block 450545 signatures/s 0.005530 submit_p50-ms 0.005611 submit_p95-ms 7069050 B/op 67540 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/catchup-8 3 74095918 ns/op 2.048 avg_cpu_cores 1.000 components/block 151.7 cpu-ms/block 0.0002610 feed_lag_p50-ms 0.0005510 feed_lag_p95-ms 8.000 mean_width 74.05 ready_p50-ms 74.35 ready_p95-ms 74.05 residual_p50-ms 74.35 residual_p95-ms 33760 signatures/block 455626 signatures/s 0.005040 submit_p50-ms 0.005761 submit_p95-ms 7023266 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/tip_200ms_after_complete-8 3 276093363 ns/op 0.5578 avg_cpu_cores 126.0 components/block 154.0 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 8.000 mean_width 176.0 ready_p50-ms 267.2 ready_p95-ms 75.77 residual_p50-ms 77.27 residual_p95-ms 33760 signatures/block 122277 signatures/s 0.01560 submit_p50-ms 0.01629 submit_p95-ms 7033453 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/tip_200ms_after_complete-8 3 275962471 ns/op 0.5557 avg_cpu_cores 126.0 components/block 153.3 cpu-ms/block 100.2 feed_lag_p50-ms 191.4 feed_lag_p95-ms 8.000 mean_width 175.6 ready_p50-ms 266.8 ready_p95-ms 75.18 residual_p50-ms 77.82 residual_p95-ms 33760 signatures/block 122335 signatures/s 0.01006 submit_p50-ms 0.01518 submit_p95-ms 7033461 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/tip_200ms_after_complete-8 3 276459279 ns/op 0.5617 avg_cpu_cores 126.0 components/block 155.3 cpu-ms/block 99.96 feed_lag_p50-ms 191.2 feed_lag_p95-ms 8.000 mean_width 176.3 ready_p50-ms 267.5 ready_p95-ms 76.20 residual_p50-ms 77.62 residual_p95-ms 33760 signatures/block 122116 signatures/s 0.01565 submit_p50-ms 0.02936 submit_p95-ms 7033461 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/tip_200ms_overlap-8 3 201053633 ns/op 0.7943 avg_cpu_cores 126.0 components/block 159.7 cpu-ms/block 0.5123 feed_lag_p50-ms 0.9677 feed_lag_p95-ms 7.912 mean_width 1.131 ready_p50-ms 1.609 ready_p95-ms 1.133 residual_p50-ms 1.338 residual_p95-ms 33760 signatures/block 167915 signatures/s 0.0009820 submit_p50-ms 0.002364 submit_p95-ms 7175658 B/op 68923 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/tip_200ms_overlap-8 3 200775689 ns/op 0.8035 avg_cpu_cores 126.0 components/block 161.3 cpu-ms/block 0.4791 feed_lag_p50-ms 0.9798 feed_lag_p95-ms 7.912 mean_width 1.141 ready_p50-ms 1.630 ready_p95-ms 0.8427 residual_p50-ms 1.052 residual_p95-ms 33760 signatures/block 168148 signatures/s 0.001102 submit_p50-ms 0.002976 submit_p95-ms 7179669 B/op 68929 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/tip_200ms_overlap-8 3 200811376 ns/op 0.8110 avg_cpu_cores 126.0 components/block 162.9 cpu-ms/block 0.5077 feed_lag_p50-ms 0.9730 feed_lag_p95-ms 7.912 mean_width 1.170 ready_p50-ms 1.645 ready_p95-ms 0.6530 residual_p50-ms 1.185 residual_p95-ms 33760 signatures/block 168118 signatures/s 0.001042 submit_p50-ms 0.003306 submit_p95-ms 7175690 B/op 68923 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/catchup-8 3 75328778 ns/op 4.020 avg_cpu_cores 1.000 components/block 302.8 cpu-ms/block 0.0002010 feed_lag_p50-ms 0.0004810 feed_lag_p95-ms 4.000 mean_width 75.78 ready_p50-ms 75.98 ready_p95-ms 75.78 residual_p50-ms 75.98 residual_p95-ms 33760 signatures/block 448169 signatures/s 0.005461 submit_p50-ms 0.006863 submit_p95-ms 7023397 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/catchup-8 3 75774293 ns/op 4.044 avg_cpu_cores 1.000 components/block 306.4 cpu-ms/block 0.0004800 feed_lag_p50-ms 0.0004810 feed_lag_p95-ms 4.000 mean_width 76.23 ready_p50-ms 76.72 ready_p95-ms 76.23 residual_p50-ms 76.72 residual_p95-ms 33760 signatures/block 445534 signatures/s 0.005711 submit_p50-ms 0.006102 submit_p95-ms 7023402 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/catchup-8 3 78168545 ns/op 3.951 avg_cpu_cores 1.000 components/block 308.8 cpu-ms/block 0.0002400 feed_lag_p50-ms 0.0005810 feed_lag_p95-ms 4.000 mean_width 77.58 ready_p50-ms 80.96 ready_p95-ms 77.58 residual_p50-ms 80.96 residual_p95-ms 33760 signatures/block 431887 signatures/s 0.004168 submit_p50-ms 0.005010 submit_p95-ms 7023397 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/tip_200ms_after_complete-8 3 275602430 ns/op 1.092 avg_cpu_cores 126.0 components/block 301.1 cpu-ms/block 100.2 feed_lag_p50-ms 191.4 feed_lag_p95-ms 4.000 mean_width 175.7 ready_p50-ms 266.9 ready_p95-ms 75.17 residual_p50-ms 76.46 residual_p95-ms 33760 signatures/block 122495 signatures/s 0.01314 submit_p50-ms 0.01613 submit_p95-ms 7033605 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/tip_200ms_after_complete-8 3 279023273 ns/op 1.120 avg_cpu_cores 126.0 components/block 312.5 cpu-ms/block 100.2 feed_lag_p50-ms 191.4 feed_lag_p95-ms 4.000 mean_width 178.6 ready_p50-ms 269.8 ready_p95-ms 74.63 residual_p50-ms 88.00 residual_p95-ms 33760 signatures/block 120993 signatures/s 0.01526 submit_p50-ms 0.01800 submit_p95-ms 7033597 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/tip_200ms_after_complete-8 3 274951714 ns/op 1.090 avg_cpu_cores 126.0 components/block 299.7 cpu-ms/block 100.2 feed_lag_p50-ms 191.4 feed_lag_p95-ms 4.000 mean_width 174.6 ready_p50-ms 265.8 ready_p95-ms 74.09 residual_p50-ms 76.92 residual_p95-ms 33760 signatures/block 122785 signatures/s 0.01396 submit_p50-ms 0.01562 submit_p95-ms 7033605 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/tip_200ms_overlap-8 3 201098547 ns/op 1.493 avg_cpu_cores 126.0 components/block 300.2 cpu-ms/block 0.5184 feed_lag_p50-ms 0.9810 feed_lag_p95-ms 3.956 mean_width 1.117 ready_p50-ms 1.603 ready_p95-ms 1.053 residual_p50-ms 1.298 residual_p95-ms 33760 signatures/block 167878 signatures/s 0.001042 submit_p50-ms 0.002425 submit_p95-ms 7193832 B/op 68923 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/tip_200ms_overlap-8 3 200804503 ns/op 1.504 avg_cpu_cores 126.0 components/block 302.0 cpu-ms/block 0.4528 feed_lag_p50-ms 0.9812 feed_lag_p95-ms 3.956 mean_width 1.073 ready_p50-ms 1.596 ready_p95-ms 0.6861 residual_p50-ms 1.062 residual_p95-ms 33760 signatures/block 168124 signatures/s 0.001172 submit_p50-ms 0.003016 submit_p95-ms 7193242 B/op 68918 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/tip_200ms_overlap-8 3 201170435 ns/op 1.496 avg_cpu_cores 126.0 components/block 300.9 cpu-ms/block 0.5088 feed_lag_p50-ms 0.9289 feed_lag_p95-ms 3.956 mean_width 1.085 ready_p50-ms 1.534 ready_p95-ms 1.085 residual_p50-ms 1.335 residual_p95-ms 33760 signatures/block 167818 signatures/s 0.001072 submit_p50-ms 0.002414 submit_p95-ms 7193242 B/op 68918 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/catchup-8 3 39479657 ns/op 3.975 avg_cpu_cores 1.000 components/block 157.0 cpu-ms/block 0.0002710 feed_lag_p50-ms 0.0004810 feed_lag_p95-ms 8.000 mean_width 39.13 ready_p50-ms 41.02 ready_p95-ms 39.13 residual_p50-ms 41.02 residual_p95-ms 33760 signatures/block 855124 signatures/s 0.007464 submit_p50-ms 0.008786 submit_p95-ms 7023658 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/catchup-8 3 38341975 ns/op 4.014 avg_cpu_cores 1.000 components/block 153.9 cpu-ms/block 0.0005810 feed_lag_p50-ms 0.0006410 feed_lag_p95-ms 8.000 mean_width 38.31 ready_p50-ms 38.39 ready_p95-ms 38.31 residual_p50-ms 38.39 residual_p95-ms 33760 signatures/block 880497 signatures/s 0.004508 submit_p50-ms 0.004959 submit_p95-ms 7023666 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/catchup-8 3 39294804 ns/op 4.040 avg_cpu_cores 1.000 components/block 158.7 cpu-ms/block 0.0003910 feed_lag_p50-ms 0.0005510 feed_lag_p95-ms 8.000 mean_width 39.31 ready_p50-ms 39.43 ready_p95-ms 39.31 residual_p50-ms 39.43 residual_p95-ms 33760 signatures/block 859147 signatures/s 0.005029 submit_p50-ms 0.005861 submit_p95-ms 7023658 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/tip_200ms_after_complete-8 3 241004536 ns/op 0.6672 avg_cpu_cores 126.0 components/block 160.8 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 8.000 mean_width 141.0 ready_p50-ms 232.2 ready_p95-ms 40.60 residual_p50-ms 43.41 residual_p95-ms 33760 signatures/block 140080 signatures/s 0.01375 submit_p50-ms 0.01582 submit_p95-ms 7033853 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/tip_200ms_after_complete-8 3 240866782 ns/op 0.6463 avg_cpu_cores 126.0 components/block 155.7 cpu-ms/block 100.2 feed_lag_p50-ms 191.4 feed_lag_p95-ms 8.000 mean_width 140.6 ready_p50-ms 231.8 ready_p95-ms 41.37 residual_p50-ms 41.44 residual_p95-ms 33760 signatures/block 140160 signatures/s 0.01499 submit_p50-ms 0.01555 submit_p95-ms 7033853 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/tip_200ms_after_complete-8 3 245990041 ns/op 0.7043 avg_cpu_cores 126.0 components/block 173.2 cpu-ms/block 99.92 feed_lag_p50-ms 191.1 feed_lag_p95-ms 8.000 mean_width 145.8 ready_p50-ms 237.0 ready_p95-ms 46.04 residual_p50-ms 51.40 residual_p95-ms 33760 signatures/block 137241 signatures/s 0.01647 submit_p50-ms 0.01882 submit_p95-ms 7033853 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/tip_200ms_overlap-8 3 200623083 ns/op 0.9263 avg_cpu_cores 126.0 components/block 185.8 cpu-ms/block 0.5196 feed_lag_p50-ms 1.002 feed_lag_p95-ms 7.912 mean_width 0.9626 ready_p50-ms 1.463 ready_p95-ms 0.6115 residual_p50-ms 0.9216 residual_p95-ms 33760 signatures/block 168276 signatures/s 0.001794 submit_p50-ms 0.004097 submit_p95-ms 7226498 B/op 68927 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/tip_200ms_overlap-8 3 200897397 ns/op 0.8262 avg_cpu_cores 126.0 components/block 166.0 cpu-ms/block 0.5245 feed_lag_p50-ms 1.013 feed_lag_p95-ms 7.912 mean_width 0.9103 ready_p50-ms 1.390 ready_p95-ms 1.083 residual_p50-ms 1.198 residual_p95-ms 33760 signatures/block 168046 signatures/s 0.001183 submit_p50-ms 0.003416 submit_p95-ms 7225789 B/op 68921 allocs/op +BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/tip_200ms_overlap-8 3 200745122 ns/op 0.8122 avg_cpu_cores 126.0 components/block 163.0 cpu-ms/block 0.5160 feed_lag_p50-ms 0.9821 feed_lag_p95-ms 7.912 mean_width 0.8819 ready_p50-ms 1.329 ready_p95-ms 0.6724 residual_p50-ms 1.132 residual_p95-ms 33760 signatures/block 168173 signatures/s 0.001112 submit_p50-ms 0.002685 submit_p95-ms 7225640 B/op 68919 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/catchup-8 3 143957884 ns/op 2.052 avg_cpu_cores 1.000 components/block 295.4 cpu-ms/block 0.0002410 feed_lag_p50-ms 0.0004910 feed_lag_p95-ms 4.000 mean_width 144.0 ready_p50-ms 144.7 ready_p95-ms 144.0 residual_p50-ms 144.7 residual_p95-ms 33760 signatures/block 234513 signatures/s 0.005019 submit_p50-ms 0.006172 submit_p95-ms 7023133 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/catchup-8 3 155693549 ns/op 2.032 avg_cpu_cores 1.000 components/block 316.4 cpu-ms/block 0.0003000 feed_lag_p50-ms 0.0003110 feed_lag_p95-ms 4.000 mean_width 158.7 ready_p50-ms 162.0 ready_p95-ms 158.7 residual_p50-ms 162.0 residual_p95-ms 33760 signatures/block 216836 signatures/s 0.004819 submit_p50-ms 0.007064 submit_p95-ms 7023125 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/catchup-8 3 175713321 ns/op 2.044 avg_cpu_cores 1.000 components/block 359.1 cpu-ms/block 0.0002110 feed_lag_p50-ms 0.0004810 feed_lag_p95-ms 4.000 mean_width 175.6 ready_p50-ms 178.9 ready_p95-ms 175.6 residual_p50-ms 178.9 residual_p95-ms 33760 signatures/block 192131 signatures/s 0.005420 submit_p50-ms 0.006953 submit_p95-ms 7023141 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/tip_200ms_after_complete-8 3 346489723 ns/op 0.8637 avg_cpu_cores 126.0 components/block 299.3 cpu-ms/block 99.82 feed_lag_p50-ms 191.0 feed_lag_p95-ms 4.000 mean_width 246.5 ready_p50-ms 337.7 ready_p95-ms 145.9 residual_p50-ms 147.9 residual_p95-ms 33760 signatures/block 97434 signatures/s 0.01410 submit_p50-ms 0.02031 submit_p95-ms 7033333 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/tip_200ms_after_complete-8 3 345618207 ns/op 0.8597 avg_cpu_cores 126.0 components/block 297.1 cpu-ms/block 99.96 feed_lag_p50-ms 191.2 feed_lag_p95-ms 4.000 mean_width 245.1 ready_p50-ms 336.3 ready_p95-ms 145.9 residual_p50-ms 145.9 residual_p95-ms 33760 signatures/block 97680 signatures/s 0.01199 submit_p50-ms 0.01378 submit_p95-ms 7033333 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/tip_200ms_after_complete-8 3 371354060 ns/op 0.9267 avg_cpu_cores 126.0 components/block 344.1 cpu-ms/block 99.54 feed_lag_p50-ms 190.7 feed_lag_p95-ms 4.000 mean_width 271.4 ready_p50-ms 362.6 ready_p95-ms 172.2 residual_p50-ms 177.1 residual_p95-ms 33760 signatures/block 90911 signatures/s 0.01910 submit_p50-ms 0.02254 submit_p95-ms 7033333 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/tip_200ms_overlap-8 3 201052815 ns/op 1.483 avg_cpu_cores 126.0 components/block 298.1 cpu-ms/block 0.2236 feed_lag_p50-ms 0.8478 feed_lag_p95-ms 3.956 mean_width 1.585 ready_p50-ms 2.432 ready_p95-ms 0.9514 residual_p50-ms 1.353 residual_p95-ms 33760 signatures/block 167916 signatures/s 0.0009520 submit_p50-ms 0.002114 submit_p95-ms 7161128 B/op 68937 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/tip_200ms_overlap-8 3 200977968 ns/op 1.811 avg_cpu_cores 126.0 components/block 363.9 cpu-ms/block 0.02301 feed_lag_p50-ms 0.7957 feed_lag_p95-ms 3.956 mean_width 1.998 ready_p50-ms 3.094 ready_p95-ms 0.8770 residual_p50-ms 1.215 residual_p95-ms 33760 signatures/block 167979 signatures/s 0.002054 submit_p50-ms 0.004097 submit_p95-ms 7159746 B/op 68925 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/tip_200ms_overlap-8 3 201118531 ns/op 1.488 avg_cpu_cores 126.0 components/block 299.2 cpu-ms/block 0.2092 feed_lag_p50-ms 0.8745 feed_lag_p95-ms 3.956 mean_width 1.589 ready_p50-ms 2.484 ready_p95-ms 1.204 residual_p50-ms 1.456 residual_p95-ms 33760 signatures/block 167861 signatures/s 0.001001 submit_p50-ms 0.002295 submit_p95-ms 7160709 B/op 68934 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/catchup-8 3 79597279 ns/op 2.042 avg_cpu_cores 1.000 components/block 162.5 cpu-ms/block 0.0002600 feed_lag_p50-ms 0.0004710 feed_lag_p95-ms 8.000 mean_width 81.98 ready_p50-ms 82.10 ready_p95-ms 81.98 residual_p50-ms 82.10 residual_p95-ms 33760 signatures/block 424135 signatures/s 0.005170 submit_p50-ms 0.006362 submit_p95-ms 7023253 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/catchup-8 3 92160738 ns/op 2.042 avg_cpu_cores 1.000 components/block 188.1 cpu-ms/block 0.0005010 feed_lag_p50-ms 0.0005710 feed_lag_p95-ms 8.000 mean_width 91.84 ready_p50-ms 96.76 ready_p95-ms 91.84 residual_p50-ms 96.76 residual_p95-ms 33760 signatures/block 366317 signatures/s 0.007174 submit_p50-ms 0.008115 submit_p95-ms 7023266 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/catchup-8 3 77785688 ns/op 2.040 avg_cpu_cores 1.000 components/block 158.7 cpu-ms/block 0.0002110 feed_lag_p50-ms 0.0008720 feed_lag_p95-ms 8.000 mean_width 76.89 ready_p50-ms 81.28 ready_p95-ms 76.89 residual_p50-ms 81.28 residual_p95-ms 33760 signatures/block 434013 signatures/s 0.005009 submit_p50-ms 0.006552 submit_p95-ms 7023261 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/tip_200ms_after_complete-8 3 279921937 ns/op 0.5736 avg_cpu_cores 126.0 components/block 160.6 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 8.000 mean_width 179.3 ready_p50-ms 270.5 ready_p95-ms 76.93 residual_p50-ms 87.45 residual_p95-ms 33760 signatures/block 120605 signatures/s 0.01510 submit_p50-ms 0.01745 submit_p95-ms 7033453 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/tip_200ms_after_complete-8 3 280609110 ns/op 0.5790 avg_cpu_cores 126.0 components/block 162.5 cpu-ms/block 100.8 feed_lag_p50-ms 192.0 feed_lag_p95-ms 8.000 mean_width 180.0 ready_p50-ms 271.2 ready_p95-ms 80.28 residual_p50-ms 85.53 residual_p95-ms 33760 signatures/block 120310 signatures/s 0.01629 submit_p50-ms 0.05750 submit_p95-ms 7079253 B/op 67541 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/tip_200ms_after_complete-8 3 275007675 ns/op 0.5550 avg_cpu_cores 126.0 components/block 152.6 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 8.000 mean_width 174.7 ready_p50-ms 265.9 ready_p95-ms 75.43 residual_p50-ms 75.48 residual_p95-ms 33760 signatures/block 122760 signatures/s 0.01441 submit_p50-ms 0.01488 submit_p95-ms 7033474 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/tip_200ms_overlap-8 3 200553309 ns/op 0.8722 avg_cpu_cores 126.0 components/block 174.9 cpu-ms/block 0.4164 feed_lag_p50-ms 0.9441 feed_lag_p95-ms 7.912 mean_width 1.193 ready_p50-ms 1.708 ready_p95-ms 0.5627 residual_p50-ms 0.5720 residual_p95-ms 33760 signatures/block 168334 signatures/s 0.001743 submit_p50-ms 0.004909 submit_p95-ms 7175850 B/op 68920 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/tip_200ms_overlap-8 3 201115062 ns/op 0.8365 avg_cpu_cores 126.0 components/block 168.2 cpu-ms/block 0.4905 feed_lag_p50-ms 0.9819 feed_lag_p95-ms 7.912 mean_width 1.142 ready_p50-ms 1.804 ready_p95-ms 1.348 residual_p50-ms 1.353 residual_p95-ms 33760 signatures/block 167864 signatures/s 0.001302 submit_p50-ms 0.003086 submit_p95-ms 7175330 B/op 68920 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/tip_200ms_overlap-8 3 200875209 ns/op 0.9083 avg_cpu_cores 126.0 components/block 182.5 cpu-ms/block 0.4053 feed_lag_p50-ms 0.9401 feed_lag_p95-ms 7.912 mean_width 1.210 ready_p50-ms 1.734 ready_p95-ms 0.7018 residual_p50-ms 1.361 residual_p95-ms 33760 signatures/block 168065 signatures/s 0.001733 submit_p50-ms 0.004358 submit_p95-ms 7176061 B/op 68927 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/catchup-8 3 84397967 ns/op 3.886 avg_cpu_cores 1.000 components/block 328.0 cpu-ms/block 0.0002700 feed_lag_p50-ms 0.0006510 feed_lag_p95-ms 4.000 mean_width 80.13 ready_p50-ms 96.48 ready_p95-ms 80.13 residual_p50-ms 96.48 residual_p95-ms 33760 signatures/block 400010 signatures/s 0.004749 submit_p50-ms 0.008185 submit_p95-ms 7023397 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/catchup-8 3 84545510 ns/op 3.891 avg_cpu_cores 1.000 components/block 328.9 cpu-ms/block 0.0002200 feed_lag_p50-ms 0.0007220 feed_lag_p95-ms 4.000 mean_width 81.04 ready_p50-ms 94.42 ready_p95-ms 81.04 residual_p50-ms 94.42 residual_p95-ms 33760 signatures/block 399312 signatures/s 0.007054 submit_p50-ms 0.007925 submit_p95-ms 7023402 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/catchup-8 3 77376355 ns/op 3.992 avg_cpu_cores 1.000 components/block 308.9 cpu-ms/block 0.0002100 feed_lag_p50-ms 0.0006110 feed_lag_p95-ms 4.000 mean_width 76.57 ready_p50-ms 79.75 ready_p95-ms 76.57 residual_p50-ms 79.75 residual_p95-ms 33760 signatures/block 436309 signatures/s 0.01021 submit_p50-ms 0.01104 submit_p95-ms 7023402 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/tip_200ms_after_complete-8 3 276775942 ns/op 1.101 avg_cpu_cores 126.0 components/block 304.8 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 4.000 mean_width 176.7 ready_p50-ms 267.9 ready_p95-ms 76.69 residual_p50-ms 77.72 residual_p95-ms 33760 signatures/block 121976 signatures/s 0.01302 submit_p50-ms 0.01303 submit_p95-ms 7033605 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/tip_200ms_after_complete-8 3 286266781 ns/op 1.147 avg_cpu_cores 126.0 components/block 328.2 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 4.000 mean_width 186.0 ready_p50-ms 277.2 ready_p95-ms 88.39 residual_p50-ms 92.31 residual_p95-ms 33760 signatures/block 117932 signatures/s 0.01630 submit_p50-ms 0.03380 submit_p95-ms 7033597 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/tip_200ms_after_complete-8 3 280082230 ns/op 1.127 avg_cpu_cores 126.0 components/block 315.7 cpu-ms/block 100.1 feed_lag_p50-ms 191.3 feed_lag_p95-ms 4.000 mean_width 180.2 ready_p50-ms 271.4 ready_p95-ms 81.38 residual_p50-ms 82.65 residual_p95-ms 33760 signatures/block 120536 signatures/s 0.01659 submit_p50-ms 0.02034 submit_p95-ms 7033605 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/tip_200ms_overlap-8 3 200915755 ns/op 1.500 avg_cpu_cores 126.0 components/block 301.4 cpu-ms/block 0.5144 feed_lag_p50-ms 0.9691 feed_lag_p95-ms 3.956 mean_width 1.113 ready_p50-ms 1.588 ready_p95-ms 0.9998 residual_p50-ms 1.239 residual_p95-ms 33760 signatures/block 168031 signatures/s 0.001022 submit_p50-ms 0.002404 submit_p95-ms 7193234 B/op 68918 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/tip_200ms_overlap-8 3 200718712 ns/op 1.523 avg_cpu_cores 126.0 components/block 305.7 cpu-ms/block 0.4580 feed_lag_p50-ms 0.9962 feed_lag_p95-ms 3.956 mean_width 1.154 ready_p50-ms 1.675 ready_p95-ms 0.4901 residual_p50-ms 1.164 residual_p95-ms 33760 signatures/block 168196 signatures/s 0.001242 submit_p50-ms 0.002976 submit_p95-ms 7194797 B/op 68932 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/tip_200ms_overlap-8 3 200572819 ns/op 1.531 avg_cpu_cores 126.0 components/block 307.1 cpu-ms/block 0.4611 feed_lag_p50-ms 0.9844 feed_lag_p95-ms 3.956 mean_width 1.135 ready_p50-ms 1.701 ready_p95-ms 0.6289 residual_p50-ms 0.6701 residual_p95-ms 33760 signatures/block 168318 signatures/s 0.001202 submit_p50-ms 0.003256 submit_p95-ms 7193720 B/op 68922 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/catchup-8 3 39509312 ns/op 4.011 avg_cpu_cores 1.000 components/block 158.5 cpu-ms/block 0.0002500 feed_lag_p50-ms 0.0004110 feed_lag_p95-ms 8.000 mean_width 39.88 ready_p50-ms 40.18 ready_p95-ms 39.88 residual_p50-ms 40.18 residual_p95-ms 33760 signatures/block 854482 signatures/s 0.004759 submit_p50-ms 0.005010 submit_p95-ms 7023658 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/catchup-8 3 44691175 ns/op 3.832 avg_cpu_cores 1.000 components/block 171.2 cpu-ms/block 0.0005010 feed_lag_p50-ms 0.0007710 feed_lag_p95-ms 8.000 mean_width 42.16 ready_p50-ms 52.97 ready_p95-ms 42.16 residual_p50-ms 52.97 residual_p95-ms 33760 signatures/block 755406 signatures/s 0.005119 submit_p50-ms 0.008055 submit_p95-ms 7023653 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/catchup-8 3 44816763 ns/op 3.923 avg_cpu_cores 1.000 components/block 175.8 cpu-ms/block 0.0003010 feed_lag_p50-ms 0.0004810 feed_lag_p95-ms 8.000 mean_width 42.65 ready_p50-ms 51.93 ready_p95-ms 42.65 residual_p50-ms 51.93 residual_p95-ms 33760 signatures/block 753290 signatures/s 0.003908 submit_p50-ms 0.006643 submit_p95-ms 7023653 B/op 67538 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/tip_200ms_after_complete-8 3 242531854 ns/op 0.6854 avg_cpu_cores 126.0 components/block 166.2 cpu-ms/block 99.89 feed_lag_p50-ms 191.1 feed_lag_p95-ms 8.000 mean_width 142.2 ready_p50-ms 233.4 ready_p95-ms 41.44 residual_p50-ms 44.73 residual_p95-ms 33760 signatures/block 139198 signatures/s 0.01571 submit_p50-ms 0.01829 submit_p95-ms 7033861 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/tip_200ms_after_complete-8 3 239652146 ns/op 0.6493 avg_cpu_cores 126.0 components/block 155.6 cpu-ms/block 100.2 feed_lag_p50-ms 191.4 feed_lag_p95-ms 8.000 mean_width 139.2 ready_p50-ms 230.4 ready_p95-ms 39.49 residual_p50-ms 40.03 residual_p95-ms 33760 signatures/block 140871 signatures/s 0.01441 submit_p50-ms 0.01704 submit_p95-ms 7033861 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/tip_200ms_after_complete-8 3 240787126 ns/op 0.6555 avg_cpu_cores 126.0 components/block 157.8 cpu-ms/block 100.2 feed_lag_p50-ms 191.4 feed_lag_p95-ms 8.000 mean_width 140.6 ready_p50-ms 231.8 ready_p95-ms 40.74 residual_p50-ms 41.39 residual_p95-ms 33760 signatures/block 140207 signatures/s 0.01393 submit_p50-ms 0.01417 submit_p95-ms 7033861 B/op 67539 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/tip_200ms_overlap-8 3 200949184 ns/op 0.9006 avg_cpu_cores 126.0 components/block 181.0 cpu-ms/block 0.4993 feed_lag_p50-ms 0.9768 feed_lag_p95-ms 7.912 mean_width 0.9266 ready_p50-ms 1.401 ready_p95-ms 1.066 residual_p50-ms 1.141 residual_p95-ms 33760 signatures/block 168003 signatures/s 0.001683 submit_p50-ms 0.006322 submit_p95-ms 7225536 B/op 68919 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/tip_200ms_overlap-8 3 200864502 ns/op 0.8069 avg_cpu_cores 126.0 components/block 162.1 cpu-ms/block 0.5202 feed_lag_p50-ms 1.012 feed_lag_p95-ms 7.912 mean_width 0.8813 ready_p50-ms 1.380 ready_p95-ms 1.090 residual_p50-ms 1.206 residual_p95-ms 33760 signatures/block 168074 signatures/s 0.001102 submit_p50-ms 0.002535 submit_p95-ms 7227181 B/op 68933 allocs/op +BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/tip_200ms_overlap-8 3 200828651 ns/op 0.8102 avg_cpu_cores 126.0 components/block 162.7 cpu-ms/block 0.5170 feed_lag_p50-ms 1.016 feed_lag_p95-ms 7.912 mean_width 0.8773 ready_p50-ms 1.385 ready_p95-ms 0.9549 residual_p50-ms 1.219 residual_p95-ms 33760 signatures/block 168104 signatures/s 0.001112 submit_p50-ms 0.002545 submit_p95-ms 7226312 B/op 68925 allocs/op +PASS diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/execution-comparison.json b/docs/results/sigverify-streaming/2026-09-12-zen5/execution-comparison.json new file mode 100644 index 00000000..488e0481 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/execution-comparison.json @@ -0,0 +1,1653 @@ +{ + "description": "Compare actual transfer load/execute under separate-process sigverify load.\n\nThis does not benchmark complete block replay, the shared Go scheduler, account\ncommit, or the dependency planner. Both processes use the same explicitly chosen\neight physical CPU cores. The signature workload uses the production verifier\npool; 200 ms availability is simulated, not a captured network trace.\n", + "arguments": { + "turbine_bin": "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "replay_bin": "/srv/mithril-sigverify-streaming-20260912/bin/replay.test", + "fixtures": "/srv/mithril-planner-pr257-trial-20260912/fixtures", + "slot": 2622581, + "cpus": "0-7", + "out": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention", + "samples": 3, + "probe_time": "500ms", + "catchup_blocks": 96, + "tip_blocks": 20, + "warmup_ms": 400, + "backend": "r51", + "seed": 1, + "timeout": 120 + }, + "topology": [ + { + "cpu": 0, + "package": 0, + "core": 0 + }, + { + "cpu": 1, + "package": 0, + "core": 1 + }, + { + "cpu": 2, + "package": 0, + "core": 2 + }, + { + "cpu": 3, + "package": 0, + "core": 3 + }, + { + "cpu": 4, + "package": 0, + "core": 4 + }, + { + "cpu": 5, + "package": 0, + "core": 5 + }, + { + "cpu": 6, + "package": 0, + "core": 6 + }, + { + "cpu": 7, + "package": 0, + "core": 7 + } + ], + "gomaxprocs_each_process": 8, + "execution_probe_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/replay.test", + "-test.run=^$", + "-test.bench=^BenchmarkLoadAndExecuteTransferResultMode$/^lean$", + "-test.benchtime=500ms", + "-test.count=1", + "-test.timeout=120s" + ], + "runs": [ + { + "sample": 1, + "workers": 2, + "target": 8, + "mode": "tip_200ms_overlap", + "baseline": { + "wall_seconds": 0.6645082151517272, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 167744, + "ns/op": 3471.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-tip_200ms_overlap-workers2-target8/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6648174938745797, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 171600, + "ns/op": 3530.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-tip_200ms_overlap-workers2-target8/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.0169979832901181, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 20, + "ns/op": 200924240.0, + "avg_cpu_cores": 0.8916, + "components/block": 126.0, + "cpu-ms/block": 179.2, + "feed_lag_p50-ms": 0.4462, + "feed_lag_p95-ms": 0.9733, + "mean_width": 7.912, + "ready_p50-ms": 1.19, + "ready_p95-ms": 1.808, + "residual_p50-ms": 0.9351, + "residual_p95-ms": 1.356, + "signatures/block": 33760.0, + "signatures/s": 168024.0, + "submit_p50-ms": 0.001734, + "submit_p95-ms": 0.004288, + "B/op": 7208732.0, + "allocs/op": 68935.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_2$/^target_8$/^tip_200ms_overlap$", + "-test.benchtime=20x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-tip_200ms_overlap-workers2-target8/sigverify-load.log" + }, + { + "sample": 1, + "workers": 4, + "target": 8, + "mode": "catchup", + "baseline": { + "wall_seconds": 0.6647921879775822, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 170856, + "ns/op": 3547.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-catchup-workers4-target8/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6148647838272154, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 139512, + "ns/op": 4060.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-catchup-workers4-target8/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.1446292641669016, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/catchup-8", + "iterations": 96, + "ns/op": 40210294.0, + "avg_cpu_cores": 3.993, + "components/block": 1.0, + "cpu-ms/block": 160.6, + "feed_lag_p50-ms": 0.000211, + "feed_lag_p95-ms": 0.00043, + "mean_width": 8.0, + "ready_p50-ms": 39.57, + "ready_p95-ms": 43.68, + "residual_p50-ms": 39.57, + "residual_p95-ms": 43.68, + "signatures/block": 33760.0, + "signatures/s": 839586.0, + "submit_p50-ms": 0.003306, + "submit_p95-ms": 0.005891, + "B/op": 7058192.0, + "allocs/op": 67537.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_4$/^target_8$/^catchup$", + "-test.benchtime=96x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-catchup-workers4-target8/sigverify-load.log" + }, + { + "sample": 1, + "workers": 2, + "target": 4, + "mode": "tip_200ms_overlap", + "baseline": { + "wall_seconds": 0.6646570740267634, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 172561, + "ns/op": 3462.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-tip_200ms_overlap-workers2-target4/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6148028671741486, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 156918, + "ns/op": 3704.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-tip_200ms_overlap-workers2-target4/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.069901790872328, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 20, + "ns/op": 201168852.0, + "avg_cpu_cores": 1.563, + "components/block": 126.0, + "cpu-ms/block": 314.3, + "feed_lag_p50-ms": 0.04094, + "feed_lag_p95-ms": 0.8651, + "mean_width": 3.956, + "ready_p50-ms": 1.616, + "ready_p95-ms": 2.755, + "residual_p50-ms": 1.196, + "residual_p95-ms": 1.497, + "signatures/block": 33760.0, + "signatures/s": 167819.0, + "submit_p50-ms": 0.001432, + "submit_p95-ms": 0.004018, + "B/op": 7234818.0, + "allocs/op": 68941.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_2$/^target_4$/^tip_200ms_overlap$", + "-test.benchtime=20x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-tip_200ms_overlap-workers2-target4/sigverify-load.log" + }, + { + "sample": 1, + "workers": 4, + "target": 4, + "mode": "tip_200ms_overlap", + "baseline": { + "wall_seconds": 0.6144880480132997, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 165466, + "ns/op": 3489.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-tip_200ms_overlap-workers4-target4/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.7155657866969705, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 155095, + "ns/op": 4302.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-tip_200ms_overlap-workers4-target4/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.233018056749785, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 20, + "ns/op": 201022700.0, + "avg_cpu_cores": 1.651, + "components/block": 126.0, + "cpu-ms/block": 332.0, + "feed_lag_p50-ms": 0.4337, + "feed_lag_p95-ms": 0.9876, + "mean_width": 3.956, + "ready_p50-ms": 1.206, + "ready_p95-ms": 1.982, + "residual_p50-ms": 1.099, + "residual_p95-ms": 1.257, + "signatures/block": 33760.0, + "signatures/s": 167941.0, + "submit_p50-ms": 0.001674, + "submit_p95-ms": 0.004459, + "B/op": 7241454.0, + "allocs/op": 68940.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_4$/^target_4$/^tip_200ms_overlap$", + "-test.benchtime=20x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-tip_200ms_overlap-workers4-target4/sigverify-load.log" + }, + { + "sample": 1, + "workers": 4, + "target": 8, + "mode": "tip_200ms_overlap", + "baseline": { + "wall_seconds": 0.6647598869167268, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 165728, + "ns/op": 3596.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-tip_200ms_overlap-workers4-target8/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6647413833998144, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 167448, + "ns/op": 3564.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-tip_200ms_overlap-workers4-target8/execution-concurrent.log" + }, + "execution_slowdown_ratio": 0.9911012235817576, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 20, + "ns/op": 200934390.0, + "avg_cpu_cores": 0.8582, + "components/block": 126.0, + "cpu-ms/block": 172.4, + "feed_lag_p50-ms": 0.517, + "feed_lag_p95-ms": 1.006, + "mean_width": 7.912, + "ready_p50-ms": 0.9062, + "ready_p95-ms": 1.396, + "residual_p50-ms": 1.041, + "residual_p95-ms": 1.256, + "signatures/block": 33760.0, + "signatures/s": 168015.0, + "submit_p50-ms": 0.001383, + "submit_p95-ms": 0.003878, + "B/op": 7274370.0, + "allocs/op": 68941.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_4$/^target_8$/^tip_200ms_overlap$", + "-test.benchtime=20x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-tip_200ms_overlap-workers4-target8/sigverify-load.log" + }, + { + "sample": 1, + "workers": 2, + "target": 4, + "mode": "catchup", + "baseline": { + "wall_seconds": 0.6649067718535662, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 149238, + "ns/op": 4008.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-catchup-workers2-target4/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6675727372057736, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 146756, + "ns/op": 4085.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-catchup-workers2-target4/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.0192115768463075, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/catchup-8", + "iterations": 96, + "ns/op": 153189266.0, + "avg_cpu_cores": 2.048, + "components/block": 1.0, + "cpu-ms/block": 313.8, + "feed_lag_p50-ms": 0.000211, + "feed_lag_p95-ms": 0.000481, + "mean_width": 4.0, + "ready_p50-ms": 149.6, + "ready_p95-ms": 174.2, + "residual_p50-ms": 149.6, + "residual_p95-ms": 174.2, + "signatures/block": 33760.0, + "signatures/s": 220381.0, + "submit_p50-ms": 0.003657, + "submit_p95-ms": 0.01091, + "B/op": 7054772.0, + "allocs/op": 67536.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_2$/^target_4$/^catchup$", + "-test.benchtime=96x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-catchup-workers2-target4/sigverify-load.log" + }, + { + "sample": 1, + "workers": 4, + "target": 4, + "mode": "catchup", + "baseline": { + "wall_seconds": 0.7147685927338898, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 164997, + "ns/op": 3958.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-catchup-workers4-target4/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6648671478033066, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 152398, + "ns/op": 4006.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-catchup-workers4-target4/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.0121273370389086, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/catchup-8", + "iterations": 96, + "ns/op": 79167070.0, + "avg_cpu_cores": 3.991, + "components/block": 1.0, + "cpu-ms/block": 316.0, + "feed_lag_p50-ms": 0.00023, + "feed_lag_p95-ms": 0.00037, + "mean_width": 4.0, + "ready_p50-ms": 76.56, + "ready_p95-ms": 96.7, + "residual_p50-ms": 76.56, + "residual_p95-ms": 96.7, + "signatures/block": 33760.0, + "signatures/s": 426440.0, + "submit_p50-ms": 0.003706, + "submit_p95-ms": 0.011, + "B/op": 7060780.0, + "allocs/op": 67537.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_4$/^target_4$/^catchup$", + "-test.benchtime=96x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-catchup-workers4-target4/sigverify-load.log" + }, + { + "sample": 1, + "workers": 2, + "target": 8, + "mode": "catchup", + "baseline": { + "wall_seconds": 0.6647211848758161, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 174750, + "ns/op": 3542.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-catchup-workers2-target8/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.7656238148920238, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 159576, + "ns/op": 4432.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-catchup-workers2-target8/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.251270468661773, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/catchup-8", + "iterations": 96, + "ns/op": 78756306.0, + "avg_cpu_cores": 2.051, + "components/block": 1.0, + "cpu-ms/block": 161.6, + "feed_lag_p50-ms": 0.00022, + "feed_lag_p95-ms": 0.000491, + "mean_width": 8.0, + "ready_p50-ms": 75.68, + "ready_p95-ms": 90.65, + "residual_p50-ms": 75.68, + "residual_p95-ms": 90.65, + "signatures/block": 33760.0, + "signatures/s": 428664.0, + "submit_p50-ms": 0.003326, + "submit_p95-ms": 0.01184, + "B/op": 7060612.0, + "allocs/op": 67536.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_2$/^target_8$/^catchup$", + "-test.benchtime=96x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-1-catchup-workers2-target8/sigverify-load.log" + }, + { + "sample": 2, + "workers": 2, + "target": 8, + "mode": "catchup", + "baseline": { + "wall_seconds": 0.6144605274312198, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 168331, + "ns/op": 3448.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-catchup-workers2-target8/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6650009588338435, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 142396, + "ns/op": 4201.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-catchup-workers2-target8/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.2183874709976799, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/catchup-8", + "iterations": 96, + "ns/op": 77871825.0, + "avg_cpu_cores": 2.052, + "components/block": 1.0, + "cpu-ms/block": 159.8, + "feed_lag_p50-ms": 0.0002, + "feed_lag_p95-ms": 0.00038, + "mean_width": 8.0, + "ready_p50-ms": 76.13, + "ready_p95-ms": 88.31, + "residual_p50-ms": 76.13, + "residual_p95-ms": 88.31, + "signatures/block": 33760.0, + "signatures/s": 433533.0, + "submit_p50-ms": 0.003436, + "submit_p95-ms": 0.007464, + "B/op": 7056320.0, + "allocs/op": 67536.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_2$/^target_8$/^catchup$", + "-test.benchtime=96x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-catchup-workers2-target8/sigverify-load.log" + }, + { + "sample": 2, + "workers": 4, + "target": 8, + "mode": "catchup", + "baseline": { + "wall_seconds": 0.6145014832727611, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 168381, + "ns/op": 3462.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-catchup-workers4-target8/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.7652924442663789, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 141712, + "ns/op": 4887.0, + "B/op": 4145.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-catchup-workers4-target8/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.4116117850953207, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/catchup-8", + "iterations": 96, + "ns/op": 42898713.0, + "avg_cpu_cores": 3.925, + "components/block": 1.0, + "cpu-ms/block": 168.4, + "feed_lag_p50-ms": 0.00023, + "feed_lag_p95-ms": 0.000471, + "mean_width": 8.0, + "ready_p50-ms": 41.07, + "ready_p95-ms": 54.05, + "residual_p50-ms": 41.07, + "residual_p95-ms": 54.05, + "signatures/block": 33760.0, + "signatures/s": 786970.0, + "submit_p50-ms": 0.003397, + "submit_p95-ms": 0.01086, + "B/op": 7053910.0, + "allocs/op": 67536.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_4$/^target_8$/^catchup$", + "-test.benchtime=96x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-catchup-workers4-target8/sigverify-load.log" + }, + { + "sample": 2, + "workers": 4, + "target": 4, + "mode": "catchup", + "baseline": { + "wall_seconds": 0.7149186637252569, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 155592, + "ns/op": 4248.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-catchup-workers4-target4/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6148898312821984, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 134372, + "ns/op": 4182.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-catchup-workers4-target4/execution-concurrent.log" + }, + "execution_slowdown_ratio": 0.9844632768361582, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/catchup-8", + "iterations": 96, + "ns/op": 79047165.0, + "avg_cpu_cores": 3.999, + "components/block": 1.0, + "cpu-ms/block": 316.1, + "feed_lag_p50-ms": 0.0002, + "feed_lag_p95-ms": 0.000291, + "mean_width": 4.0, + "ready_p50-ms": 76.57, + "ready_p95-ms": 94.24, + "residual_p50-ms": 76.57, + "residual_p95-ms": 94.24, + "signatures/block": 33760.0, + "signatures/s": 427087.0, + "submit_p50-ms": 0.003747, + "submit_p95-ms": 0.01152, + "B/op": 7056514.0, + "allocs/op": 67537.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_4$/^target_4$/^catchup$", + "-test.benchtime=96x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-catchup-workers4-target4/sigverify-load.log" + }, + { + "sample": 2, + "workers": 2, + "target": 4, + "mode": "catchup", + "baseline": { + "wall_seconds": 0.6647634031251073, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 170058, + "ns/op": 3624.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-catchup-workers2-target4/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.7149660927243531, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 160458, + "ns/op": 4021.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-catchup-workers2-target4/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.1095474613686533, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/catchup-8", + "iterations": 96, + "ns/op": 151919970.0, + "avg_cpu_cores": 2.051, + "components/block": 1.0, + "cpu-ms/block": 311.5, + "feed_lag_p50-ms": 0.00022, + "feed_lag_p95-ms": 0.000461, + "mean_width": 4.0, + "ready_p50-ms": 147.5, + "ready_p95-ms": 175.5, + "residual_p50-ms": 147.5, + "residual_p95-ms": 175.5, + "signatures/block": 33760.0, + "signatures/s": 222222.0, + "submit_p50-ms": 0.003677, + "submit_p95-ms": 0.007574, + "B/op": 7061922.0, + "allocs/op": 67537.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_2$/^target_4$/^catchup$", + "-test.benchtime=96x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-catchup-workers2-target4/sigverify-load.log" + }, + { + "sample": 2, + "workers": 2, + "target": 4, + "mode": "tip_200ms_overlap", + "baseline": { + "wall_seconds": 0.6146299438551068, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 166706, + "ns/op": 3502.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-tip_200ms_overlap-workers2-target4/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.5672630779445171, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 107145, + "ns/op": 4799.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-tip_200ms_overlap-workers2-target4/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.3703597944031982, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 20, + "ns/op": 201262863.0, + "avg_cpu_cores": 1.606, + "components/block": 126.0, + "cpu-ms/block": 323.2, + "feed_lag_p50-ms": 0.03106, + "feed_lag_p95-ms": 0.8493, + "mean_width": 3.956, + "ready_p50-ms": 1.723, + "ready_p95-ms": 2.871, + "residual_p50-ms": 1.114, + "residual_p95-ms": 1.802, + "signatures/block": 33760.0, + "signatures/s": 167741.0, + "submit_p50-ms": 0.001453, + "submit_p95-ms": 0.004058, + "B/op": 7212730.0, + "allocs/op": 68935.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_2$/^target_4$/^tip_200ms_overlap$", + "-test.benchtime=20x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-tip_200ms_overlap-workers2-target4/sigverify-load.log" + }, + { + "sample": 2, + "workers": 2, + "target": 8, + "mode": "tip_200ms_overlap", + "baseline": { + "wall_seconds": 0.7147977370768785, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 174418, + "ns/op": 3727.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-tip_200ms_overlap-workers2-target8/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6647711875848472, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 167622, + "ns/op": 3536.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-tip_200ms_overlap-workers2-target8/execution-concurrent.log" + }, + "execution_slowdown_ratio": 0.948752347732761, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 20, + "ns/op": 200967117.0, + "avg_cpu_cores": 0.8453, + "components/block": 126.0, + "cpu-ms/block": 169.9, + "feed_lag_p50-ms": 0.4835, + "feed_lag_p95-ms": 0.9802, + "mean_width": 7.912, + "ready_p50-ms": 1.166, + "ready_p95-ms": 1.747, + "residual_p50-ms": 0.9879, + "residual_p95-ms": 1.377, + "signatures/block": 33760.0, + "signatures/s": 167988.0, + "submit_p50-ms": 0.001313, + "submit_p95-ms": 0.003486, + "B/op": 7222552.0, + "allocs/op": 68937.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_2$/^target_8$/^tip_200ms_overlap$", + "-test.benchtime=20x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-tip_200ms_overlap-workers2-target8/sigverify-load.log" + }, + { + "sample": 2, + "workers": 4, + "target": 4, + "mode": "tip_200ms_overlap", + "baseline": { + "wall_seconds": 0.6646457533352077, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 174780, + "ns/op": 3601.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-tip_200ms_overlap-workers4-target4/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6648325030691922, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 162474, + "ns/op": 3740.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-tip_200ms_overlap-workers4-target4/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.0386003887808941, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 20, + "ns/op": 200928113.0, + "avg_cpu_cores": 1.526, + "components/block": 126.0, + "cpu-ms/block": 306.7, + "feed_lag_p50-ms": 0.476, + "feed_lag_p95-ms": 0.9896, + "mean_width": 3.956, + "ready_p50-ms": 1.136, + "ready_p95-ms": 1.672, + "residual_p50-ms": 1.002, + "residual_p95-ms": 1.263, + "signatures/block": 33760.0, + "signatures/s": 168020.0, + "submit_p50-ms": 0.001252, + "submit_p95-ms": 0.003527, + "B/op": 7256845.0, + "allocs/op": 68947.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_4$/^target_4$/^tip_200ms_overlap$", + "-test.benchtime=20x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-tip_200ms_overlap-workers4-target4/sigverify-load.log" + }, + { + "sample": 2, + "workers": 4, + "target": 8, + "mode": "tip_200ms_overlap", + "baseline": { + "wall_seconds": 0.6147988210432231, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 137796, + "ns/op": 3945.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-tip_200ms_overlap-workers4-target8/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6647517010569572, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 171933, + "ns/op": 3546.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-tip_200ms_overlap-workers4-target8/execution-concurrent.log" + }, + "execution_slowdown_ratio": 0.8988593155893536, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 20, + "ns/op": 200756778.0, + "avg_cpu_cores": 0.8979, + "components/block": 126.0, + "cpu-ms/block": 180.3, + "feed_lag_p50-ms": 0.5109, + "feed_lag_p95-ms": 0.9931, + "mean_width": 7.912, + "ready_p50-ms": 0.9308, + "ready_p95-ms": 1.413, + "residual_p50-ms": 0.7481, + "residual_p95-ms": 1.147, + "signatures/block": 33760.0, + "signatures/s": 168164.0, + "submit_p50-ms": 0.001683, + "submit_p95-ms": 0.004809, + "B/op": 7287646.0, + "allocs/op": 68938.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_4$/^target_8$/^tip_200ms_overlap$", + "-test.benchtime=20x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-2-tip_200ms_overlap-workers4-target8/sigverify-load.log" + }, + { + "sample": 3, + "workers": 2, + "target": 8, + "mode": "catchup", + "baseline": { + "wall_seconds": 0.6647746949456632, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 164358, + "ns/op": 3884.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-catchup-workers2-target8/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6149854301474988, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 126450, + "ns/op": 4148.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-catchup-workers2-target8/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.0679711637487126, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/catchup-8", + "iterations": 96, + "ns/op": 78410504.0, + "avg_cpu_cores": 2.05, + "components/block": 1.0, + "cpu-ms/block": 160.8, + "feed_lag_p50-ms": 0.00023, + "feed_lag_p95-ms": 0.000481, + "mean_width": 8.0, + "ready_p50-ms": 75.31, + "ready_p95-ms": 90.25, + "residual_p50-ms": 75.31, + "residual_p95-ms": 90.25, + "signatures/block": 33760.0, + "signatures/s": 430555.0, + "submit_p50-ms": 0.003577, + "submit_p95-ms": 0.007614, + "B/op": 7057749.0, + "allocs/op": 67536.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_2$/^target_8$/^catchup$", + "-test.benchtime=96x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-catchup-workers2-target8/sigverify-load.log" + }, + { + "sample": 3, + "workers": 4, + "target": 4, + "mode": "tip_200ms_overlap", + "baseline": { + "wall_seconds": 0.6145468889735639, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 167756, + "ns/op": 3486.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-tip_200ms_overlap-workers4-target4/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.665121624711901, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 141630, + "ns/op": 4231.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-tip_200ms_overlap-workers4-target4/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.2137119908204246, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 20, + "ns/op": 200751013.0, + "avg_cpu_cores": 1.678, + "components/block": 126.0, + "cpu-ms/block": 336.9, + "feed_lag_p50-ms": 0.4283, + "feed_lag_p95-ms": 0.9902, + "mean_width": 3.956, + "ready_p50-ms": 1.217, + "ready_p95-ms": 2.032, + "residual_p50-ms": 0.5827, + "residual_p95-ms": 1.286, + "signatures/block": 33760.0, + "signatures/s": 168169.0, + "submit_p50-ms": 0.001764, + "submit_p95-ms": 0.004468, + "B/op": 7235916.0, + "allocs/op": 68943.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_4$/^target_4$/^tip_200ms_overlap$", + "-test.benchtime=20x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-tip_200ms_overlap-workers4-target4/sigverify-load.log" + }, + { + "sample": 3, + "workers": 2, + "target": 4, + "mode": "tip_200ms_overlap", + "baseline": { + "wall_seconds": 0.5645706839859486, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 140451, + "ns/op": 3666.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-tip_200ms_overlap-workers2-target4/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6147960559464991, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 155277, + "ns/op": 3597.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-tip_200ms_overlap-workers2-target4/execution-concurrent.log" + }, + "execution_slowdown_ratio": 0.9811783960720131, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 20, + "ns/op": 201192091.0, + "avg_cpu_cores": 1.506, + "components/block": 126.0, + "cpu-ms/block": 303.0, + "feed_lag_p50-ms": 0.0687, + "feed_lag_p95-ms": 0.8534, + "mean_width": 3.956, + "ready_p50-ms": 1.591, + "ready_p95-ms": 2.54, + "residual_p50-ms": 1.178, + "residual_p95-ms": 1.465, + "signatures/block": 33760.0, + "signatures/s": 167800.0, + "submit_p50-ms": 0.001112, + "submit_p95-ms": 0.002705, + "B/op": 7200373.0, + "allocs/op": 68940.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_2$/^target_4$/^tip_200ms_overlap$", + "-test.benchtime=20x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-tip_200ms_overlap-workers2-target4/sigverify-load.log" + }, + { + "sample": 3, + "workers": 4, + "target": 8, + "mode": "tip_200ms_overlap", + "baseline": { + "wall_seconds": 0.6645939256995916, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 170604, + "ns/op": 3472.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-tip_200ms_overlap-workers4-target8/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.7150162868201733, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 159267, + "ns/op": 4093.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-tip_200ms_overlap-workers4-target8/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.1788594470046083, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 20, + "ns/op": 200971694.0, + "avg_cpu_cores": 0.8761, + "components/block": 126.0, + "cpu-ms/block": 176.1, + "feed_lag_p50-ms": 0.5208, + "feed_lag_p95-ms": 1.015, + "mean_width": 7.912, + "ready_p50-ms": 0.9286, + "ready_p95-ms": 1.43, + "residual_p50-ms": 1.097, + "residual_p95-ms": 1.294, + "signatures/block": 33760.0, + "signatures/s": 167984.0, + "submit_p50-ms": 0.001553, + "submit_p95-ms": 0.004518, + "B/op": 7272930.0, + "allocs/op": 68937.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_4$/^target_8$/^tip_200ms_overlap$", + "-test.benchtime=20x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-tip_200ms_overlap-workers4-target8/sigverify-load.log" + }, + { + "sample": 3, + "workers": 2, + "target": 4, + "mode": "catchup", + "baseline": { + "wall_seconds": 0.6646965383552015, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 166202, + "ns/op": 3817.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-catchup-workers2-target4/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6147506199777126, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 160532, + "ns/op": 3624.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-catchup-workers2-target4/execution-concurrent.log" + }, + "execution_slowdown_ratio": 0.9494367304165575, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/catchup-8", + "iterations": 96, + "ns/op": 147847296.0, + "avg_cpu_cores": 2.051, + "components/block": 1.0, + "cpu-ms/block": 303.2, + "feed_lag_p50-ms": 0.00023, + "feed_lag_p95-ms": 0.000491, + "mean_width": 4.0, + "ready_p50-ms": 145.3, + "ready_p95-ms": 159.8, + "residual_p50-ms": 145.3, + "residual_p95-ms": 159.8, + "signatures/block": 33760.0, + "signatures/s": 228344.0, + "submit_p50-ms": 0.003817, + "submit_p95-ms": 0.007323, + "B/op": 7057630.0, + "allocs/op": 67536.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_2$/^target_4$/^catchup$", + "-test.benchtime=96x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-catchup-workers2-target4/sigverify-load.log" + }, + { + "sample": 3, + "workers": 4, + "target": 4, + "mode": "catchup", + "baseline": { + "wall_seconds": 0.664639541413635, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 169953, + "ns/op": 3669.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-catchup-workers4-target4/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6653050077147782, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 145288, + "ns/op": 4207.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-catchup-workers4-target4/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.146633960207141, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/catchup-8", + "iterations": 96, + "ns/op": 80096206.0, + "avg_cpu_cores": 3.978, + "components/block": 1.0, + "cpu-ms/block": 318.6, + "feed_lag_p50-ms": 0.00023, + "feed_lag_p95-ms": 0.000691, + "mean_width": 4.0, + "ready_p50-ms": 77.65, + "ready_p95-ms": 92.49, + "residual_p50-ms": 77.65, + "residual_p95-ms": 92.49, + "signatures/block": 33760.0, + "signatures/s": 421493.0, + "submit_p50-ms": 0.004018, + "submit_p95-ms": 0.01255, + "B/op": 7057922.0, + "allocs/op": 67536.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_4$/^target_4$/^catchup$", + "-test.benchtime=96x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-catchup-workers4-target4/sigverify-load.log" + }, + { + "sample": 3, + "workers": 2, + "target": 8, + "mode": "tip_200ms_overlap", + "baseline": { + "wall_seconds": 0.614480503834784, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 168775, + "ns/op": 3465.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-tip_200ms_overlap-workers2-target8/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.6648324020206928, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 167258, + "ns/op": 3583.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-tip_200ms_overlap-workers2-target8/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.0340548340548341, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 20, + "ns/op": 200968000.0, + "avg_cpu_cores": 0.842, + "components/block": 126.0, + "cpu-ms/block": 169.2, + "feed_lag_p50-ms": 0.4334, + "feed_lag_p95-ms": 0.9712, + "mean_width": 7.912, + "ready_p50-ms": 1.178, + "ready_p95-ms": 1.715, + "residual_p50-ms": 1.039, + "residual_p95-ms": 1.357, + "signatures/block": 33760.0, + "signatures/s": 167987.0, + "submit_p50-ms": 0.001403, + "submit_p95-ms": 0.004248, + "B/op": 7250793.0, + "allocs/op": 68941.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_2$/^target_8$/^tip_200ms_overlap$", + "-test.benchtime=20x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-tip_200ms_overlap-workers2-target8/sigverify-load.log" + }, + { + "sample": 3, + "workers": 4, + "target": 8, + "mode": "catchup", + "baseline": { + "wall_seconds": 0.6646514940075576, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 167019, + "ns/op": 3809.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-catchup-workers4-target8/execution-baseline.log" + }, + "concurrent": { + "wall_seconds": 0.7654618811793625, + "benchmark": { + "name": "BenchmarkLoadAndExecuteTransferResultMode/lean-8", + "iterations": 148914, + "ns/op": 4629.0, + "B/op": 4146.0, + "allocs/op": 59.0 + }, + "log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-catchup-workers4-target8/execution-concurrent.log" + }, + "execution_slowdown_ratio": 1.2152796009451299, + "sigverify": { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/catchup-8", + "iterations": 96, + "ns/op": 44540126.0, + "avg_cpu_cores": 3.899, + "components/block": 1.0, + "cpu-ms/block": 173.7, + "feed_lag_p50-ms": 0.00024, + "feed_lag_p95-ms": 0.00033, + "mean_width": 8.0, + "ready_p50-ms": 42.49, + "ready_p95-ms": 55.03, + "residual_p50-ms": 42.49, + "residual_p95-ms": 55.03, + "signatures/block": 33760.0, + "signatures/s": 757968.0, + "submit_p50-ms": 0.003727, + "submit_p95-ms": 0.01137, + "B/op": 7053895.0, + "allocs/op": 67536.0 + }, + "sigverify_command": [ + "taskset", + "--cpu-list", + "0-7", + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", + "-test.run=^$", + "-test.bench=^BenchmarkTransactionVerificationFlow$/^captured_2622581$/^workers_4$/^target_8$/^catchup$", + "-test.benchtime=96x", + "-test.count=1", + "-test.timeout=120s" + ], + "sigverify_log": "/srv/mithril-sigverify-streaming-20260912/results/execution-contention/sample-3-catchup-workers4-target8/sigverify-load.log" + } + ], + "summary": [ + { + "workers": 2, + "target": 4, + "mode": "catchup", + "samples": 3, + "median_execution_slowdown_ratio": 1.0192115768463075, + "median_execution_baseline_ns": 3817.0, + "median_execution_concurrent_ns": 4021.0, + "median_sigverify_residual_p95_ms": 174.2, + "median_sigverify_cpu_ms_per_block": 311.5 + }, + { + "workers": 2, + "target": 4, + "mode": "tip_200ms_overlap", + "samples": 3, + "median_execution_slowdown_ratio": 1.069901790872328, + "median_execution_baseline_ns": 3502.0, + "median_execution_concurrent_ns": 3704.0, + "median_sigverify_residual_p95_ms": 1.497, + "median_sigverify_cpu_ms_per_block": 314.3 + }, + { + "workers": 2, + "target": 8, + "mode": "catchup", + "samples": 3, + "median_execution_slowdown_ratio": 1.2183874709976799, + "median_execution_baseline_ns": 3542.0, + "median_execution_concurrent_ns": 4201.0, + "median_sigverify_residual_p95_ms": 90.25, + "median_sigverify_cpu_ms_per_block": 160.8 + }, + { + "workers": 2, + "target": 8, + "mode": "tip_200ms_overlap", + "samples": 3, + "median_execution_slowdown_ratio": 1.0169979832901181, + "median_execution_baseline_ns": 3471.0, + "median_execution_concurrent_ns": 3536.0, + "median_sigverify_residual_p95_ms": 1.357, + "median_sigverify_cpu_ms_per_block": 169.9 + }, + { + "workers": 4, + "target": 4, + "mode": "catchup", + "samples": 3, + "median_execution_slowdown_ratio": 1.0121273370389086, + "median_execution_baseline_ns": 3958.0, + "median_execution_concurrent_ns": 4182.0, + "median_sigverify_residual_p95_ms": 94.24, + "median_sigverify_cpu_ms_per_block": 316.1 + }, + { + "workers": 4, + "target": 4, + "mode": "tip_200ms_overlap", + "samples": 3, + "median_execution_slowdown_ratio": 1.2137119908204246, + "median_execution_baseline_ns": 3489.0, + "median_execution_concurrent_ns": 4231.0, + "median_sigverify_residual_p95_ms": 1.263, + "median_sigverify_cpu_ms_per_block": 332.0 + }, + { + "workers": 4, + "target": 8, + "mode": "catchup", + "samples": 3, + "median_execution_slowdown_ratio": 1.2152796009451299, + "median_execution_baseline_ns": 3547.0, + "median_execution_concurrent_ns": 4629.0, + "median_sigverify_residual_p95_ms": 54.05, + "median_sigverify_cpu_ms_per_block": 168.4 + }, + { + "workers": 4, + "target": 8, + "mode": "tip_200ms_overlap", + "samples": 3, + "median_execution_slowdown_ratio": 0.9911012235817576, + "median_execution_baseline_ns": 3596.0, + "median_execution_concurrent_ns": 3564.0, + "median_sigverify_residual_p95_ms": 1.256, + "median_sigverify_cpu_ms_per_block": 176.1 + } + ] +} diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/execution-contention.log b/docs/results/sigverify-streaming/2026-09-12-zen5/execution-contention.log new file mode 100644 index 00000000..7f2cd0ca --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/execution-contention.log @@ -0,0 +1,162 @@ +sample-1-tip_200ms_overlap-workers2-target8: baseline execution +sample-1-tip_200ms_overlap-workers2-target8: concurrent execution +sample-1-tip_200ms_overlap-workers2-target8: execution ratio 1.017x +sample-1-catchup-workers4-target8: baseline execution +sample-1-catchup-workers4-target8: concurrent execution +sample-1-catchup-workers4-target8: execution ratio 1.145x +sample-1-tip_200ms_overlap-workers2-target4: baseline execution +sample-1-tip_200ms_overlap-workers2-target4: concurrent execution +sample-1-tip_200ms_overlap-workers2-target4: execution ratio 1.070x +sample-1-tip_200ms_overlap-workers4-target4: baseline execution +sample-1-tip_200ms_overlap-workers4-target4: concurrent execution +sample-1-tip_200ms_overlap-workers4-target4: execution ratio 1.233x +sample-1-tip_200ms_overlap-workers4-target8: baseline execution +sample-1-tip_200ms_overlap-workers4-target8: concurrent execution +sample-1-tip_200ms_overlap-workers4-target8: execution ratio 0.991x +sample-1-catchup-workers2-target4: baseline execution +sample-1-catchup-workers2-target4: concurrent execution +sample-1-catchup-workers2-target4: execution ratio 1.019x +sample-1-catchup-workers4-target4: baseline execution +sample-1-catchup-workers4-target4: concurrent execution +sample-1-catchup-workers4-target4: execution ratio 1.012x +sample-1-catchup-workers2-target8: baseline execution +sample-1-catchup-workers2-target8: concurrent execution +sample-1-catchup-workers2-target8: execution ratio 1.251x +sample-2-catchup-workers2-target8: baseline execution +sample-2-catchup-workers2-target8: concurrent execution +sample-2-catchup-workers2-target8: execution ratio 1.218x +sample-2-catchup-workers4-target8: baseline execution +sample-2-catchup-workers4-target8: concurrent execution +sample-2-catchup-workers4-target8: execution ratio 1.412x +sample-2-catchup-workers4-target4: baseline execution +sample-2-catchup-workers4-target4: concurrent execution +sample-2-catchup-workers4-target4: execution ratio 0.984x +sample-2-catchup-workers2-target4: baseline execution +sample-2-catchup-workers2-target4: concurrent execution +sample-2-catchup-workers2-target4: execution ratio 1.110x +sample-2-tip_200ms_overlap-workers2-target4: baseline execution +sample-2-tip_200ms_overlap-workers2-target4: concurrent execution +sample-2-tip_200ms_overlap-workers2-target4: execution ratio 1.370x +sample-2-tip_200ms_overlap-workers2-target8: baseline execution +sample-2-tip_200ms_overlap-workers2-target8: concurrent execution +sample-2-tip_200ms_overlap-workers2-target8: execution ratio 0.949x +sample-2-tip_200ms_overlap-workers4-target4: baseline execution +sample-2-tip_200ms_overlap-workers4-target4: concurrent execution +sample-2-tip_200ms_overlap-workers4-target4: execution ratio 1.039x +sample-2-tip_200ms_overlap-workers4-target8: baseline execution +sample-2-tip_200ms_overlap-workers4-target8: concurrent execution +sample-2-tip_200ms_overlap-workers4-target8: execution ratio 0.899x +sample-3-catchup-workers2-target8: baseline execution +sample-3-catchup-workers2-target8: concurrent execution +sample-3-catchup-workers2-target8: execution ratio 1.068x +sample-3-tip_200ms_overlap-workers4-target4: baseline execution +sample-3-tip_200ms_overlap-workers4-target4: concurrent execution +sample-3-tip_200ms_overlap-workers4-target4: execution ratio 1.214x +sample-3-tip_200ms_overlap-workers2-target4: baseline execution +sample-3-tip_200ms_overlap-workers2-target4: concurrent execution +sample-3-tip_200ms_overlap-workers2-target4: execution ratio 0.981x +sample-3-tip_200ms_overlap-workers4-target8: baseline execution +sample-3-tip_200ms_overlap-workers4-target8: concurrent execution +sample-3-tip_200ms_overlap-workers4-target8: execution ratio 1.179x +sample-3-catchup-workers2-target4: baseline execution +sample-3-catchup-workers2-target4: concurrent execution +sample-3-catchup-workers2-target4: execution ratio 0.949x +sample-3-catchup-workers4-target4: baseline execution +sample-3-catchup-workers4-target4: concurrent execution +sample-3-catchup-workers4-target4: execution ratio 1.147x +sample-3-tip_200ms_overlap-workers2-target8: baseline execution +sample-3-tip_200ms_overlap-workers2-target8: concurrent execution +sample-3-tip_200ms_overlap-workers2-target8: execution ratio 1.034x +sample-3-catchup-workers4-target8: baseline execution +sample-3-catchup-workers4-target8: concurrent execution +sample-3-catchup-workers4-target8: execution ratio 1.215x +[ + { + "workers": 2, + "target": 4, + "mode": "catchup", + "samples": 3, + "median_execution_slowdown_ratio": 1.0192115768463075, + "median_execution_baseline_ns": 3817.0, + "median_execution_concurrent_ns": 4021.0, + "median_sigverify_residual_p95_ms": 174.2, + "median_sigverify_cpu_ms_per_block": 311.5 + }, + { + "workers": 2, + "target": 4, + "mode": "tip_200ms_overlap", + "samples": 3, + "median_execution_slowdown_ratio": 1.069901790872328, + "median_execution_baseline_ns": 3502.0, + "median_execution_concurrent_ns": 3704.0, + "median_sigverify_residual_p95_ms": 1.497, + "median_sigverify_cpu_ms_per_block": 314.3 + }, + { + "workers": 2, + "target": 8, + "mode": "catchup", + "samples": 3, + "median_execution_slowdown_ratio": 1.2183874709976799, + "median_execution_baseline_ns": 3542.0, + "median_execution_concurrent_ns": 4201.0, + "median_sigverify_residual_p95_ms": 90.25, + "median_sigverify_cpu_ms_per_block": 160.8 + }, + { + "workers": 2, + "target": 8, + "mode": "tip_200ms_overlap", + "samples": 3, + "median_execution_slowdown_ratio": 1.0169979832901181, + "median_execution_baseline_ns": 3471.0, + "median_execution_concurrent_ns": 3536.0, + "median_sigverify_residual_p95_ms": 1.357, + "median_sigverify_cpu_ms_per_block": 169.9 + }, + { + "workers": 4, + "target": 4, + "mode": "catchup", + "samples": 3, + "median_execution_slowdown_ratio": 1.0121273370389086, + "median_execution_baseline_ns": 3958.0, + "median_execution_concurrent_ns": 4182.0, + "median_sigverify_residual_p95_ms": 94.24, + "median_sigverify_cpu_ms_per_block": 316.1 + }, + { + "workers": 4, + "target": 4, + "mode": "tip_200ms_overlap", + "samples": 3, + "median_execution_slowdown_ratio": 1.2137119908204246, + "median_execution_baseline_ns": 3489.0, + "median_execution_concurrent_ns": 4231.0, + "median_sigverify_residual_p95_ms": 1.263, + "median_sigverify_cpu_ms_per_block": 332.0 + }, + { + "workers": 4, + "target": 8, + "mode": "catchup", + "samples": 3, + "median_execution_slowdown_ratio": 1.2152796009451299, + "median_execution_baseline_ns": 3547.0, + "median_execution_concurrent_ns": 4629.0, + "median_sigverify_residual_p95_ms": 54.05, + "median_sigverify_cpu_ms_per_block": 168.4 + }, + { + "workers": 4, + "target": 8, + "mode": "tip_200ms_overlap", + "samples": 3, + "median_execution_slowdown_ratio": 0.9911012235817576, + "median_execution_baseline_ns": 3596.0, + "median_execution_concurrent_ns": 3564.0, + "median_sigverify_residual_p95_ms": 1.256, + "median_sigverify_cpu_ms_per_block": 176.1 + } +] diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/execution-raw.log b/docs/results/sigverify-streaming/2026-09-12-zen5/execution-raw.log new file mode 100644 index 00000000..e5fbb1e8 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/execution-raw.log @@ -0,0 +1,576 @@ + +=== sample-1-catchup-workers2-target4/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 149238 4008 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-catchup-workers2-target4/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 146756 4085 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-catchup-workers2-target4/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/catchup-8 96 153189266 ns/op 2.048 avg_cpu_cores 1.000 components/block 313.8 cpu-ms/block 0.0002110 feed_lag_p50-ms 0.0004810 feed_lag_p95-ms 4.000 mean_width 149.6 ready_p50-ms 174.2 ready_p95-ms 149.6 residual_p50-ms 174.2 residual_p95-ms 33760 signatures/block 220381 signatures/s 0.003657 submit_p50-ms 0.01091 submit_p95-ms 7054772 B/op 67536 allocs/op +PASS + +=== sample-1-catchup-workers2-target8/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 174750 3542 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-catchup-workers2-target8/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 159576 4432 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-catchup-workers2-target8/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/catchup-8 96 78756306 ns/op 2.051 avg_cpu_cores 1.000 components/block 161.6 cpu-ms/block 0.0002200 feed_lag_p50-ms 0.0004910 feed_lag_p95-ms 8.000 mean_width 75.68 ready_p50-ms 90.65 ready_p95-ms 75.68 residual_p50-ms 90.65 residual_p95-ms 33760 signatures/block 428664 signatures/s 0.003326 submit_p50-ms 0.01184 submit_p95-ms 7060612 B/op 67536 allocs/op +PASS + +=== sample-1-catchup-workers4-target4/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 164997 3958 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-catchup-workers4-target4/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 152398 4006 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-catchup-workers4-target4/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/catchup-8 96 79167070 ns/op 3.991 avg_cpu_cores 1.000 components/block 316.0 cpu-ms/block 0.0002300 feed_lag_p50-ms 0.0003700 feed_lag_p95-ms 4.000 mean_width 76.56 ready_p50-ms 96.70 ready_p95-ms 76.56 residual_p50-ms 96.70 residual_p95-ms 33760 signatures/block 426440 signatures/s 0.003706 submit_p50-ms 0.01100 submit_p95-ms 7060780 B/op 67537 allocs/op +PASS + +=== sample-1-catchup-workers4-target8/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 170856 3547 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-catchup-workers4-target8/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 139512 4060 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-catchup-workers4-target8/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/catchup-8 96 40210294 ns/op 3.993 avg_cpu_cores 1.000 components/block 160.6 cpu-ms/block 0.0002110 feed_lag_p50-ms 0.0004300 feed_lag_p95-ms 8.000 mean_width 39.57 ready_p50-ms 43.68 ready_p95-ms 39.57 residual_p50-ms 43.68 residual_p95-ms 33760 signatures/block 839586 signatures/s 0.003306 submit_p50-ms 0.005891 submit_p95-ms 7058192 B/op 67537 allocs/op +PASS + +=== sample-1-tip_200ms_overlap-workers2-target4/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 172561 3462 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-tip_200ms_overlap-workers2-target4/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 156918 3704 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-tip_200ms_overlap-workers2-target4/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_overlap-8 20 201168852 ns/op 1.563 avg_cpu_cores 126.0 components/block 314.3 cpu-ms/block 0.04094 feed_lag_p50-ms 0.8651 feed_lag_p95-ms 3.956 mean_width 1.616 ready_p50-ms 2.755 ready_p95-ms 1.196 residual_p50-ms 1.497 residual_p95-ms 33760 signatures/block 167819 signatures/s 0.001432 submit_p50-ms 0.004018 submit_p95-ms 7234818 B/op 68941 allocs/op +PASS + +=== sample-1-tip_200ms_overlap-workers2-target8/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 167744 3471 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-tip_200ms_overlap-workers2-target8/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 171600 3530 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-tip_200ms_overlap-workers2-target8/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_overlap-8 20 200924240 ns/op 0.8916 avg_cpu_cores 126.0 components/block 179.2 cpu-ms/block 0.4462 feed_lag_p50-ms 0.9733 feed_lag_p95-ms 7.912 mean_width 1.190 ready_p50-ms 1.808 ready_p95-ms 0.9351 residual_p50-ms 1.356 residual_p95-ms 33760 signatures/block 168024 signatures/s 0.001734 submit_p50-ms 0.004288 submit_p95-ms 7208732 B/op 68935 allocs/op +PASS + +=== sample-1-tip_200ms_overlap-workers4-target4/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 165466 3489 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-tip_200ms_overlap-workers4-target4/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 155095 4302 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-tip_200ms_overlap-workers4-target4/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_overlap-8 20 201022700 ns/op 1.651 avg_cpu_cores 126.0 components/block 332.0 cpu-ms/block 0.4337 feed_lag_p50-ms 0.9876 feed_lag_p95-ms 3.956 mean_width 1.206 ready_p50-ms 1.982 ready_p95-ms 1.099 residual_p50-ms 1.257 residual_p95-ms 33760 signatures/block 167941 signatures/s 0.001674 submit_p50-ms 0.004459 submit_p95-ms 7241454 B/op 68940 allocs/op +PASS + +=== sample-1-tip_200ms_overlap-workers4-target8/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 165728 3596 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-tip_200ms_overlap-workers4-target8/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 167448 3564 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-1-tip_200ms_overlap-workers4-target8/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_overlap-8 20 200934390 ns/op 0.8582 avg_cpu_cores 126.0 components/block 172.4 cpu-ms/block 0.5170 feed_lag_p50-ms 1.006 feed_lag_p95-ms 7.912 mean_width 0.9062 ready_p50-ms 1.396 ready_p95-ms 1.041 residual_p50-ms 1.256 residual_p95-ms 33760 signatures/block 168015 signatures/s 0.001383 submit_p50-ms 0.003878 submit_p95-ms 7274370 B/op 68941 allocs/op +PASS + +=== sample-2-catchup-workers2-target4/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 170058 3624 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-catchup-workers2-target4/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 160458 4021 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-catchup-workers2-target4/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/catchup-8 96 151919970 ns/op 2.051 avg_cpu_cores 1.000 components/block 311.5 cpu-ms/block 0.0002200 feed_lag_p50-ms 0.0004610 feed_lag_p95-ms 4.000 mean_width 147.5 ready_p50-ms 175.5 ready_p95-ms 147.5 residual_p50-ms 175.5 residual_p95-ms 33760 signatures/block 222222 signatures/s 0.003677 submit_p50-ms 0.007574 submit_p95-ms 7061922 B/op 67537 allocs/op +PASS + +=== sample-2-catchup-workers2-target8/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 168331 3448 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-catchup-workers2-target8/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 142396 4201 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-catchup-workers2-target8/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/catchup-8 96 77871825 ns/op 2.052 avg_cpu_cores 1.000 components/block 159.8 cpu-ms/block 0.0002000 feed_lag_p50-ms 0.0003800 feed_lag_p95-ms 8.000 mean_width 76.13 ready_p50-ms 88.31 ready_p95-ms 76.13 residual_p50-ms 88.31 residual_p95-ms 33760 signatures/block 433533 signatures/s 0.003436 submit_p50-ms 0.007464 submit_p95-ms 7056320 B/op 67536 allocs/op +PASS + +=== sample-2-catchup-workers4-target4/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 155592 4248 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-catchup-workers4-target4/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 134372 4182 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-catchup-workers4-target4/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/catchup-8 96 79047165 ns/op 3.999 avg_cpu_cores 1.000 components/block 316.1 cpu-ms/block 0.0002000 feed_lag_p50-ms 0.0002910 feed_lag_p95-ms 4.000 mean_width 76.57 ready_p50-ms 94.24 ready_p95-ms 76.57 residual_p50-ms 94.24 residual_p95-ms 33760 signatures/block 427087 signatures/s 0.003747 submit_p50-ms 0.01152 submit_p95-ms 7056514 B/op 67537 allocs/op +PASS + +=== sample-2-catchup-workers4-target8/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 168381 3462 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-catchup-workers4-target8/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 141712 4887 ns/op 4145 B/op 59 allocs/op +PASS + +=== sample-2-catchup-workers4-target8/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/catchup-8 96 42898713 ns/op 3.925 avg_cpu_cores 1.000 components/block 168.4 cpu-ms/block 0.0002300 feed_lag_p50-ms 0.0004710 feed_lag_p95-ms 8.000 mean_width 41.07 ready_p50-ms 54.05 ready_p95-ms 41.07 residual_p50-ms 54.05 residual_p95-ms 33760 signatures/block 786970 signatures/s 0.003397 submit_p50-ms 0.01086 submit_p95-ms 7053910 B/op 67536 allocs/op +PASS + +=== sample-2-tip_200ms_overlap-workers2-target4/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 166706 3502 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-tip_200ms_overlap-workers2-target4/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 107145 4799 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-tip_200ms_overlap-workers2-target4/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_overlap-8 20 201262863 ns/op 1.606 avg_cpu_cores 126.0 components/block 323.2 cpu-ms/block 0.03106 feed_lag_p50-ms 0.8493 feed_lag_p95-ms 3.956 mean_width 1.723 ready_p50-ms 2.871 ready_p95-ms 1.114 residual_p50-ms 1.802 residual_p95-ms 33760 signatures/block 167741 signatures/s 0.001453 submit_p50-ms 0.004058 submit_p95-ms 7212730 B/op 68935 allocs/op +PASS + +=== sample-2-tip_200ms_overlap-workers2-target8/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 174418 3727 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-tip_200ms_overlap-workers2-target8/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 167622 3536 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-tip_200ms_overlap-workers2-target8/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_overlap-8 20 200967117 ns/op 0.8453 avg_cpu_cores 126.0 components/block 169.9 cpu-ms/block 0.4835 feed_lag_p50-ms 0.9802 feed_lag_p95-ms 7.912 mean_width 1.166 ready_p50-ms 1.747 ready_p95-ms 0.9879 residual_p50-ms 1.377 residual_p95-ms 33760 signatures/block 167988 signatures/s 0.001313 submit_p50-ms 0.003486 submit_p95-ms 7222552 B/op 68937 allocs/op +PASS + +=== sample-2-tip_200ms_overlap-workers4-target4/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 174780 3601 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-tip_200ms_overlap-workers4-target4/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 162474 3740 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-tip_200ms_overlap-workers4-target4/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_overlap-8 20 200928113 ns/op 1.526 avg_cpu_cores 126.0 components/block 306.7 cpu-ms/block 0.4760 feed_lag_p50-ms 0.9896 feed_lag_p95-ms 3.956 mean_width 1.136 ready_p50-ms 1.672 ready_p95-ms 1.002 residual_p50-ms 1.263 residual_p95-ms 33760 signatures/block 168020 signatures/s 0.001252 submit_p50-ms 0.003527 submit_p95-ms 7256845 B/op 68947 allocs/op +PASS + +=== sample-2-tip_200ms_overlap-workers4-target8/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 137796 3945 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-tip_200ms_overlap-workers4-target8/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 171933 3546 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-2-tip_200ms_overlap-workers4-target8/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_overlap-8 20 200756778 ns/op 0.8979 avg_cpu_cores 126.0 components/block 180.3 cpu-ms/block 0.5109 feed_lag_p50-ms 0.9931 feed_lag_p95-ms 7.912 mean_width 0.9308 ready_p50-ms 1.413 ready_p95-ms 0.7481 residual_p50-ms 1.147 residual_p95-ms 33760 signatures/block 168164 signatures/s 0.001683 submit_p50-ms 0.004809 submit_p95-ms 7287646 B/op 68938 allocs/op +PASS + +=== sample-3-catchup-workers2-target4/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 166202 3817 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-catchup-workers2-target4/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 160532 3624 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-catchup-workers2-target4/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/catchup-8 96 147847296 ns/op 2.051 avg_cpu_cores 1.000 components/block 303.2 cpu-ms/block 0.0002300 feed_lag_p50-ms 0.0004910 feed_lag_p95-ms 4.000 mean_width 145.3 ready_p50-ms 159.8 ready_p95-ms 145.3 residual_p50-ms 159.8 residual_p95-ms 33760 signatures/block 228344 signatures/s 0.003817 submit_p50-ms 0.007323 submit_p95-ms 7057630 B/op 67536 allocs/op +PASS + +=== sample-3-catchup-workers2-target8/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 164358 3884 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-catchup-workers2-target8/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 126450 4148 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-catchup-workers2-target8/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/catchup-8 96 78410504 ns/op 2.050 avg_cpu_cores 1.000 components/block 160.8 cpu-ms/block 0.0002300 feed_lag_p50-ms 0.0004810 feed_lag_p95-ms 8.000 mean_width 75.31 ready_p50-ms 90.25 ready_p95-ms 75.31 residual_p50-ms 90.25 residual_p95-ms 33760 signatures/block 430555 signatures/s 0.003577 submit_p50-ms 0.007614 submit_p95-ms 7057749 B/op 67536 allocs/op +PASS + +=== sample-3-catchup-workers4-target4/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 169953 3669 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-catchup-workers4-target4/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 145288 4207 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-catchup-workers4-target4/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/catchup-8 96 80096206 ns/op 3.978 avg_cpu_cores 1.000 components/block 318.6 cpu-ms/block 0.0002300 feed_lag_p50-ms 0.0006910 feed_lag_p95-ms 4.000 mean_width 77.65 ready_p50-ms 92.49 ready_p95-ms 77.65 residual_p50-ms 92.49 residual_p95-ms 33760 signatures/block 421493 signatures/s 0.004018 submit_p50-ms 0.01255 submit_p95-ms 7057922 B/op 67536 allocs/op +PASS + +=== sample-3-catchup-workers4-target8/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 167019 3809 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-catchup-workers4-target8/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 148914 4629 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-catchup-workers4-target8/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/catchup-8 96 44540126 ns/op 3.899 avg_cpu_cores 1.000 components/block 173.7 cpu-ms/block 0.0002400 feed_lag_p50-ms 0.0003300 feed_lag_p95-ms 8.000 mean_width 42.49 ready_p50-ms 55.03 ready_p95-ms 42.49 residual_p50-ms 55.03 residual_p95-ms 33760 signatures/block 757968 signatures/s 0.003727 submit_p50-ms 0.01137 submit_p95-ms 7053895 B/op 67536 allocs/op +PASS + +=== sample-3-tip_200ms_overlap-workers2-target4/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 140451 3666 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-tip_200ms_overlap-workers2-target4/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 155277 3597 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-tip_200ms_overlap-workers2-target4/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_overlap-8 20 201192091 ns/op 1.506 avg_cpu_cores 126.0 components/block 303.0 cpu-ms/block 0.06870 feed_lag_p50-ms 0.8534 feed_lag_p95-ms 3.956 mean_width 1.591 ready_p50-ms 2.540 ready_p95-ms 1.178 residual_p50-ms 1.465 residual_p95-ms 33760 signatures/block 167800 signatures/s 0.001112 submit_p50-ms 0.002705 submit_p95-ms 7200373 B/op 68940 allocs/op +PASS + +=== sample-3-tip_200ms_overlap-workers2-target8/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 168775 3465 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-tip_200ms_overlap-workers2-target8/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 167258 3583 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-tip_200ms_overlap-workers2-target8/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_overlap-8 20 200968000 ns/op 0.8420 avg_cpu_cores 126.0 components/block 169.2 cpu-ms/block 0.4334 feed_lag_p50-ms 0.9712 feed_lag_p95-ms 7.912 mean_width 1.178 ready_p50-ms 1.715 ready_p95-ms 1.039 residual_p50-ms 1.357 residual_p95-ms 33760 signatures/block 167987 signatures/s 0.001403 submit_p50-ms 0.004248 submit_p95-ms 7250793 B/op 68941 allocs/op +PASS + +=== sample-3-tip_200ms_overlap-workers4-target4/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 167756 3486 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-tip_200ms_overlap-workers4-target4/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 141630 4231 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-tip_200ms_overlap-workers4-target4/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_overlap-8 20 200751013 ns/op 1.678 avg_cpu_cores 126.0 components/block 336.9 cpu-ms/block 0.4283 feed_lag_p50-ms 0.9902 feed_lag_p95-ms 3.956 mean_width 1.217 ready_p50-ms 2.032 ready_p95-ms 0.5827 residual_p50-ms 1.286 residual_p95-ms 33760 signatures/block 168169 signatures/s 0.001764 submit_p50-ms 0.004468 submit_p95-ms 7235916 B/op 68943 allocs/op +PASS + +=== sample-3-tip_200ms_overlap-workers4-target8/execution-baseline.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 170604 3472 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-tip_200ms_overlap-workers4-target8/execution-concurrent.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkLoadAndExecuteTransferResultMode/lean-8 159267 4093 ns/op 4146 B/op 59 allocs/op +PASS + +=== sample-3-tip_200ms_overlap-workers4-target8/sigverify-load.log === +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_overlap-8 20 200971694 ns/op 0.8761 avg_cpu_cores 126.0 components/block 176.1 cpu-ms/block 0.5208 feed_lag_p50-ms 1.015 feed_lag_p95-ms 7.912 mean_width 0.9286 ready_p50-ms 1.430 ready_p95-ms 1.097 residual_p50-ms 1.294 residual_p95-ms 33760 signatures/block 167984 signatures/s 0.001553 submit_p50-ms 0.004518 submit_p95-ms 7272930 B/op 68937 allocs/op +PASS diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/final-commands.jsonl b/docs/results/sigverify-streaming/2026-09-12-zen5/final-commands.jsonl new file mode 100644 index 00000000..b63468b4 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/final-commands.jsonl @@ -0,0 +1,6 @@ +{"name": "native-final-unit", "command": ["nice", "-n", "10", "/usr/local/go/bin/go", "test", "-p", "2", "./pkg/turbine", "./pkg/sigverify", "./pkg/txverify", "./pkg/block", "./pkg/statsd", "./cmd/mithril/node"], "extra_env": {}, "seconds": 4.821521997451782, "exit_code": 0} +{"name": "native-final-race", "command": ["nice", "-n", "10", "/usr/local/go/bin/go", "test", "-race", "-p", "2", "./pkg/turbine", "-run", "TestEntryPrefetch|TestEntryDecode|TestTransactionVerifi|TestUDPReceiver", "-count=2"], "extra_env": {}, "seconds": 7.025081634521484, "exit_code": 0} +{"name": "build-turbine-final", "command": ["nice", "-n", "10", "/usr/local/go/bin/go", "test", "-c", "-p", "2", "-o", "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", "./pkg/turbine"], "extra_env": {}, "seconds": 1.566469430923462, "exit_code": 0} +{"name": "assembly-captured", "command": ["nice", "-n", "10", "taskset", "--cpu-list", "0-7", "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", "-test.run=^$", "-test.benchtime=3x", "-test.count=3", "-test.timeout=240s", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^captured_2622581$/^workers_.*$/^target_8$/.*"], "extra_env": {"MITHRIL_SIGVERIFY_FLOW_BACKEND": "r51", "MITHRIL_SIGVERIFY_FLOW_FIXTURES": "/srv/mithril-planner-pr257-trial-20260912/fixtures"}, "seconds": 18.89488172531128, "exit_code": 0} +{"name": "assembly-1232", "command": ["nice", "-n", "10", "taskset", "--cpu-list", "0-7", "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", "-test.run=^$", "-test.benchtime=3x", "-test.count=3", "-test.timeout=240s", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_1232B$/^workers_.*$/^target_8$/.*"], "extra_env": {"MITHRIL_SIGVERIFY_FLOW_BACKEND": "r51", "MITHRIL_SIGVERIFY_FLOW_COUNT": "33760"}, "seconds": 24.252999305725098, "exit_code": 0} +{"name": "execution-contention", "command": ["nice", "-n", "10", "python3", "/srv/mithril-sigverify-streaming-20260912/run_execution_contention.py", "--turbine-bin", "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", "--replay-bin", "/srv/mithril-sigverify-streaming-20260912/bin/replay.test", "--fixtures", "/srv/mithril-planner-pr257-trial-20260912/fixtures", "--slot", "2622581", "--cpus", "0-7", "--out", "/srv/mithril-sigverify-streaming-20260912/results/execution-contention", "--samples", "3", "--catchup-blocks", "96", "--tip-blocks", "20"], "extra_env": {}, "seconds": 179.08553171157837, "exit_code": 0} diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/final-source-update.json b/docs/results/sigverify-streaming/2026-09-12-zen5/final-source-update.json new file mode 100644 index 00000000..aaa6fe97 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/final-source-update.json @@ -0,0 +1,30 @@ +[ + { + "path": "config.example.toml", + "sha256": "ed208df9d174ab939160353779715209c8e2c82a6acc0a88dbc50c83dbad0c9b" + }, + { + "path": "docs/transaction_sigverify_streaming.md", + "sha256": "75968dbf5479adb7d25af6648e2b7ab33115534617f5551bde30a96a218332e4" + }, + { + "path": "pkg/turbine/entry_prefetch.go", + "sha256": "b69d9b559daccf85c042c01e06f93038f4d8a39fdc07a40397904935d84c4f3f" + }, + { + "path": "pkg/turbine/entry_prefetch_benchmark_test.go", + "sha256": "c360fc332437e8f2515d45dce9d0e798246a5bf1a087f64e5e82fa83e1fb0bd2" + }, + { + "path": "pkg/turbine/entry_prefetch_bounds_test.go", + "sha256": "541fea19a6f3edaaba3e10945b4676342a4802c418a7317944a4e93c653dcd4b" + }, + { + "path": "pkg/turbine/entry_prefetch_test.go", + "sha256": "edcd844eb622124c181670b696174f3dde427e95570b45f012a20e62fb8e6dc8" + }, + { + "path": "pkg/turbine/receiver.go", + "sha256": "73a7b55e6efcc9b54e11e1b882840992263f90b350e71d2d8a69306a1f124066" + } +] \ No newline at end of file diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/generated-1232-pool-flow.log b/docs/results/sigverify-streaming/2026-09-12-zen5/generated-1232-pool-flow.log new file mode 100644 index 00000000..d6f8f950 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/generated-1232-pool-flow.log @@ -0,0 +1,77 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/catchup-8 3 165232107 ns/op 2.073 avg_cpu_cores 1.000 components/block 342.6 cpu-ms/block 0.0002410 feed_lag_p50-ms 0.0004810 feed_lag_p95-ms 4.000 mean_width 163.8 ready_p50-ms 171.9 ready_p95-ms 163.8 residual_p50-ms 171.9 residual_p95-ms 33760 signatures/block 204319 signatures/s 0.004889 submit_p50-ms 0.005961 submit_p95-ms 44570082 B/op 67557 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/catchup-8 3 166036574 ns/op 2.074 avg_cpu_cores 1.000 components/block 344.4 cpu-ms/block 0.0002110 feed_lag_p50-ms 0.0004710 feed_lag_p95-ms 4.000 mean_width 164.3 ready_p50-ms 172.6 ready_p95-ms 164.3 residual_p50-ms 172.6 residual_p95-ms 33760 signatures/block 203329 signatures/s 0.004078 submit_p50-ms 0.006131 submit_p95-ms 44615709 B/op 67558 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/catchup-8 3 158962907 ns/op 2.075 avg_cpu_cores 1.000 components/block 329.9 cpu-ms/block 0.0002500 feed_lag_p50-ms 0.0004610 feed_lag_p95-ms 4.000 mean_width 159.6 ready_p50-ms 160.2 ready_p95-ms 159.6 residual_p50-ms 160.2 residual_p95-ms 33760 signatures/block 212377 signatures/s 0.004829 submit_p50-ms 0.005470 submit_p95-ms 44707154 B/op 67561 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/tip_200ms_after_complete-8 3 359264432 ns/op 0.9167 avg_cpu_cores 689.0 components/block 329.3 cpu-ms/block 100.5 feed_lag_p50-ms 190.7 feed_lag_p95-ms 4.000 mean_width 259.3 ready_p50-ms 349.4 ready_p95-ms 159.3 residual_p50-ms 160.9 residual_p95-ms 33760 signatures/block 93970 signatures/s 0.01357 submit_p50-ms 0.01492 submit_p95-ms 44535416 B/op 67551 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/tip_200ms_after_complete-8 3 359629949 ns/op 0.9180 avg_cpu_cores 689.0 components/block 330.1 cpu-ms/block 100.7 feed_lag_p50-ms 190.8 feed_lag_p95-ms 4.000 mean_width 259.7 ready_p50-ms 349.8 ready_p95-ms 159.4 residual_p50-ms 161.5 residual_p95-ms 33760 signatures/block 93874 signatures/s 0.01362 submit_p50-ms 0.01513 submit_p95-ms 44535658 B/op 67552 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/tip_200ms_after_complete-8 3 359479815 ns/op 0.9171 avg_cpu_cores 689.0 components/block 329.7 cpu-ms/block 100.4 feed_lag_p50-ms 190.5 feed_lag_p95-ms 4.000 mean_width 259.5 ready_p50-ms 349.6 ready_p95-ms 159.9 residual_p50-ms 160.2 residual_p95-ms 33760 signatures/block 93913 signatures/s 0.01571 submit_p50-ms 0.01585 submit_p95-ms 44489704 B/op 67550 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/tip_200ms_overlap-8 3 200661932 ns/op 1.879 avg_cpu_cores 689.0 components/block 377.1 cpu-ms/block 0.04898 feed_lag_p50-ms 3.657 feed_lag_p95-ms 3.770 mean_width 0.9535 ready_p50-ms 5.077 ready_p95-ms 0.6731 residual_p50-ms 0.9156 residual_p95-ms 33760 signatures/block 168243 signatures/s 0.001192 submit_p50-ms 0.5149 submit_p95-ms 45346429 B/op 75220 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/tip_200ms_overlap-8 3 200375326 ns/op 1.856 avg_cpu_cores 689.0 components/block 371.8 cpu-ms/block 0.04542 feed_lag_p50-ms 2.251 feed_lag_p95-ms 3.770 mean_width 0.9154 ready_p50-ms 3.697 ready_p95-ms 0.3591 residual_p50-ms 0.4024 residual_p95-ms 33760 signatures/block 168484 signatures/s 0.001102 submit_p50-ms 0.5519 submit_p95-ms 45279114 B/op 75174 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/tip_200ms_overlap-8 3 200405779 ns/op 1.798 avg_cpu_cores 689.0 components/block 360.4 cpu-ms/block 0.03941 feed_lag_p50-ms 1.051 feed_lag_p95-ms 3.770 mean_width 0.8241 ready_p50-ms 2.498 ready_p95-ms 0.3923 residual_p50-ms 0.5034 residual_p95-ms 33760 signatures/block 168458 signatures/s 0.0008520 submit_p50-ms 0.5208 submit_p95-ms 45287578 B/op 75182 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_4tx_200ms_overlap-8 3 200381187 ns/op 0.02000 avg_cpu_cores 64.00 components/block 4.008 cpu-ms/block 0.5205 feed_lag_p50-ms 0.9542 feed_lag_p95-ms 4.000 mean_width 0.5746 ready_p50-ms 1.006 ready_p95-ms 0.06565 residual_p50-ms 1.008 residual_p95-ms 256.0 signatures/block 1278 signatures/s 0.001062 submit_p50-ms 0.003106 submit_p95-ms 405549 B/op 1227 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_4tx_200ms_overlap-8 3 200548807 ns/op 0.03358 avg_cpu_cores 64.00 components/block 6.734 cpu-ms/block 0.5788 feed_lag_p50-ms 0.9736 feed_lag_p95-ms 4.000 mean_width 0.6502 ready_p50-ms 1.057 ready_p95-ms 0.4003 residual_p50-ms 0.8991 residual_p95-ms 256.0 signatures/block 1276 signatures/s 0.002535 submit_p50-ms 0.01151 submit_p95-ms 405570 B/op 1228 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_4tx_200ms_overlap-8 3 200692627 ns/op 0.02155 avg_cpu_cores 64.00 components/block 4.324 cpu-ms/block 0.5307 feed_lag_p50-ms 0.9496 feed_lag_p95-ms 4.000 mean_width 0.5792 ready_p50-ms 1.004 ready_p95-ms 0.7962 residual_p50-ms 0.9766 residual_p95-ms 256.0 signatures/block 1276 signatures/s 0.001232 submit_p50-ms 0.003516 submit_p95-ms 405549 B/op 1227 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_7tx_200ms_overlap-8 3 200734923 ns/op 0.02505 avg_cpu_cores 37.00 components/block 5.029 cpu-ms/block 0.5435 feed_lag_p50-ms 0.9882 feed_lag_p95-ms 3.507 mean_width 0.6193 ready_p50-ms 1.055 ready_p95-ms 0.8195 residual_p50-ms 1.028 residual_p95-ms 256.0 signatures/block 1275 signatures/s 0.001703 submit_p50-ms 0.005821 submit_p95-ms 376309 B/op 931 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_7tx_200ms_overlap-8 3 200839933 ns/op 0.02110 avg_cpu_cores 37.00 components/block 4.237 cpu-ms/block 0.5533 feed_lag_p50-ms 1.006 feed_lag_p95-ms 3.507 mean_width 0.6087 ready_p50-ms 1.060 ready_p95-ms 0.9924 residual_p50-ms 1.071 residual_p95-ms 256.0 signatures/block 1275 signatures/s 0.001212 submit_p50-ms 0.003216 submit_p95-ms 376301 B/op 930 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_7tx_200ms_overlap-8 3 200166050 ns/op 0.02238 avg_cpu_cores 37.00 components/block 4.480 cpu-ms/block 0.5576 feed_lag_p50-ms 1.003 feed_lag_p95-ms 3.507 mean_width 0.6392 ready_p50-ms 1.089 ready_p95-ms 0.1596 residual_p50-ms 0.1676 residual_p95-ms 256.0 signatures/block 1279 signatures/s 0.001563 submit_p50-ms 0.003787 submit_p95-ms 376301 B/op 930 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_8tx_200ms_overlap-8 3 200891132 ns/op 0.02598 avg_cpu_cores 32.00 components/block 5.220 cpu-ms/block 0.6103 feed_lag_p50-ms 1.029 feed_lag_p95-ms 4.000 mean_width 0.6923 ready_p50-ms 1.127 ready_p95-ms 0.9305 residual_p50-ms 1.072 residual_p95-ms 256.0 signatures/block 1274 signatures/s 0.002044 submit_p50-ms 0.01812 submit_p95-ms 370733 B/op 875 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_8tx_200ms_overlap-8 3 200748211 ns/op 0.01877 avg_cpu_cores 32.00 components/block 3.768 cpu-ms/block 0.4370 feed_lag_p50-ms 1.023 feed_lag_p95-ms 4.000 mean_width 0.5164 ready_p50-ms 1.080 ready_p95-ms 0.7419 residual_p50-ms 0.7640 residual_p95-ms 256.0 signatures/block 1275 signatures/s 0.001562 submit_p50-ms 0.003406 submit_p95-ms 370733 B/op 875 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_8tx_200ms_overlap-8 3 200821061 ns/op 0.02266 avg_cpu_cores 32.00 components/block 4.551 cpu-ms/block 0.6271 feed_lag_p50-ms 1.025 feed_lag_p95-ms 4.000 mean_width 0.6833 ready_p50-ms 1.099 ready_p95-ms 0.8705 residual_p50-ms 0.9203 residual_p95-ms 256.0 signatures/block 1275 signatures/s 0.001843 submit_p50-ms 0.01049 submit_p95-ms 370733 B/op 875 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/catchup-8 3 88134578 ns/op 2.097 avg_cpu_cores 1.000 components/block 184.8 cpu-ms/block 0.0004010 feed_lag_p50-ms 0.0008320 feed_lag_p95-ms 8.000 mean_width 89.14 ready_p50-ms 92.15 ready_p95-ms 89.14 residual_p50-ms 92.15 residual_p95-ms 33760 signatures/block 383051 signatures/s 0.005991 submit_p50-ms 0.01270 submit_p95-ms 44478298 B/op 67549 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/catchup-8 3 86113936 ns/op 2.090 avg_cpu_cores 1.000 components/block 180.0 cpu-ms/block 0.0002600 feed_lag_p50-ms 0.0007320 feed_lag_p95-ms 8.000 mean_width 84.68 ready_p50-ms 89.35 ready_p95-ms 84.68 residual_p50-ms 89.35 residual_p95-ms 33760 signatures/block 392039 signatures/s 0.004137 submit_p50-ms 0.005370 submit_p95-ms 44386656 B/op 67544 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/catchup-8 3 84405778 ns/op 2.098 avg_cpu_cores 1.000 components/block 177.1 cpu-ms/block 0.0002500 feed_lag_p50-ms 0.0004810 feed_lag_p95-ms 8.000 mean_width 84.06 ready_p50-ms 85.34 ready_p95-ms 84.06 residual_p50-ms 85.34 residual_p95-ms 33760 signatures/block 399973 signatures/s 0.004048 submit_p50-ms 0.004588 submit_p95-ms 44569941 B/op 67554 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/tip_200ms_after_complete-8 3 284620670 ns/op 0.6238 avg_cpu_cores 689.0 components/block 177.6 cpu-ms/block 100.2 feed_lag_p50-ms 190.3 feed_lag_p95-ms 8.000 mean_width 184.6 ready_p50-ms 274.8 ready_p95-ms 84.69 residual_p50-ms 85.06 residual_p95-ms 33760 signatures/block 118614 signatures/s 0.01488 submit_p50-ms 0.01676 submit_p95-ms 44489685 B/op 67548 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/tip_200ms_after_complete-8 3 285857000 ns/op 0.6282 avg_cpu_cores 689.0 components/block 179.6 cpu-ms/block 100.4 feed_lag_p50-ms 190.5 feed_lag_p95-ms 8.000 mean_width 185.9 ready_p50-ms 276.0 ready_p95-ms 85.29 residual_p50-ms 87.23 residual_p95-ms 33760 signatures/block 118101 signatures/s 0.01511 submit_p50-ms 0.01781 submit_p95-ms 44627120 B/op 67555 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/tip_200ms_after_complete-8 3 290218004 ns/op 0.6484 avg_cpu_cores 689.0 components/block 188.2 cpu-ms/block 100.7 feed_lag_p50-ms 190.8 feed_lag_p95-ms 8.000 mean_width 190.1 ready_p50-ms 280.3 ready_p95-ms 91.02 residual_p50-ms 96.42 residual_p95-ms 33760 signatures/block 116326 signatures/s 0.01466 submit_p50-ms 0.02630 submit_p95-ms 44535536 B/op 67551 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/tip_200ms_overlap-8 3 200393747 ns/op 0.9599 avg_cpu_cores 689.0 components/block 192.4 cpu-ms/block 0.1208 feed_lag_p50-ms 0.6496 feed_lag_p95-ms 7.001 mean_width 0.5021 ready_p50-ms 1.098 ready_p95-ms 0.3487 residual_p50-ms 0.6362 residual_p95-ms 33760 signatures/block 168468 signatures/s 0.0006010 submit_p50-ms 0.002294 submit_p95-ms 45456104 B/op 75154 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/tip_200ms_overlap-8 3 200299620 ns/op 0.9555 avg_cpu_cores 689.0 components/block 191.4 cpu-ms/block 0.1164 feed_lag_p50-ms 0.6451 feed_lag_p95-ms 7.001 mean_width 0.4922 ready_p50-ms 1.083 ready_p95-ms 0.2595 residual_p50-ms 0.3970 residual_p95-ms 33760 signatures/block 168547 signatures/s 0.0005810 submit_p50-ms 0.001764 submit_p95-ms 45500650 B/op 75145 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/tip_200ms_overlap-8 3 200435552 ns/op 1.129 avg_cpu_cores 689.0 components/block 226.3 cpu-ms/block 0.08438 feed_lag_p50-ms 0.7172 feed_lag_p95-ms 7.001 mean_width 0.5504 ready_p50-ms 1.334 ready_p95-ms 0.3419 residual_p50-ms 0.6538 residual_p95-ms 33760 signatures/block 168433 signatures/s 0.0009020 submit_p50-ms 0.08766 submit_p95-ms 45547920 B/op 75161 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_4tx_200ms_overlap-8 3 200244671 ns/op 0.03331 avg_cpu_cores 64.00 components/block 6.669 cpu-ms/block 0.5335 feed_lag_p50-ms 0.9768 feed_lag_p95-ms 4.000 mean_width 0.6135 ready_p50-ms 1.060 ready_p95-ms 0.2598 residual_p50-ms 0.2713 residual_p95-ms 256.0 signatures/block 1278 signatures/s 0.002525 submit_p50-ms 0.008676 submit_p95-ms 413741 B/op 1227 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_4tx_200ms_overlap-8 3 200802763 ns/op 0.02071 avg_cpu_cores 64.00 components/block 4.159 cpu-ms/block 0.5448 feed_lag_p50-ms 0.9858 feed_lag_p95-ms 4.000 mean_width 0.5937 ready_p50-ms 1.037 ready_p95-ms 1.016 residual_p50-ms 1.070 residual_p95-ms 256.0 signatures/block 1275 signatures/s 0.001102 submit_p50-ms 0.002816 submit_p95-ms 413741 B/op 1227 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_4tx_200ms_overlap-8 3 200470397 ns/op 0.02005 avg_cpu_cores 64.00 components/block 4.019 cpu-ms/block 0.5300 feed_lag_p50-ms 0.9483 feed_lag_p95-ms 4.000 mean_width 0.5769 ready_p50-ms 1.000 ready_p95-ms 0.3612 residual_p50-ms 0.8751 residual_p95-ms 256.0 signatures/block 1277 signatures/s 0.001022 submit_p50-ms 0.003106 submit_p95-ms 413741 B/op 1227 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_7tx_200ms_overlap-8 3 200530663 ns/op 0.02064 avg_cpu_cores 37.00 components/block 4.138 cpu-ms/block 0.5475 feed_lag_p50-ms 1.006 feed_lag_p95-ms 6.919 mean_width 0.6508 ready_p50-ms 1.095 ready_p95-ms 0.2603 residual_p50-ms 1.101 residual_p95-ms 256.0 signatures/block 1277 signatures/s 0.001413 submit_p50-ms 0.003417 submit_p95-ms 381074 B/op 931 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_7tx_200ms_overlap-8 3 200704225 ns/op 0.02970 avg_cpu_cores 37.00 components/block 5.961 cpu-ms/block 0.5458 feed_lag_p50-ms 0.9953 feed_lag_p95-ms 6.919 mean_width 0.6663 ready_p50-ms 1.147 ready_p95-ms 0.6189 residual_p50-ms 1.129 residual_p95-ms 256.0 signatures/block 1276 signatures/s 0.003116 submit_p50-ms 0.008637 submit_p95-ms 381186 B/op 932 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_7tx_200ms_overlap-8 3 200734505 ns/op 0.01986 avg_cpu_cores 37.00 components/block 3.987 cpu-ms/block 0.5511 feed_lag_p50-ms 0.9991 feed_lag_p95-ms 6.919 mean_width 0.6354 ready_p50-ms 1.085 ready_p95-ms 0.8372 residual_p50-ms 1.059 residual_p95-ms 256.0 signatures/block 1275 signatures/s 0.001192 submit_p50-ms 0.003176 submit_p95-ms 381037 B/op 930 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_8tx_200ms_overlap-8 3 200612984 ns/op 0.01255 avg_cpu_cores 32.00 components/block 2.517 cpu-ms/block 0.5718 feed_lag_p50-ms 1.002 feed_lag_p95-ms 8.000 mean_width 0.6479 ready_p50-ms 1.064 ready_p95-ms 0.4956 residual_p50-ms 0.9127 residual_p95-ms 256.0 signatures/block 1276 signatures/s 0.001432 submit_p50-ms 0.005420 submit_p95-ms 420621 B/op 878 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_8tx_200ms_overlap-8 3 200664651 ns/op 0.01322 avg_cpu_cores 32.00 components/block 2.652 cpu-ms/block 0.6362 feed_lag_p50-ms 1.010 feed_lag_p95-ms 8.000 mean_width 0.6894 ready_p50-ms 1.085 ready_p95-ms 0.6880 residual_p50-ms 0.7389 residual_p95-ms 256.0 signatures/block 1276 signatures/s 0.001503 submit_p50-ms 0.006863 submit_p95-ms 374829 B/op 875 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_8tx_200ms_overlap-8 3 200621059 ns/op 0.01625 avg_cpu_cores 32.00 components/block 3.260 cpu-ms/block 0.6089 feed_lag_p50-ms 1.027 feed_lag_p95-ms 8.000 mean_width 0.6781 ready_p50-ms 1.091 ready_p95-ms 0.6671 residual_p50-ms 0.8341 residual_p95-ms 256.0 signatures/block 1276 signatures/s 0.002575 submit_p50-ms 0.008015 submit_p95-ms 374941 B/op 876 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/catchup-8 3 95848314 ns/op 3.962 avg_cpu_cores 1.000 components/block 379.7 cpu-ms/block 0.0005010 feed_lag_p50-ms 0.0008820 feed_lag_p95-ms 4.000 mean_width 97.21 ready_p50-ms 97.49 ready_p95-ms 97.21 residual_p50-ms 97.49 residual_p95-ms 33760 signatures/block 352223 signatures/s 0.006161 submit_p50-ms 0.01999 submit_p95-ms 44616002 B/op 67558 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/catchup-8 3 93784047 ns/op 3.933 avg_cpu_cores 1.000 components/block 368.9 cpu-ms/block 0.0002600 feed_lag_p50-ms 0.0005810 feed_lag_p95-ms 4.000 mean_width 97.49 ready_p50-ms 98.78 ready_p95-ms 97.49 residual_p50-ms 98.78 residual_p95-ms 33760 signatures/block 359976 signatures/s 0.004298 submit_p50-ms 0.005380 submit_p95-ms 44570261 B/op 67556 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/catchup-8 3 83340419 ns/op 4.080 avg_cpu_cores 1.000 components/block 340.1 cpu-ms/block 0.0002600 feed_lag_p50-ms 0.0004810 feed_lag_p95-ms 4.000 mean_width 83.48 ready_p50-ms 84.27 ready_p95-ms 83.48 residual_p50-ms 84.27 residual_p95-ms 33760 signatures/block 405086 signatures/s 0.004358 submit_p50-ms 0.004779 submit_p95-ms 44615936 B/op 67557 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/tip_200ms_after_complete-8 3 284770040 ns/op 1.189 avg_cpu_cores 689.0 components/block 338.7 cpu-ms/block 100.2 feed_lag_p50-ms 190.3 feed_lag_p95-ms 4.000 mean_width 184.7 ready_p50-ms 274.8 ready_p95-ms 84.70 residual_p50-ms 84.90 residual_p95-ms 33760 signatures/block 118552 signatures/s 0.01420 submit_p50-ms 0.01501 submit_p95-ms 44627381 B/op 67557 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/tip_200ms_after_complete-8 3 283332704 ns/op 1.187 avg_cpu_cores 689.0 components/block 336.2 cpu-ms/block 100.4 feed_lag_p50-ms 190.5 feed_lag_p95-ms 4.000 mean_width 183.3 ready_p50-ms 273.5 ready_p95-ms 83.42 residual_p50-ms 83.48 residual_p95-ms 33760 signatures/block 119153 signatures/s 0.01509 submit_p50-ms 0.01671 submit_p95-ms 44764728 B/op 67563 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/tip_200ms_after_complete-8 3 288602671 ns/op 1.226 avg_cpu_cores 689.0 components/block 353.9 cpu-ms/block 100.4 feed_lag_p50-ms 190.5 feed_lag_p95-ms 4.000 mean_width 188.6 ready_p50-ms 278.7 ready_p95-ms 82.52 residual_p50-ms 101.5 residual_p95-ms 33760 signatures/block 116977 signatures/s 0.01444 submit_p50-ms 0.01669 submit_p95-ms 44627397 B/op 67557 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/tip_200ms_overlap-8 3 200391623 ns/op 1.700 avg_cpu_cores 689.0 components/block 340.6 cpu-ms/block 0.1294 feed_lag_p50-ms 0.6600 feed_lag_p95-ms 3.770 mean_width 0.4949 ready_p50-ms 1.080 ready_p95-ms 0.3539 residual_p50-ms 0.5576 residual_p95-ms 33760 signatures/block 168470 signatures/s 0.0006110 submit_p50-ms 0.001674 submit_p95-ms 45465394 B/op 75165 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/tip_200ms_overlap-8 3 200606793 ns/op 1.834 avg_cpu_cores 689.0 components/block 368.0 cpu-ms/block 0.09882 feed_lag_p50-ms 0.6699 feed_lag_p95-ms 3.770 mean_width 0.5168 ready_p50-ms 1.161 ready_p95-ms 0.5678 residual_p50-ms 0.7021 residual_p95-ms 33760 signatures/block 168289 signatures/s 0.0007420 submit_p50-ms 0.002705 submit_p95-ms 45554293 B/op 75146 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/tip_200ms_overlap-8 3 200390250 ns/op 1.799 avg_cpu_cores 689.0 components/block 360.4 cpu-ms/block 0.1082 feed_lag_p50-ms 0.6858 feed_lag_p95-ms 3.770 mean_width 0.5309 ready_p50-ms 1.290 ready_p95-ms 0.2986 residual_p50-ms 0.5996 residual_p95-ms 33760 signatures/block 168471 signatures/s 0.0007310 submit_p50-ms 0.002485 submit_p95-ms 45417898 B/op 75147 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_4tx_200ms_overlap-8 3 200708870 ns/op 0.02342 avg_cpu_cores 64.00 components/block 4.700 cpu-ms/block 0.5677 feed_lag_p50-ms 0.9727 feed_lag_p95-ms 4.000 mean_width 0.6206 ready_p50-ms 1.032 ready_p95-ms 0.5738 residual_p50-ms 1.110 residual_p95-ms 256.0 signatures/block 1275 signatures/s 0.001583 submit_p50-ms 0.004359 submit_p95-ms 423106 B/op 1229 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_4tx_200ms_overlap-8 3 200711469 ns/op 0.02988 avg_cpu_cores 64.00 components/block 5.998 cpu-ms/block 0.5955 feed_lag_p50-ms 1.001 feed_lag_p95-ms 4.000 mean_width 0.6690 ready_p50-ms 1.073 ready_p95-ms 0.6728 residual_p50-ms 0.8759 residual_p95-ms 256.0 signatures/block 1275 signatures/s 0.002254 submit_p50-ms 0.005320 submit_p95-ms 422957 B/op 1227 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_4tx_200ms_overlap-8 3 200458906 ns/op 0.02460 avg_cpu_cores 64.00 components/block 4.931 cpu-ms/block 0.5634 feed_lag_p50-ms 0.9819 feed_lag_p95-ms 4.000 mean_width 0.6169 ready_p50-ms 1.037 ready_p95-ms 0.4628 residual_p50-ms 0.6882 residual_p95-ms 256.0 signatures/block 1277 signatures/s 0.001663 submit_p50-ms 0.004278 submit_p95-ms 422957 B/op 1227 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_7tx_200ms_overlap-8 3 200411787 ns/op 0.02500 avg_cpu_cores 37.00 components/block 5.011 cpu-ms/block 0.5475 feed_lag_p50-ms 0.9961 feed_lag_p95-ms 3.507 mean_width 0.6059 ready_p50-ms 1.064 ready_p95-ms 0.2112 residual_p50-ms 0.8439 residual_p95-ms 256.0 signatures/block 1277 signatures/s 0.001603 submit_p50-ms 0.008125 submit_p95-ms 386365 B/op 930 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_7tx_200ms_overlap-8 3 200155541 ns/op 0.02172 avg_cpu_cores 37.00 components/block 4.348 cpu-ms/block 0.5305 feed_lag_p50-ms 0.9950 feed_lag_p95-ms 3.507 mean_width 0.5883 ready_p50-ms 1.045 ready_p95-ms 0.1636 residual_p50-ms 0.1931 residual_p95-ms 256.0 signatures/block 1279 signatures/s 0.001372 submit_p50-ms 0.003166 submit_p95-ms 386365 B/op 930 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_7tx_200ms_overlap-8 3 200521630 ns/op 0.02820 avg_cpu_cores 37.00 components/block 5.655 cpu-ms/block 0.5292 feed_lag_p50-ms 0.9752 feed_lag_p95-ms 3.507 mean_width 0.5981 ready_p50-ms 1.088 ready_p95-ms 0.5204 residual_p50-ms 0.7328 residual_p95-ms 256.0 signatures/block 1277 signatures/s 0.001944 submit_p50-ms 0.01280 submit_p95-ms 386365 B/op 930 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_8tx_200ms_overlap-8 3 200734011 ns/op 0.01795 avg_cpu_cores 32.00 components/block 3.602 cpu-ms/block 0.6295 feed_lag_p50-ms 1.028 feed_lag_p95-ms 4.000 mean_width 0.6833 ready_p50-ms 1.083 ready_p95-ms 0.6833 residual_p50-ms 0.8221 residual_p95-ms 256.0 signatures/block 1275 signatures/s 0.001363 submit_p50-ms 0.002945 submit_p95-ms 379437 B/op 875 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_8tx_200ms_overlap-8 3 201052107 ns/op 0.02754 avg_cpu_cores 32.00 components/block 5.536 cpu-ms/block 0.6295 feed_lag_p50-ms 1.034 feed_lag_p95-ms 4.000 mean_width 0.7177 ready_p50-ms 1.115 ready_p95-ms 1.048 residual_p50-ms 1.051 residual_p95-ms 256.0 signatures/block 1273 signatures/s 0.001994 submit_p50-ms 0.02698 submit_p95-ms 379437 B/op 875 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_8tx_200ms_overlap-8 3 200711809 ns/op 0.01892 avg_cpu_cores 32.00 components/block 3.797 cpu-ms/block 0.6447 feed_lag_p50-ms 1.037 feed_lag_p95-ms 4.000 mean_width 0.6915 ready_p50-ms 1.093 ready_p95-ms 0.6820 residual_p50-ms 0.8024 residual_p95-ms 256.0 signatures/block 1275 signatures/s 0.001643 submit_p50-ms 0.003516 submit_p95-ms 379624 B/op 877 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/catchup-8 3 45816383 ns/op 4.073 avg_cpu_cores 1.000 components/block 186.6 cpu-ms/block 0.0002000 feed_lag_p50-ms 0.0004910 feed_lag_p95-ms 8.000 mean_width 45.06 ready_p50-ms 48.98 ready_p95-ms 45.06 residual_p50-ms 48.98 residual_p95-ms 33760 signatures/block 736854 signatures/s 0.004338 submit_p50-ms 0.004658 submit_p95-ms 44570576 B/op 67557 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/catchup-8 3 56218950 ns/op 3.966 avg_cpu_cores 1.000 components/block 223.0 cpu-ms/block 0.0005010 feed_lag_p50-ms 0.0005510 feed_lag_p95-ms 8.000 mean_width 55.44 ready_p50-ms 58.07 ready_p95-ms 55.44 residual_p50-ms 58.07 residual_p95-ms 33760 signatures/block 600509 signatures/s 0.006312 submit_p50-ms 0.01470 submit_p95-ms 44524608 B/op 67553 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/catchup-8 3 61550045 ns/op 3.871 avg_cpu_cores 1.000 components/block 238.3 cpu-ms/block 0.0003010 feed_lag_p50-ms 0.0009120 feed_lag_p95-ms 8.000 mean_width 60.95 ready_p50-ms 62.93 ready_p95-ms 60.95 residual_p50-ms 62.93 residual_p95-ms 33760 signatures/block 548497 signatures/s 0.007384 submit_p50-ms 0.01248 submit_p95-ms 44662146 B/op 67562 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/tip_200ms_after_complete-8 3 248039377 ns/op 0.7751 avg_cpu_cores 689.0 components/block 192.2 cpu-ms/block 100.4 feed_lag_p50-ms 190.5 feed_lag_p95-ms 8.000 mean_width 148.0 ready_p50-ms 238.1 ready_p95-ms 46.77 residual_p50-ms 54.35 residual_p95-ms 33760 signatures/block 136107 signatures/s 0.01645 submit_p50-ms 0.04039 submit_p95-ms 44398781 B/op 67546 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/tip_200ms_after_complete-8 3 244855756 ns/op 0.7431 avg_cpu_cores 689.0 components/block 182.0 cpu-ms/block 100.4 feed_lag_p50-ms 190.5 feed_lag_p95-ms 8.000 mean_width 144.8 ready_p50-ms 234.9 ready_p95-ms 44.62 residual_p50-ms 45.36 residual_p95-ms 33760 signatures/block 137877 signatures/s 0.01356 submit_p50-ms 0.01404 submit_p95-ms 44627629 B/op 67556 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/tip_200ms_after_complete-8 3 246211332 ns/op 0.7541 avg_cpu_cores 689.0 components/block 185.7 cpu-ms/block 100.4 feed_lag_p50-ms 190.5 feed_lag_p95-ms 8.000 mean_width 146.2 ready_p50-ms 236.3 ready_p95-ms 46.81 residual_p50-ms 47.71 residual_p95-ms 33760 signatures/block 137118 signatures/s 0.01444 submit_p50-ms 0.01546 submit_p95-ms 44490194 B/op 67549 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/tip_200ms_overlap-8 3 200653199 ns/op 1.155 avg_cpu_cores 689.0 components/block 231.7 cpu-ms/block 0.2466 feed_lag_p50-ms 0.7849 feed_lag_p95-ms 7.001 mean_width 0.5281 ready_p50-ms 1.123 ready_p95-ms 0.6670 residual_p50-ms 0.9134 residual_p95-ms 33760 signatures/block 168250 signatures/s 0.0008210 submit_p50-ms 0.002454 submit_p95-ms 45684682 B/op 75142 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/tip_200ms_overlap-8 3 200414118 ns/op 0.9499 avg_cpu_cores 689.0 components/block 190.4 cpu-ms/block 0.2950 feed_lag_p50-ms 0.7820 feed_lag_p95-ms 7.001 mean_width 0.5114 ready_p50-ms 1.017 ready_p95-ms 0.2302 residual_p50-ms 0.8310 residual_p95-ms 33760 signatures/block 168451 signatures/s 0.0005310 submit_p50-ms 0.001473 submit_p95-ms 45775912 B/op 75143 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/tip_200ms_overlap-8 3 200256049 ns/op 0.9622 avg_cpu_cores 689.0 components/block 192.7 cpu-ms/block 0.2963 feed_lag_p50-ms 0.7859 feed_lag_p95-ms 7.001 mean_width 0.5143 ready_p50-ms 1.015 ready_p95-ms 0.1938 residual_p50-ms 0.4353 residual_p95-ms 33760 signatures/block 168584 signatures/s 0.0005610 submit_p50-ms 0.001704 submit_p95-ms 45825840 B/op 75182 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_4tx_200ms_overlap-8 3 200387649 ns/op 0.02538 avg_cpu_cores 64.00 components/block 5.086 cpu-ms/block 0.5414 feed_lag_p50-ms 0.9647 feed_lag_p95-ms 4.000 mean_width 0.5928 ready_p50-ms 1.033 ready_p95-ms 0.5171 residual_p50-ms 0.5489 residual_p95-ms 256.0 signatures/block 1278 signatures/s 0.001523 submit_p50-ms 0.005641 submit_p95-ms 439648 B/op 1230 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_4tx_200ms_overlap-8 3 200883060 ns/op 0.03008 avg_cpu_cores 64.00 components/block 6.043 cpu-ms/block 0.5974 feed_lag_p50-ms 0.9731 feed_lag_p95-ms 4.000 mean_width 0.6814 ready_p50-ms 1.066 ready_p95-ms 1.116 residual_p50-ms 1.146 residual_p95-ms 256.0 signatures/block 1274 signatures/s 0.002054 submit_p50-ms 0.007835 submit_p95-ms 439349 B/op 1228 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_4tx_200ms_overlap-8 3 200468704 ns/op 0.02652 avg_cpu_cores 64.00 components/block 5.317 cpu-ms/block 0.5614 feed_lag_p50-ms 0.9687 feed_lag_p95-ms 4.000 mean_width 0.6253 ready_p50-ms 1.041 ready_p95-ms 0.3181 residual_p50-ms 0.7522 residual_p95-ms 256.0 signatures/block 1277 signatures/s 0.002074 submit_p50-ms 0.004910 submit_p95-ms 439341 B/op 1227 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_7tx_200ms_overlap-8 3 200580146 ns/op 0.02269 avg_cpu_cores 37.00 components/block 4.551 cpu-ms/block 0.5230 feed_lag_p50-ms 1.004 feed_lag_p95-ms 6.919 mean_width 0.6231 ready_p50-ms 1.099 ready_p95-ms 0.4480 residual_p50-ms 1.093 residual_p95-ms 256.0 signatures/block 1276 signatures/s 0.001423 submit_p50-ms 0.004990 submit_p95-ms 395837 B/op 930 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_7tx_200ms_overlap-8 3 200704543 ns/op 0.02296 avg_cpu_cores 37.00 components/block 4.607 cpu-ms/block 0.5328 feed_lag_p50-ms 1.022 feed_lag_p95-ms 6.919 mean_width 0.6489 ready_p50-ms 1.107 ready_p95-ms 0.6480 residual_p50-ms 1.079 residual_p95-ms 256.0 signatures/block 1276 signatures/s 0.001783 submit_p50-ms 0.008186 submit_p95-ms 395837 B/op 930 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_7tx_200ms_overlap-8 3 200601736 ns/op 0.02604 avg_cpu_cores 37.00 components/block 5.224 cpu-ms/block 0.5685 feed_lag_p50-ms 1.009 feed_lag_p95-ms 6.919 mean_width 0.6868 ready_p50-ms 1.128 ready_p95-ms 0.6345 residual_p50-ms 0.9120 residual_p95-ms 256.0 signatures/block 1276 signatures/s 0.001794 submit_p50-ms 0.01362 submit_p95-ms 395912 B/op 931 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_8tx_200ms_overlap-8 3 200649189 ns/op 0.02026 avg_cpu_cores 32.00 components/block 4.065 cpu-ms/block 0.5870 feed_lag_p50-ms 1.023 feed_lag_p95-ms 8.000 mean_width 0.6574 ready_p50-ms 1.106 ready_p95-ms 0.5239 residual_p50-ms 0.9372 residual_p95-ms 256.0 signatures/block 1276 signatures/s 0.002274 submit_p50-ms 0.02225 submit_p95-ms 387637 B/op 876 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_8tx_200ms_overlap-8 3 200762942 ns/op 0.01440 avg_cpu_cores 32.00 components/block 2.890 cpu-ms/block 0.5812 feed_lag_p50-ms 1.006 feed_lag_p95-ms 8.000 mean_width 0.6570 ready_p50-ms 1.083 ready_p95-ms 0.6634 residual_p50-ms 1.040 residual_p95-ms 256.0 signatures/block 1275 signatures/s 0.001613 submit_p50-ms 0.01741 submit_p95-ms 387629 B/op 875 allocs/op +BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_8tx_200ms_overlap-8 3 200762972 ns/op 0.01549 avg_cpu_cores 32.00 components/block 3.109 cpu-ms/block 0.6425 feed_lag_p50-ms 1.017 feed_lag_p95-ms 8.000 mean_width 0.6983 ready_p50-ms 1.092 ready_p95-ms 0.6307 residual_p50-ms 1.064 residual_p95-ms 256.0 signatures/block 1275 signatures/s 0.001583 submit_p50-ms 0.01700 submit_p95-ms 387629 B/op 875 allocs/op +PASS diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/health-after-final.txt b/docs/results/sigverify-streaming/2026-09-12-zen5/health-after-final.txt new file mode 100644 index 00000000..2cc76eee --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/health-after-final.txt @@ -0,0 +1,4 @@ +ActiveState=active +SubState=running +MainPID=3343610 +ExecMainStartTimestamp=Sat 2026-09-12 05:41:10 UTC diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/local-replay.log b/docs/results/sigverify-streaming/2026-09-12-zen5/local-replay.log new file mode 100644 index 00000000..88af8e8a --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/local-replay.log @@ -0,0 +1 @@ +ok github.com/Overclock-Validator/mithril/pkg/replay 1.251s diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/local-turbine-race.log b/docs/results/sigverify-streaming/2026-09-12-zen5/local-turbine-race.log new file mode 100644 index 00000000..8ff7b29e --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/local-turbine-race.log @@ -0,0 +1 @@ +ok github.com/Overclock-Validator/mithril/pkg/turbine 8.226s diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/local-unit.log b/docs/results/sigverify-streaming/2026-09-12-zen5/local-unit.log new file mode 100644 index 00000000..5d57f161 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/local-unit.log @@ -0,0 +1,7 @@ +ok github.com/Overclock-Validator/mithril/pkg/turbine 1.442s +ok github.com/Overclock-Validator/mithril/pkg/sigverify (cached) +ok github.com/Overclock-Validator/mithril/pkg/txverify (cached) +ok github.com/Overclock-Validator/mithril/pkg/tpu/sigverify (cached) +ok github.com/Overclock-Validator/mithril/cmd/mithril/node 0.653s +ok github.com/Overclock-Validator/mithril/pkg/block (cached) +ok github.com/Overclock-Validator/mithril/pkg/statsd (cached) diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/native-final-race.log b/docs/results/sigverify-streaming/2026-09-12-zen5/native-final-race.log new file mode 100644 index 00000000..356c3b4b --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/native-final-race.log @@ -0,0 +1 @@ +ok github.com/Overclock-Validator/mithril/pkg/turbine 2.051s diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/native-final-unit.log b/docs/results/sigverify-streaming/2026-09-12-zen5/native-final-unit.log new file mode 100644 index 00000000..cf82467a --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/native-final-unit.log @@ -0,0 +1,6 @@ +ok github.com/Overclock-Validator/mithril/pkg/turbine 0.966s +ok github.com/Overclock-Validator/mithril/pkg/sigverify 0.634s +ok github.com/Overclock-Validator/mithril/pkg/txverify 0.003s +ok github.com/Overclock-Validator/mithril/pkg/block 0.025s +ok github.com/Overclock-Validator/mithril/pkg/statsd 0.003s +ok github.com/Overclock-Validator/mithril/cmd/mithril/node 0.029s diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/pool-comparison.json b/docs/results/sigverify-streaming/2026-09-12-zen5/pool-comparison.json new file mode 100644 index 00000000..e90d1508 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/pool-comparison.json @@ -0,0 +1,5503 @@ +{ + "description": "Medians across repeated samples; captured rows aggregate three equal-transaction-count blocks. Arrival schedules are synthetic. Process CPU excludes the separate execution probe.", + "rows": [ + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/catchup-8", + "iterations": 3, + "ns/op": 156715891.0, + "avg_cpu_cores": 2.043, + "components/block": 1.0, + "cpu-ms/block": 320.2, + "feed_lag_p50-ms": 0.000271, + "feed_lag_p95-ms": 0.001282, + "mean_width": 4.0, + "ready_p50-ms": 158.0, + "ready_p95-ms": 164.2, + "residual_p50-ms": 158.0, + "residual_p95-ms": 164.2, + "signatures/block": 33760.0, + "signatures/s": 215422.0, + "submit_p50-ms": 0.01062, + "submit_p95-ms": 0.02492, + "B/op": 7023138.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 2, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/catchup-8", + "iterations": 3, + "ns/op": 155986070.0, + "avg_cpu_cores": 2.047, + "components/block": 1.0, + "cpu-ms/block": 319.2, + "feed_lag_p50-ms": 0.000251, + "feed_lag_p95-ms": 0.000781, + "mean_width": 4.0, + "ready_p50-ms": 151.6, + "ready_p95-ms": 171.1, + "residual_p50-ms": 151.6, + "residual_p95-ms": 171.1, + "signatures/block": 33760.0, + "signatures/s": 216430.0, + "submit_p50-ms": 0.006532, + "submit_p95-ms": 0.007404, + "B/op": 7023144.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 2, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/catchup-8", + "iterations": 3, + "ns/op": 145764753.0, + "avg_cpu_cores": 2.047, + "components/block": 1.0, + "cpu-ms/block": 298.4, + "feed_lag_p50-ms": 0.000281, + "feed_lag_p95-ms": 0.000681, + "mean_width": 4.0, + "ready_p50-ms": 145.4, + "ready_p95-ms": 146.6, + "residual_p50-ms": 145.4, + "residual_p95-ms": 146.6, + "signatures/block": 33760.0, + "signatures/s": 231606.0, + "submit_p50-ms": 0.004178, + "submit_p95-ms": 0.007975, + "B/op": 7023133.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 2, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 344523723.0, + "avg_cpu_cores": 0.8561, + "components/block": 126.0, + "cpu-ms/block": 295.0, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 191.4, + "mean_width": 4.0, + "ready_p50-ms": 244.4, + "ready_p95-ms": 335.6, + "residual_p50-ms": 144.7, + "residual_p95-ms": 145.2, + "signatures/block": 33760.0, + "signatures/s": 97990.0, + "submit_p50-ms": 0.01582, + "submit_p95-ms": 0.01597, + "B/op": 7033338.0, + "allocs/op": 67539.0, + "source": "captured_2622581", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 352992831.0, + "avg_cpu_cores": 0.8764, + "components/block": 126.0, + "cpu-ms/block": 309.4, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 191.4, + "mean_width": 4.0, + "ready_p50-ms": 252.4, + "ready_p95-ms": 343.6, + "residual_p50-ms": 144.6, + "residual_p95-ms": 170.7, + "signatures/block": 33760.0, + "signatures/s": 95639.0, + "submit_p50-ms": 0.0175, + "submit_p95-ms": 0.01914, + "B/op": 7033616.0, + "allocs/op": 67540.0, + "source": "captured_2622581", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 363417787.0, + "avg_cpu_cores": 0.9118, + "components/block": 126.0, + "cpu-ms/block": 331.4, + "feed_lag_p50-ms": 99.82, + "feed_lag_p95-ms": 191.0, + "mean_width": 4.0, + "ready_p50-ms": 263.0, + "ready_p95-ms": 355.8, + "residual_p50-ms": 163.8, + "residual_p95-ms": 175.6, + "signatures/block": 33760.0, + "signatures/s": 92896.0, + "submit_p50-ms": 0.02098, + "submit_p95-ms": 0.02324, + "B/op": 7033530.0, + "allocs/op": 67539.0, + "source": "captured_2622581", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201111285.0, + "avg_cpu_cores": 1.522, + "components/block": 126.0, + "cpu-ms/block": 306.1, + "feed_lag_p50-ms": 0.03627, + "feed_lag_p95-ms": 0.8607, + "mean_width": 3.956, + "ready_p50-ms": 1.602, + "ready_p95-ms": 2.473, + "residual_p50-ms": 1.08, + "residual_p95-ms": 1.219, + "signatures/block": 33760.0, + "signatures/s": 167867.0, + "submit_p50-ms": 0.001032, + "submit_p95-ms": 0.003166, + "B/op": 7165506.0, + "allocs/op": 68945.0, + "source": "captured_2622581", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201378639.0, + "avg_cpu_cores": 1.498, + "components/block": 126.0, + "cpu-ms/block": 301.7, + "feed_lag_p50-ms": 0.2205, + "feed_lag_p95-ms": 0.8775, + "mean_width": 3.956, + "ready_p50-ms": 1.582, + "ready_p95-ms": 2.532, + "residual_p50-ms": 1.361, + "residual_p95-ms": 1.407, + "signatures/block": 33760.0, + "signatures/s": 167644.0, + "submit_p50-ms": 0.001123, + "submit_p95-ms": 0.002805, + "B/op": 7167869.0, + "allocs/op": 68945.0, + "source": "captured_2622581", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201155070.0, + "avg_cpu_cores": 1.486, + "components/block": 126.0, + "cpu-ms/block": 299.0, + "feed_lag_p50-ms": 0.05468, + "feed_lag_p95-ms": 0.831, + "mean_width": 3.956, + "ready_p50-ms": 1.579, + "ready_p95-ms": 2.456, + "residual_p50-ms": 1.334, + "residual_p95-ms": 1.38, + "signatures/block": 33760.0, + "signatures/s": 167831.0, + "submit_p50-ms": 0.001042, + "submit_p95-ms": 0.002434, + "B/op": 7159269.0, + "allocs/op": 68921.0, + "source": "captured_2622581", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/catchup-8", + "iterations": 3, + "ns/op": 74916706.0, + "avg_cpu_cores": 2.041, + "components/block": 1.0, + "cpu-ms/block": 152.9, + "feed_lag_p50-ms": 0.000251, + "feed_lag_p95-ms": 0.000481, + "mean_width": 8.0, + "ready_p50-ms": 74.9, + "ready_p95-ms": 75.95, + "residual_p50-ms": 74.9, + "residual_p95-ms": 75.95, + "signatures/block": 33760.0, + "signatures/s": 450634.0, + "submit_p50-ms": 0.004738, + "submit_p95-ms": 0.006292, + "B/op": 7023266.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 2, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/catchup-8", + "iterations": 3, + "ns/op": 75787979.0, + "avg_cpu_cores": 2.041, + "components/block": 1.0, + "cpu-ms/block": 154.7, + "feed_lag_p50-ms": 0.00039, + "feed_lag_p95-ms": 0.000531, + "mean_width": 8.0, + "ready_p50-ms": 75.44, + "ready_p95-ms": 76.9, + "residual_p50-ms": 75.44, + "residual_p95-ms": 76.9, + "signatures/block": 33760.0, + "signatures/s": 445453.0, + "submit_p50-ms": 0.006492, + "submit_p95-ms": 0.006763, + "B/op": 7023258.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 2, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/catchup-8", + "iterations": 3, + "ns/op": 83724900.0, + "avg_cpu_cores": 2.025, + "components/block": 1.0, + "cpu-ms/block": 169.5, + "feed_lag_p50-ms": 0.00033, + "feed_lag_p95-ms": 0.000431, + "mean_width": 8.0, + "ready_p50-ms": 86.47, + "ready_p95-ms": 87.41, + "residual_p50-ms": 86.47, + "residual_p95-ms": 87.41, + "signatures/block": 33760.0, + "signatures/s": 403225.0, + "submit_p50-ms": 0.007554, + "submit_p95-ms": 0.007595, + "B/op": 7023253.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 2, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 275675193.0, + "avg_cpu_cores": 0.5545, + "components/block": 126.0, + "cpu-ms/block": 152.9, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 191.4, + "mean_width": 8.0, + "ready_p50-ms": 175.6, + "ready_p95-ms": 266.8, + "residual_p50-ms": 75.25, + "residual_p95-ms": 76.98, + "signatures/block": 33760.0, + "signatures/s": 122463.0, + "submit_p50-ms": 0.01418, + "submit_p95-ms": 0.01462, + "B/op": 7033472.0, + "allocs/op": 67539.0, + "source": "captured_2622581", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 288578162.0, + "avg_cpu_cores": 0.6196, + "components/block": 126.0, + "cpu-ms/block": 178.8, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 191.4, + "mean_width": 8.0, + "ready_p50-ms": 188.3, + "ready_p95-ms": 279.5, + "residual_p50-ms": 86.33, + "residual_p95-ms": 98.29, + "signatures/block": 33760.0, + "signatures/s": 116987.0, + "submit_p50-ms": 0.01635, + "submit_p95-ms": 0.01759, + "B/op": 7033461.0, + "allocs/op": 67539.0, + "source": "captured_2622581", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 275152604.0, + "avg_cpu_cores": 0.5544, + "components/block": 126.0, + "cpu-ms/block": 152.5, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 8.0, + "ready_p50-ms": 175.0, + "ready_p95-ms": 266.2, + "residual_p50-ms": 75.23, + "residual_p95-ms": 76.0, + "signatures/block": 33760.0, + "signatures/s": 122696.0, + "submit_p50-ms": 0.01576, + "submit_p95-ms": 0.01683, + "B/op": 7079245.0, + "allocs/op": 67541.0, + "source": "captured_2622581", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201070435.0, + "avg_cpu_cores": 0.8694, + "components/block": 126.0, + "cpu-ms/block": 174.8, + "feed_lag_p50-ms": 0.4408, + "feed_lag_p95-ms": 0.9889, + "mean_width": 7.912, + "ready_p50-ms": 1.219, + "ready_p95-ms": 1.939, + "residual_p50-ms": 1.278, + "residual_p95-ms": 1.368, + "signatures/block": 33760.0, + "signatures/s": 167901.0, + "submit_p50-ms": 0.001523, + "submit_p95-ms": 0.005731, + "B/op": 7175717.0, + "allocs/op": 68921.0, + "source": "captured_2622581", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201082624.0, + "avg_cpu_cores": 0.7981, + "components/block": 126.0, + "cpu-ms/block": 160.5, + "feed_lag_p50-ms": 0.5138, + "feed_lag_p95-ms": 1.002, + "mean_width": 7.912, + "ready_p50-ms": 1.116, + "ready_p95-ms": 1.674, + "residual_p50-ms": 1.053, + "residual_p95-ms": 1.128, + "signatures/block": 33760.0, + "signatures/s": 167891.0, + "submit_p50-ms": 0.001002, + "submit_p95-ms": 0.002716, + "B/op": 7175933.0, + "allocs/op": 68923.0, + "source": "captured_2622581", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201301027.0, + "avg_cpu_cores": 0.796, + "components/block": 126.0, + "cpu-ms/block": 160.2, + "feed_lag_p50-ms": 0.5253, + "feed_lag_p95-ms": 0.9638, + "mean_width": 7.912, + "ready_p50-ms": 1.16, + "ready_p95-ms": 1.61, + "residual_p50-ms": 1.264, + "residual_p95-ms": 1.409, + "signatures/block": 33760.0, + "signatures/s": 167709.0, + "submit_p50-ms": 0.001052, + "submit_p95-ms": 0.002464, + "B/op": 7176285.0, + "allocs/op": 68929.0, + "source": "captured_2622581", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/catchup-8", + "iterations": 3, + "ns/op": 79099409.0, + "avg_cpu_cores": 3.964, + "components/block": 1.0, + "cpu-ms/block": 313.6, + "feed_lag_p50-ms": 0.000261, + "feed_lag_p95-ms": 0.000831, + "mean_width": 4.0, + "ready_p50-ms": 76.63, + "ready_p95-ms": 85.46, + "residual_p50-ms": 76.63, + "residual_p95-ms": 85.46, + "signatures/block": 33760.0, + "signatures/s": 426805.0, + "submit_p50-ms": 0.006302, + "submit_p95-ms": 0.006773, + "B/op": 7023410.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 4, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/catchup-8", + "iterations": 3, + "ns/op": 80922132.0, + "avg_cpu_cores": 3.958, + "components/block": 1.0, + "cpu-ms/block": 320.3, + "feed_lag_p50-ms": 0.0003, + "feed_lag_p95-ms": 0.000652, + "mean_width": 4.0, + "ready_p50-ms": 77.5, + "ready_p95-ms": 87.97, + "residual_p50-ms": 77.5, + "residual_p95-ms": 87.97, + "signatures/block": 33760.0, + "signatures/s": 417191.0, + "submit_p50-ms": 0.006211, + "submit_p95-ms": 0.01736, + "B/op": 7023410.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 4, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/catchup-8", + "iterations": 3, + "ns/op": 75579922.0, + "avg_cpu_cores": 4.026, + "components/block": 1.0, + "cpu-ms/block": 304.3, + "feed_lag_p50-ms": 0.000391, + "feed_lag_p95-ms": 0.000491, + "mean_width": 4.0, + "ready_p50-ms": 75.53, + "ready_p95-ms": 75.76, + "residual_p50-ms": 75.53, + "residual_p95-ms": 75.76, + "signatures/block": 33760.0, + "signatures/s": 446679.0, + "submit_p50-ms": 0.004758, + "submit_p95-ms": 0.00543, + "B/op": 7023405.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 4, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 277358928.0, + "avg_cpu_cores": 1.11, + "components/block": 126.0, + "cpu-ms/block": 307.9, + "feed_lag_p50-ms": 99.99, + "feed_lag_p95-ms": 191.2, + "mean_width": 4.0, + "ready_p50-ms": 176.8, + "ready_p95-ms": 268.0, + "residual_p50-ms": 76.98, + "residual_p95-ms": 79.21, + "signatures/block": 33760.0, + "signatures/s": 121720.0, + "submit_p50-ms": 0.01168, + "submit_p95-ms": 0.01332, + "B/op": 7033597.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 281786997.0, + "avg_cpu_cores": 1.134, + "components/block": 126.0, + "cpu-ms/block": 319.5, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 4.0, + "ready_p50-ms": 181.5, + "ready_p95-ms": 272.7, + "residual_p50-ms": 77.53, + "residual_p95-ms": 90.55, + "signatures/block": 33760.0, + "signatures/s": 119807.0, + "submit_p50-ms": 0.01547, + "submit_p95-ms": 0.017, + "B/op": 7033605.0, + "allocs/op": 67539.0, + "source": "captured_2622581", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 275851016.0, + "avg_cpu_cores": 1.1, + "components/block": 126.0, + "cpu-ms/block": 303.6, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 4.0, + "ready_p50-ms": 175.8, + "ready_p95-ms": 267.0, + "residual_p50-ms": 75.74, + "residual_p95-ms": 76.57, + "signatures/block": 33760.0, + "signatures/s": 122385.0, + "submit_p50-ms": 0.01534, + "submit_p95-ms": 0.01584, + "B/op": 7033605.0, + "allocs/op": 67539.0, + "source": "captured_2622581", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200706590.0, + "avg_cpu_cores": 1.498, + "components/block": 126.0, + "cpu-ms/block": 300.7, + "feed_lag_p50-ms": 0.5031, + "feed_lag_p95-ms": 0.9977, + "mean_width": 3.956, + "ready_p50-ms": 1.105, + "ready_p95-ms": 1.659, + "residual_p50-ms": 0.5523, + "residual_p95-ms": 1.056, + "signatures/block": 33760.0, + "signatures/s": 168206.0, + "submit_p50-ms": 0.001102, + "submit_p95-ms": 0.003366, + "B/op": 7200192.0, + "allocs/op": 68942.0, + "source": "captured_2622581", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200821431.0, + "avg_cpu_cores": 1.836, + "components/block": 126.0, + "cpu-ms/block": 368.8, + "feed_lag_p50-ms": 0.3622, + "feed_lag_p95-ms": 1.002, + "mean_width": 3.956, + "ready_p50-ms": 1.224, + "ready_p95-ms": 2.145, + "residual_p50-ms": 0.9866, + "residual_p95-ms": 1.055, + "signatures/block": 33760.0, + "signatures/s": 168110.0, + "submit_p50-ms": 0.002164, + "submit_p95-ms": 0.004518, + "B/op": 7200909.0, + "allocs/op": 68946.0, + "source": "captured_2622581", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201025437.0, + "avg_cpu_cores": 1.586, + "components/block": 126.0, + "cpu-ms/block": 318.8, + "feed_lag_p50-ms": 0.4136, + "feed_lag_p95-ms": 0.9729, + "mean_width": 3.956, + "ready_p50-ms": 1.214, + "ready_p95-ms": 1.677, + "residual_p50-ms": 1.053, + "residual_p95-ms": 1.325, + "signatures/block": 33760.0, + "signatures/s": 167939.0, + "submit_p50-ms": 0.001483, + "submit_p95-ms": 0.004278, + "B/op": 7195901.0, + "allocs/op": 68936.0, + "source": "captured_2622581", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/catchup-8", + "iterations": 3, + "ns/op": 38632690.0, + "avg_cpu_cores": 4.029, + "components/block": 1.0, + "cpu-ms/block": 155.7, + "feed_lag_p50-ms": 0.00017, + "feed_lag_p95-ms": 0.00046, + "mean_width": 8.0, + "ready_p50-ms": 38.5, + "ready_p95-ms": 38.98, + "residual_p50-ms": 38.5, + "residual_p95-ms": 38.98, + "signatures/block": 33760.0, + "signatures/s": 873871.0, + "submit_p50-ms": 0.004749, + "submit_p95-ms": 0.004749, + "B/op": 7023658.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 4, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/catchup-8", + "iterations": 3, + "ns/op": 38613093.0, + "avg_cpu_cores": 4.016, + "components/block": 1.0, + "cpu-ms/block": 155.1, + "feed_lag_p50-ms": 0.00026, + "feed_lag_p95-ms": 0.000591, + "mean_width": 8.0, + "ready_p50-ms": 38.29, + "ready_p95-ms": 39.67, + "residual_p50-ms": 38.29, + "residual_p95-ms": 39.67, + "signatures/block": 33760.0, + "signatures/s": 874315.0, + "submit_p50-ms": 0.004198, + "submit_p95-ms": 0.00515, + "B/op": 7023658.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 4, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/catchup-8", + "iterations": 3, + "ns/op": 40094161.0, + "avg_cpu_cores": 3.977, + "components/block": 1.0, + "cpu-ms/block": 159.5, + "feed_lag_p50-ms": 0.000241, + "feed_lag_p95-ms": 0.000391, + "mean_width": 8.0, + "ready_p50-ms": 40.29, + "ready_p95-ms": 40.86, + "residual_p50-ms": 40.29, + "residual_p95-ms": 40.86, + "signatures/block": 33760.0, + "signatures/s": 842018.0, + "submit_p50-ms": 0.004117, + "submit_p95-ms": 0.004779, + "B/op": 7023653.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 4, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 240210161.0, + "avg_cpu_cores": 0.6517, + "components/block": 126.0, + "cpu-ms/block": 156.5, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 191.4, + "mean_width": 8.0, + "ready_p50-ms": 139.7, + "ready_p95-ms": 230.9, + "residual_p50-ms": 39.96, + "residual_p95-ms": 41.7, + "signatures/block": 33760.0, + "signatures/s": 140544.0, + "submit_p50-ms": 0.01466, + "submit_p95-ms": 0.01629, + "B/op": 7033861.0, + "allocs/op": 67539.0, + "source": "captured_2622581", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 244046162.0, + "avg_cpu_cores": 0.6703, + "components/block": 126.0, + "cpu-ms/block": 163.6, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 8.0, + "ready_p50-ms": 143.7, + "ready_p95-ms": 234.9, + "residual_p50-ms": 41.63, + "residual_p95-ms": 50.78, + "signatures/block": 33760.0, + "signatures/s": 138334.0, + "submit_p50-ms": 0.01559, + "submit_p95-ms": 0.1937, + "B/op": 7033853.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 250358365.0, + "avg_cpu_cores": 0.7621, + "components/block": 126.0, + "cpu-ms/block": 190.8, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 8.0, + "ready_p50-ms": 150.0, + "ready_p95-ms": 241.2, + "residual_p50-ms": 50.84, + "residual_p95-ms": 52.16, + "signatures/block": 33760.0, + "signatures/s": 134847.0, + "submit_p50-ms": 0.01572, + "submit_p95-ms": 0.01687, + "B/op": 7033853.0, + "allocs/op": 67538.0, + "source": "captured_2622581", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200893667.0, + "avg_cpu_cores": 0.8816, + "components/block": 126.0, + "cpu-ms/block": 177.1, + "feed_lag_p50-ms": 0.4989, + "feed_lag_p95-ms": 0.9857, + "mean_width": 7.912, + "ready_p50-ms": 0.9093, + "ready_p95-ms": 1.428, + "residual_p50-ms": 0.8878, + "residual_p95-ms": 1.069, + "signatures/block": 33760.0, + "signatures/s": 168049.0, + "submit_p50-ms": 0.001713, + "submit_p95-ms": 0.00557, + "B/op": 7225490.0, + "allocs/op": 68918.0, + "source": "captured_2622581", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201019836.0, + "avg_cpu_cores": 0.8626, + "components/block": 126.0, + "cpu-ms/block": 173.4, + "feed_lag_p50-ms": 0.5209, + "feed_lag_p95-ms": 1.002, + "mean_width": 7.912, + "ready_p50-ms": 0.912, + "ready_p95-ms": 1.381, + "residual_p50-ms": 1.081, + "residual_p95-ms": 1.125, + "signatures/block": 33760.0, + "signatures/s": 167944.0, + "submit_p50-ms": 0.001563, + "submit_p95-ms": 0.004458, + "B/op": 7226130.0, + "allocs/op": 68919.0, + "source": "captured_2622581", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622581/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200749032.0, + "avg_cpu_cores": 0.881, + "components/block": 126.0, + "cpu-ms/block": 176.9, + "feed_lag_p50-ms": 0.5054, + "feed_lag_p95-ms": 0.9943, + "mean_width": 7.912, + "ready_p50-ms": 0.9516, + "ready_p95-ms": 1.431, + "residual_p50-ms": 0.582, + "residual_p95-ms": 1.073, + "signatures/block": 33760.0, + "signatures/s": 168170.0, + "submit_p50-ms": 0.001633, + "submit_p95-ms": 0.00569, + "B/op": 7226581.0, + "allocs/op": 68928.0, + "source": "captured_2622581", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/catchup-8", + "iterations": 3, + "ns/op": 155918266.0, + "avg_cpu_cores": 2.038, + "components/block": 1.0, + "cpu-ms/block": 317.8, + "feed_lag_p50-ms": 0.000401, + "feed_lag_p95-ms": 0.000491, + "mean_width": 4.0, + "ready_p50-ms": 156.2, + "ready_p95-ms": 157.5, + "residual_p50-ms": 156.2, + "residual_p95-ms": 157.5, + "signatures/block": 33760.0, + "signatures/s": 216524.0, + "submit_p50-ms": 0.007043, + "submit_p95-ms": 0.007063, + "B/op": 7023138.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 2, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/catchup-8", + "iterations": 3, + "ns/op": 144724837.0, + "avg_cpu_cores": 2.047, + "components/block": 1.0, + "cpu-ms/block": 296.2, + "feed_lag_p50-ms": 0.000241, + "feed_lag_p95-ms": 0.000461, + "mean_width": 4.0, + "ready_p50-ms": 144.7, + "ready_p95-ms": 145.3, + "residual_p50-ms": 144.7, + "residual_p95-ms": 145.3, + "signatures/block": 33760.0, + "signatures/s": 233270.0, + "submit_p50-ms": 0.004688, + "submit_p95-ms": 0.006221, + "B/op": 7023133.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 2, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/catchup-8", + "iterations": 3, + "ns/op": 167371225.0, + "avg_cpu_cores": 2.049, + "components/block": 1.0, + "cpu-ms/block": 343.0, + "feed_lag_p50-ms": 0.00027, + "feed_lag_p95-ms": 0.000781, + "mean_width": 4.0, + "ready_p50-ms": 168.7, + "ready_p95-ms": 179.5, + "residual_p50-ms": 168.7, + "residual_p95-ms": 179.5, + "signatures/block": 33760.0, + "signatures/s": 201707.0, + "submit_p50-ms": 0.005831, + "submit_p95-ms": 0.006833, + "B/op": 7023133.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 2, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 348985373.0, + "avg_cpu_cores": 0.8724, + "components/block": 126.0, + "cpu-ms/block": 304.4, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 4.0, + "ready_p50-ms": 248.9, + "ready_p95-ms": 340.1, + "residual_p50-ms": 150.1, + "residual_p95-ms": 152.0, + "signatures/block": 33760.0, + "signatures/s": 96738.0, + "submit_p50-ms": 0.01628, + "submit_p95-ms": 0.01804, + "B/op": 7033333.0, + "allocs/op": 67539.0, + "source": "captured_2622653", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 353413098.0, + "avg_cpu_cores": 0.8855, + "components/block": 126.0, + "cpu-ms/block": 312.9, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 4.0, + "ready_p50-ms": 253.3, + "ready_p95-ms": 344.5, + "residual_p50-ms": 155.7, + "residual_p95-ms": 158.2, + "signatures/block": 33760.0, + "signatures/s": 95526.0, + "submit_p50-ms": 0.02336, + "submit_p95-ms": 0.03805, + "B/op": 7079125.0, + "allocs/op": 67541.0, + "source": "captured_2622653", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 349811570.0, + "avg_cpu_cores": 0.8763, + "components/block": 126.0, + "cpu-ms/block": 306.6, + "feed_lag_p50-ms": 99.8, + "feed_lag_p95-ms": 191.0, + "mean_width": 4.0, + "ready_p50-ms": 249.2, + "ready_p95-ms": 340.4, + "residual_p50-ms": 151.6, + "residual_p95-ms": 153.0, + "signatures/block": 33760.0, + "signatures/s": 96509.0, + "submit_p50-ms": 0.01705, + "submit_p95-ms": 0.01774, + "B/op": 7033333.0, + "allocs/op": 67539.0, + "source": "captured_2622653", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200998043.0, + "avg_cpu_cores": 1.604, + "components/block": 126.0, + "cpu-ms/block": 322.3, + "feed_lag_p50-ms": 0.03342, + "feed_lag_p95-ms": 0.8444, + "mean_width": 3.956, + "ready_p50-ms": 1.732, + "ready_p95-ms": 3.31, + "residual_p50-ms": 0.7673, + "residual_p95-ms": 1.469, + "signatures/block": 33760.0, + "signatures/s": 167962.0, + "submit_p50-ms": 0.001633, + "submit_p95-ms": 0.004388, + "B/op": 7159493.0, + "allocs/op": 68923.0, + "source": "captured_2622653", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200881915.0, + "avg_cpu_cores": 1.491, + "components/block": 126.0, + "cpu-ms/block": 299.4, + "feed_lag_p50-ms": 0.2175, + "feed_lag_p95-ms": 0.8653, + "mean_width": 3.956, + "ready_p50-ms": 1.578, + "ready_p95-ms": 2.468, + "residual_p50-ms": 0.9112, + "residual_p95-ms": 1.054, + "signatures/block": 33760.0, + "signatures/s": 168059.0, + "submit_p50-ms": 0.001072, + "submit_p95-ms": 0.002876, + "B/op": 7159410.0, + "allocs/op": 68922.0, + "source": "captured_2622653", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200919959.0, + "avg_cpu_cores": 1.509, + "components/block": 126.0, + "cpu-ms/block": 303.1, + "feed_lag_p50-ms": 0.04134, + "feed_lag_p95-ms": 0.8647, + "mean_width": 3.956, + "ready_p50-ms": 1.588, + "ready_p95-ms": 2.515, + "residual_p50-ms": 0.6775, + "residual_p95-ms": 1.408, + "signatures/block": 33760.0, + "signatures/s": 168027.0, + "submit_p50-ms": 0.001152, + "submit_p95-ms": 0.002685, + "B/op": 7160456.0, + "allocs/op": 68931.0, + "source": "captured_2622653", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/catchup-8", + "iterations": 3, + "ns/op": 78536665.0, + "avg_cpu_cores": 2.043, + "components/block": 1.0, + "cpu-ms/block": 160.4, + "feed_lag_p50-ms": 0.000201, + "feed_lag_p95-ms": 0.001353, + "mean_width": 8.0, + "ready_p50-ms": 76.4, + "ready_p95-ms": 84.11, + "residual_p50-ms": 76.4, + "residual_p95-ms": 84.11, + "signatures/block": 33760.0, + "signatures/s": 429863.0, + "submit_p50-ms": 0.005971, + "submit_p95-ms": 0.01674, + "B/op": 7023253.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 2, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/catchup-8", + "iterations": 3, + "ns/op": 74931420.0, + "avg_cpu_cores": 2.045, + "components/block": 1.0, + "cpu-ms/block": 153.2, + "feed_lag_p50-ms": 0.000261, + "feed_lag_p95-ms": 0.000491, + "mean_width": 8.0, + "ready_p50-ms": 73.86, + "ready_p95-ms": 77.19, + "residual_p50-ms": 73.86, + "residual_p95-ms": 77.19, + "signatures/block": 33760.0, + "signatures/s": 450545.0, + "submit_p50-ms": 0.00553, + "submit_p95-ms": 0.005611, + "B/op": 7069050.0, + "allocs/op": 67540.0, + "source": "captured_2622653", + "workers": 2, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/catchup-8", + "iterations": 3, + "ns/op": 74095918.0, + "avg_cpu_cores": 2.048, + "components/block": 1.0, + "cpu-ms/block": 151.7, + "feed_lag_p50-ms": 0.000261, + "feed_lag_p95-ms": 0.000551, + "mean_width": 8.0, + "ready_p50-ms": 74.05, + "ready_p95-ms": 74.35, + "residual_p50-ms": 74.05, + "residual_p95-ms": 74.35, + "signatures/block": 33760.0, + "signatures/s": 455626.0, + "submit_p50-ms": 0.00504, + "submit_p95-ms": 0.005761, + "B/op": 7023266.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 2, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 276093363.0, + "avg_cpu_cores": 0.5578, + "components/block": 126.0, + "cpu-ms/block": 154.0, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 8.0, + "ready_p50-ms": 176.0, + "ready_p95-ms": 267.2, + "residual_p50-ms": 75.77, + "residual_p95-ms": 77.27, + "signatures/block": 33760.0, + "signatures/s": 122277.0, + "submit_p50-ms": 0.0156, + "submit_p95-ms": 0.01629, + "B/op": 7033453.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 275962471.0, + "avg_cpu_cores": 0.5557, + "components/block": 126.0, + "cpu-ms/block": 153.3, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 191.4, + "mean_width": 8.0, + "ready_p50-ms": 175.6, + "ready_p95-ms": 266.8, + "residual_p50-ms": 75.18, + "residual_p95-ms": 77.82, + "signatures/block": 33760.0, + "signatures/s": 122335.0, + "submit_p50-ms": 0.01006, + "submit_p95-ms": 0.01518, + "B/op": 7033461.0, + "allocs/op": 67539.0, + "source": "captured_2622653", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 276459279.0, + "avg_cpu_cores": 0.5617, + "components/block": 126.0, + "cpu-ms/block": 155.3, + "feed_lag_p50-ms": 99.96, + "feed_lag_p95-ms": 191.2, + "mean_width": 8.0, + "ready_p50-ms": 176.3, + "ready_p95-ms": 267.5, + "residual_p50-ms": 76.2, + "residual_p95-ms": 77.62, + "signatures/block": 33760.0, + "signatures/s": 122116.0, + "submit_p50-ms": 0.01565, + "submit_p95-ms": 0.02936, + "B/op": 7033461.0, + "allocs/op": 67539.0, + "source": "captured_2622653", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201053633.0, + "avg_cpu_cores": 0.7943, + "components/block": 126.0, + "cpu-ms/block": 159.7, + "feed_lag_p50-ms": 0.5123, + "feed_lag_p95-ms": 0.9677, + "mean_width": 7.912, + "ready_p50-ms": 1.131, + "ready_p95-ms": 1.609, + "residual_p50-ms": 1.133, + "residual_p95-ms": 1.338, + "signatures/block": 33760.0, + "signatures/s": 167915.0, + "submit_p50-ms": 0.000982, + "submit_p95-ms": 0.002364, + "B/op": 7175658.0, + "allocs/op": 68923.0, + "source": "captured_2622653", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200775689.0, + "avg_cpu_cores": 0.8035, + "components/block": 126.0, + "cpu-ms/block": 161.3, + "feed_lag_p50-ms": 0.4791, + "feed_lag_p95-ms": 0.9798, + "mean_width": 7.912, + "ready_p50-ms": 1.141, + "ready_p95-ms": 1.63, + "residual_p50-ms": 0.8427, + "residual_p95-ms": 1.052, + "signatures/block": 33760.0, + "signatures/s": 168148.0, + "submit_p50-ms": 0.001102, + "submit_p95-ms": 0.002976, + "B/op": 7179669.0, + "allocs/op": 68929.0, + "source": "captured_2622653", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200811376.0, + "avg_cpu_cores": 0.811, + "components/block": 126.0, + "cpu-ms/block": 162.9, + "feed_lag_p50-ms": 0.5077, + "feed_lag_p95-ms": 0.973, + "mean_width": 7.912, + "ready_p50-ms": 1.17, + "ready_p95-ms": 1.645, + "residual_p50-ms": 0.653, + "residual_p95-ms": 1.185, + "signatures/block": 33760.0, + "signatures/s": 168118.0, + "submit_p50-ms": 0.001042, + "submit_p95-ms": 0.003306, + "B/op": 7175690.0, + "allocs/op": 68923.0, + "source": "captured_2622653", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/catchup-8", + "iterations": 3, + "ns/op": 75328778.0, + "avg_cpu_cores": 4.02, + "components/block": 1.0, + "cpu-ms/block": 302.8, + "feed_lag_p50-ms": 0.000201, + "feed_lag_p95-ms": 0.000481, + "mean_width": 4.0, + "ready_p50-ms": 75.78, + "ready_p95-ms": 75.98, + "residual_p50-ms": 75.78, + "residual_p95-ms": 75.98, + "signatures/block": 33760.0, + "signatures/s": 448169.0, + "submit_p50-ms": 0.005461, + "submit_p95-ms": 0.006863, + "B/op": 7023397.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 4, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/catchup-8", + "iterations": 3, + "ns/op": 75774293.0, + "avg_cpu_cores": 4.044, + "components/block": 1.0, + "cpu-ms/block": 306.4, + "feed_lag_p50-ms": 0.00048, + "feed_lag_p95-ms": 0.000481, + "mean_width": 4.0, + "ready_p50-ms": 76.23, + "ready_p95-ms": 76.72, + "residual_p50-ms": 76.23, + "residual_p95-ms": 76.72, + "signatures/block": 33760.0, + "signatures/s": 445534.0, + "submit_p50-ms": 0.005711, + "submit_p95-ms": 0.006102, + "B/op": 7023402.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 4, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/catchup-8", + "iterations": 3, + "ns/op": 78168545.0, + "avg_cpu_cores": 3.951, + "components/block": 1.0, + "cpu-ms/block": 308.8, + "feed_lag_p50-ms": 0.00024, + "feed_lag_p95-ms": 0.000581, + "mean_width": 4.0, + "ready_p50-ms": 77.58, + "ready_p95-ms": 80.96, + "residual_p50-ms": 77.58, + "residual_p95-ms": 80.96, + "signatures/block": 33760.0, + "signatures/s": 431887.0, + "submit_p50-ms": 0.004168, + "submit_p95-ms": 0.00501, + "B/op": 7023397.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 4, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 275602430.0, + "avg_cpu_cores": 1.092, + "components/block": 126.0, + "cpu-ms/block": 301.1, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 191.4, + "mean_width": 4.0, + "ready_p50-ms": 175.7, + "ready_p95-ms": 266.9, + "residual_p50-ms": 75.17, + "residual_p95-ms": 76.46, + "signatures/block": 33760.0, + "signatures/s": 122495.0, + "submit_p50-ms": 0.01314, + "submit_p95-ms": 0.01613, + "B/op": 7033605.0, + "allocs/op": 67539.0, + "source": "captured_2622653", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 279023273.0, + "avg_cpu_cores": 1.12, + "components/block": 126.0, + "cpu-ms/block": 312.5, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 191.4, + "mean_width": 4.0, + "ready_p50-ms": 178.6, + "ready_p95-ms": 269.8, + "residual_p50-ms": 74.63, + "residual_p95-ms": 88.0, + "signatures/block": 33760.0, + "signatures/s": 120993.0, + "submit_p50-ms": 0.01526, + "submit_p95-ms": 0.018, + "B/op": 7033597.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 274951714.0, + "avg_cpu_cores": 1.09, + "components/block": 126.0, + "cpu-ms/block": 299.7, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 191.4, + "mean_width": 4.0, + "ready_p50-ms": 174.6, + "ready_p95-ms": 265.8, + "residual_p50-ms": 74.09, + "residual_p95-ms": 76.92, + "signatures/block": 33760.0, + "signatures/s": 122785.0, + "submit_p50-ms": 0.01396, + "submit_p95-ms": 0.01562, + "B/op": 7033605.0, + "allocs/op": 67539.0, + "source": "captured_2622653", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201098547.0, + "avg_cpu_cores": 1.493, + "components/block": 126.0, + "cpu-ms/block": 300.2, + "feed_lag_p50-ms": 0.5184, + "feed_lag_p95-ms": 0.981, + "mean_width": 3.956, + "ready_p50-ms": 1.117, + "ready_p95-ms": 1.603, + "residual_p50-ms": 1.053, + "residual_p95-ms": 1.298, + "signatures/block": 33760.0, + "signatures/s": 167878.0, + "submit_p50-ms": 0.001042, + "submit_p95-ms": 0.002425, + "B/op": 7193832.0, + "allocs/op": 68923.0, + "source": "captured_2622653", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200804503.0, + "avg_cpu_cores": 1.504, + "components/block": 126.0, + "cpu-ms/block": 302.0, + "feed_lag_p50-ms": 0.4528, + "feed_lag_p95-ms": 0.9812, + "mean_width": 3.956, + "ready_p50-ms": 1.073, + "ready_p95-ms": 1.596, + "residual_p50-ms": 0.6861, + "residual_p95-ms": 1.062, + "signatures/block": 33760.0, + "signatures/s": 168124.0, + "submit_p50-ms": 0.001172, + "submit_p95-ms": 0.003016, + "B/op": 7193242.0, + "allocs/op": 68918.0, + "source": "captured_2622653", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201170435.0, + "avg_cpu_cores": 1.496, + "components/block": 126.0, + "cpu-ms/block": 300.9, + "feed_lag_p50-ms": 0.5088, + "feed_lag_p95-ms": 0.9289, + "mean_width": 3.956, + "ready_p50-ms": 1.085, + "ready_p95-ms": 1.534, + "residual_p50-ms": 1.085, + "residual_p95-ms": 1.335, + "signatures/block": 33760.0, + "signatures/s": 167818.0, + "submit_p50-ms": 0.001072, + "submit_p95-ms": 0.002414, + "B/op": 7193242.0, + "allocs/op": 68918.0, + "source": "captured_2622653", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/catchup-8", + "iterations": 3, + "ns/op": 39479657.0, + "avg_cpu_cores": 3.975, + "components/block": 1.0, + "cpu-ms/block": 157.0, + "feed_lag_p50-ms": 0.000271, + "feed_lag_p95-ms": 0.000481, + "mean_width": 8.0, + "ready_p50-ms": 39.13, + "ready_p95-ms": 41.02, + "residual_p50-ms": 39.13, + "residual_p95-ms": 41.02, + "signatures/block": 33760.0, + "signatures/s": 855124.0, + "submit_p50-ms": 0.007464, + "submit_p95-ms": 0.008786, + "B/op": 7023658.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 4, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/catchup-8", + "iterations": 3, + "ns/op": 38341975.0, + "avg_cpu_cores": 4.014, + "components/block": 1.0, + "cpu-ms/block": 153.9, + "feed_lag_p50-ms": 0.000581, + "feed_lag_p95-ms": 0.000641, + "mean_width": 8.0, + "ready_p50-ms": 38.31, + "ready_p95-ms": 38.39, + "residual_p50-ms": 38.31, + "residual_p95-ms": 38.39, + "signatures/block": 33760.0, + "signatures/s": 880497.0, + "submit_p50-ms": 0.004508, + "submit_p95-ms": 0.004959, + "B/op": 7023666.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 4, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/catchup-8", + "iterations": 3, + "ns/op": 39294804.0, + "avg_cpu_cores": 4.04, + "components/block": 1.0, + "cpu-ms/block": 158.7, + "feed_lag_p50-ms": 0.000391, + "feed_lag_p95-ms": 0.000551, + "mean_width": 8.0, + "ready_p50-ms": 39.31, + "ready_p95-ms": 39.43, + "residual_p50-ms": 39.31, + "residual_p95-ms": 39.43, + "signatures/block": 33760.0, + "signatures/s": 859147.0, + "submit_p50-ms": 0.005029, + "submit_p95-ms": 0.005861, + "B/op": 7023658.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 4, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 241004536.0, + "avg_cpu_cores": 0.6672, + "components/block": 126.0, + "cpu-ms/block": 160.8, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 8.0, + "ready_p50-ms": 141.0, + "ready_p95-ms": 232.2, + "residual_p50-ms": 40.6, + "residual_p95-ms": 43.41, + "signatures/block": 33760.0, + "signatures/s": 140080.0, + "submit_p50-ms": 0.01375, + "submit_p95-ms": 0.01582, + "B/op": 7033853.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 240866782.0, + "avg_cpu_cores": 0.6463, + "components/block": 126.0, + "cpu-ms/block": 155.7, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 191.4, + "mean_width": 8.0, + "ready_p50-ms": 140.6, + "ready_p95-ms": 231.8, + "residual_p50-ms": 41.37, + "residual_p95-ms": 41.44, + "signatures/block": 33760.0, + "signatures/s": 140160.0, + "submit_p50-ms": 0.01499, + "submit_p95-ms": 0.01555, + "B/op": 7033853.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 245990041.0, + "avg_cpu_cores": 0.7043, + "components/block": 126.0, + "cpu-ms/block": 173.2, + "feed_lag_p50-ms": 99.92, + "feed_lag_p95-ms": 191.1, + "mean_width": 8.0, + "ready_p50-ms": 145.8, + "ready_p95-ms": 237.0, + "residual_p50-ms": 46.04, + "residual_p95-ms": 51.4, + "signatures/block": 33760.0, + "signatures/s": 137241.0, + "submit_p50-ms": 0.01647, + "submit_p95-ms": 0.01882, + "B/op": 7033853.0, + "allocs/op": 67538.0, + "source": "captured_2622653", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200623083.0, + "avg_cpu_cores": 0.9263, + "components/block": 126.0, + "cpu-ms/block": 185.8, + "feed_lag_p50-ms": 0.5196, + "feed_lag_p95-ms": 1.002, + "mean_width": 7.912, + "ready_p50-ms": 0.9626, + "ready_p95-ms": 1.463, + "residual_p50-ms": 0.6115, + "residual_p95-ms": 0.9216, + "signatures/block": 33760.0, + "signatures/s": 168276.0, + "submit_p50-ms": 0.001794, + "submit_p95-ms": 0.004097, + "B/op": 7226498.0, + "allocs/op": 68927.0, + "source": "captured_2622653", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200897397.0, + "avg_cpu_cores": 0.8262, + "components/block": 126.0, + "cpu-ms/block": 166.0, + "feed_lag_p50-ms": 0.5245, + "feed_lag_p95-ms": 1.013, + "mean_width": 7.912, + "ready_p50-ms": 0.9103, + "ready_p95-ms": 1.39, + "residual_p50-ms": 1.083, + "residual_p95-ms": 1.198, + "signatures/block": 33760.0, + "signatures/s": 168046.0, + "submit_p50-ms": 0.001183, + "submit_p95-ms": 0.003416, + "B/op": 7225789.0, + "allocs/op": 68921.0, + "source": "captured_2622653", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2622653/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200745122.0, + "avg_cpu_cores": 0.8122, + "components/block": 126.0, + "cpu-ms/block": 163.0, + "feed_lag_p50-ms": 0.516, + "feed_lag_p95-ms": 0.9821, + "mean_width": 7.912, + "ready_p50-ms": 0.8819, + "ready_p95-ms": 1.329, + "residual_p50-ms": 0.6724, + "residual_p95-ms": 1.132, + "signatures/block": 33760.0, + "signatures/s": 168173.0, + "submit_p50-ms": 0.001112, + "submit_p95-ms": 0.002685, + "B/op": 7225640.0, + "allocs/op": 68919.0, + "source": "captured_2622653", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/catchup-8", + "iterations": 3, + "ns/op": 143957884.0, + "avg_cpu_cores": 2.052, + "components/block": 1.0, + "cpu-ms/block": 295.4, + "feed_lag_p50-ms": 0.000241, + "feed_lag_p95-ms": 0.000491, + "mean_width": 4.0, + "ready_p50-ms": 144.0, + "ready_p95-ms": 144.7, + "residual_p50-ms": 144.0, + "residual_p95-ms": 144.7, + "signatures/block": 33760.0, + "signatures/s": 234513.0, + "submit_p50-ms": 0.005019, + "submit_p95-ms": 0.006172, + "B/op": 7023133.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 2, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/catchup-8", + "iterations": 3, + "ns/op": 155693549.0, + "avg_cpu_cores": 2.032, + "components/block": 1.0, + "cpu-ms/block": 316.4, + "feed_lag_p50-ms": 0.0003, + "feed_lag_p95-ms": 0.000311, + "mean_width": 4.0, + "ready_p50-ms": 158.7, + "ready_p95-ms": 162.0, + "residual_p50-ms": 158.7, + "residual_p95-ms": 162.0, + "signatures/block": 33760.0, + "signatures/s": 216836.0, + "submit_p50-ms": 0.004819, + "submit_p95-ms": 0.007064, + "B/op": 7023125.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 2, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/catchup-8", + "iterations": 3, + "ns/op": 175713321.0, + "avg_cpu_cores": 2.044, + "components/block": 1.0, + "cpu-ms/block": 359.1, + "feed_lag_p50-ms": 0.000211, + "feed_lag_p95-ms": 0.000481, + "mean_width": 4.0, + "ready_p50-ms": 175.6, + "ready_p95-ms": 178.9, + "residual_p50-ms": 175.6, + "residual_p95-ms": 178.9, + "signatures/block": 33760.0, + "signatures/s": 192131.0, + "submit_p50-ms": 0.00542, + "submit_p95-ms": 0.006953, + "B/op": 7023141.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 2, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 346489723.0, + "avg_cpu_cores": 0.8637, + "components/block": 126.0, + "cpu-ms/block": 299.3, + "feed_lag_p50-ms": 99.82, + "feed_lag_p95-ms": 191.0, + "mean_width": 4.0, + "ready_p50-ms": 246.5, + "ready_p95-ms": 337.7, + "residual_p50-ms": 145.9, + "residual_p95-ms": 147.9, + "signatures/block": 33760.0, + "signatures/s": 97434.0, + "submit_p50-ms": 0.0141, + "submit_p95-ms": 0.02031, + "B/op": 7033333.0, + "allocs/op": 67539.0, + "source": "captured_2624657", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 345618207.0, + "avg_cpu_cores": 0.8597, + "components/block": 126.0, + "cpu-ms/block": 297.1, + "feed_lag_p50-ms": 99.96, + "feed_lag_p95-ms": 191.2, + "mean_width": 4.0, + "ready_p50-ms": 245.1, + "ready_p95-ms": 336.3, + "residual_p50-ms": 145.9, + "residual_p95-ms": 145.9, + "signatures/block": 33760.0, + "signatures/s": 97680.0, + "submit_p50-ms": 0.01199, + "submit_p95-ms": 0.01378, + "B/op": 7033333.0, + "allocs/op": 67539.0, + "source": "captured_2624657", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 371354060.0, + "avg_cpu_cores": 0.9267, + "components/block": 126.0, + "cpu-ms/block": 344.1, + "feed_lag_p50-ms": 99.54, + "feed_lag_p95-ms": 190.7, + "mean_width": 4.0, + "ready_p50-ms": 271.4, + "ready_p95-ms": 362.6, + "residual_p50-ms": 172.2, + "residual_p95-ms": 177.1, + "signatures/block": 33760.0, + "signatures/s": 90911.0, + "submit_p50-ms": 0.0191, + "submit_p95-ms": 0.02254, + "B/op": 7033333.0, + "allocs/op": 67539.0, + "source": "captured_2624657", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201052815.0, + "avg_cpu_cores": 1.483, + "components/block": 126.0, + "cpu-ms/block": 298.1, + "feed_lag_p50-ms": 0.2236, + "feed_lag_p95-ms": 0.8478, + "mean_width": 3.956, + "ready_p50-ms": 1.585, + "ready_p95-ms": 2.432, + "residual_p50-ms": 0.9514, + "residual_p95-ms": 1.353, + "signatures/block": 33760.0, + "signatures/s": 167916.0, + "submit_p50-ms": 0.000952, + "submit_p95-ms": 0.002114, + "B/op": 7161128.0, + "allocs/op": 68937.0, + "source": "captured_2624657", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200977968.0, + "avg_cpu_cores": 1.811, + "components/block": 126.0, + "cpu-ms/block": 363.9, + "feed_lag_p50-ms": 0.02301, + "feed_lag_p95-ms": 0.7957, + "mean_width": 3.956, + "ready_p50-ms": 1.998, + "ready_p95-ms": 3.094, + "residual_p50-ms": 0.877, + "residual_p95-ms": 1.215, + "signatures/block": 33760.0, + "signatures/s": 167979.0, + "submit_p50-ms": 0.002054, + "submit_p95-ms": 0.004097, + "B/op": 7159746.0, + "allocs/op": 68925.0, + "source": "captured_2624657", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201118531.0, + "avg_cpu_cores": 1.488, + "components/block": 126.0, + "cpu-ms/block": 299.2, + "feed_lag_p50-ms": 0.2092, + "feed_lag_p95-ms": 0.8745, + "mean_width": 3.956, + "ready_p50-ms": 1.589, + "ready_p95-ms": 2.484, + "residual_p50-ms": 1.204, + "residual_p95-ms": 1.456, + "signatures/block": 33760.0, + "signatures/s": 167861.0, + "submit_p50-ms": 0.001001, + "submit_p95-ms": 0.002295, + "B/op": 7160709.0, + "allocs/op": 68934.0, + "source": "captured_2624657", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/catchup-8", + "iterations": 3, + "ns/op": 79597279.0, + "avg_cpu_cores": 2.042, + "components/block": 1.0, + "cpu-ms/block": 162.5, + "feed_lag_p50-ms": 0.00026, + "feed_lag_p95-ms": 0.000471, + "mean_width": 8.0, + "ready_p50-ms": 81.98, + "ready_p95-ms": 82.1, + "residual_p50-ms": 81.98, + "residual_p95-ms": 82.1, + "signatures/block": 33760.0, + "signatures/s": 424135.0, + "submit_p50-ms": 0.00517, + "submit_p95-ms": 0.006362, + "B/op": 7023253.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 2, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/catchup-8", + "iterations": 3, + "ns/op": 92160738.0, + "avg_cpu_cores": 2.042, + "components/block": 1.0, + "cpu-ms/block": 188.1, + "feed_lag_p50-ms": 0.000501, + "feed_lag_p95-ms": 0.000571, + "mean_width": 8.0, + "ready_p50-ms": 91.84, + "ready_p95-ms": 96.76, + "residual_p50-ms": 91.84, + "residual_p95-ms": 96.76, + "signatures/block": 33760.0, + "signatures/s": 366317.0, + "submit_p50-ms": 0.007174, + "submit_p95-ms": 0.008115, + "B/op": 7023266.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 2, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/catchup-8", + "iterations": 3, + "ns/op": 77785688.0, + "avg_cpu_cores": 2.04, + "components/block": 1.0, + "cpu-ms/block": 158.7, + "feed_lag_p50-ms": 0.000211, + "feed_lag_p95-ms": 0.000872, + "mean_width": 8.0, + "ready_p50-ms": 76.89, + "ready_p95-ms": 81.28, + "residual_p50-ms": 76.89, + "residual_p95-ms": 81.28, + "signatures/block": 33760.0, + "signatures/s": 434013.0, + "submit_p50-ms": 0.005009, + "submit_p95-ms": 0.006552, + "B/op": 7023261.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 2, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 279921937.0, + "avg_cpu_cores": 0.5736, + "components/block": 126.0, + "cpu-ms/block": 160.6, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 8.0, + "ready_p50-ms": 179.3, + "ready_p95-ms": 270.5, + "residual_p50-ms": 76.93, + "residual_p95-ms": 87.45, + "signatures/block": 33760.0, + "signatures/s": 120605.0, + "submit_p50-ms": 0.0151, + "submit_p95-ms": 0.01745, + "B/op": 7033453.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 280609110.0, + "avg_cpu_cores": 0.579, + "components/block": 126.0, + "cpu-ms/block": 162.5, + "feed_lag_p50-ms": 100.8, + "feed_lag_p95-ms": 192.0, + "mean_width": 8.0, + "ready_p50-ms": 180.0, + "ready_p95-ms": 271.2, + "residual_p50-ms": 80.28, + "residual_p95-ms": 85.53, + "signatures/block": 33760.0, + "signatures/s": 120310.0, + "submit_p50-ms": 0.01629, + "submit_p95-ms": 0.0575, + "B/op": 7079253.0, + "allocs/op": 67541.0, + "source": "captured_2624657", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 275007675.0, + "avg_cpu_cores": 0.555, + "components/block": 126.0, + "cpu-ms/block": 152.6, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 8.0, + "ready_p50-ms": 174.7, + "ready_p95-ms": 265.9, + "residual_p50-ms": 75.43, + "residual_p95-ms": 75.48, + "signatures/block": 33760.0, + "signatures/s": 122760.0, + "submit_p50-ms": 0.01441, + "submit_p95-ms": 0.01488, + "B/op": 7033474.0, + "allocs/op": 67539.0, + "source": "captured_2624657", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200553309.0, + "avg_cpu_cores": 0.8722, + "components/block": 126.0, + "cpu-ms/block": 174.9, + "feed_lag_p50-ms": 0.4164, + "feed_lag_p95-ms": 0.9441, + "mean_width": 7.912, + "ready_p50-ms": 1.193, + "ready_p95-ms": 1.708, + "residual_p50-ms": 0.5627, + "residual_p95-ms": 0.572, + "signatures/block": 33760.0, + "signatures/s": 168334.0, + "submit_p50-ms": 0.001743, + "submit_p95-ms": 0.004909, + "B/op": 7175850.0, + "allocs/op": 68920.0, + "source": "captured_2624657", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 201115062.0, + "avg_cpu_cores": 0.8365, + "components/block": 126.0, + "cpu-ms/block": 168.2, + "feed_lag_p50-ms": 0.4905, + "feed_lag_p95-ms": 0.9819, + "mean_width": 7.912, + "ready_p50-ms": 1.142, + "ready_p95-ms": 1.804, + "residual_p50-ms": 1.348, + "residual_p95-ms": 1.353, + "signatures/block": 33760.0, + "signatures/s": 167864.0, + "submit_p50-ms": 0.001302, + "submit_p95-ms": 0.003086, + "B/op": 7175330.0, + "allocs/op": 68920.0, + "source": "captured_2624657", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200875209.0, + "avg_cpu_cores": 0.9083, + "components/block": 126.0, + "cpu-ms/block": 182.5, + "feed_lag_p50-ms": 0.4053, + "feed_lag_p95-ms": 0.9401, + "mean_width": 7.912, + "ready_p50-ms": 1.21, + "ready_p95-ms": 1.734, + "residual_p50-ms": 0.7018, + "residual_p95-ms": 1.361, + "signatures/block": 33760.0, + "signatures/s": 168065.0, + "submit_p50-ms": 0.001733, + "submit_p95-ms": 0.004358, + "B/op": 7176061.0, + "allocs/op": 68927.0, + "source": "captured_2624657", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/catchup-8", + "iterations": 3, + "ns/op": 84397967.0, + "avg_cpu_cores": 3.886, + "components/block": 1.0, + "cpu-ms/block": 328.0, + "feed_lag_p50-ms": 0.00027, + "feed_lag_p95-ms": 0.000651, + "mean_width": 4.0, + "ready_p50-ms": 80.13, + "ready_p95-ms": 96.48, + "residual_p50-ms": 80.13, + "residual_p95-ms": 96.48, + "signatures/block": 33760.0, + "signatures/s": 400010.0, + "submit_p50-ms": 0.004749, + "submit_p95-ms": 0.008185, + "B/op": 7023397.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 4, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/catchup-8", + "iterations": 3, + "ns/op": 84545510.0, + "avg_cpu_cores": 3.891, + "components/block": 1.0, + "cpu-ms/block": 328.9, + "feed_lag_p50-ms": 0.00022, + "feed_lag_p95-ms": 0.000722, + "mean_width": 4.0, + "ready_p50-ms": 81.04, + "ready_p95-ms": 94.42, + "residual_p50-ms": 81.04, + "residual_p95-ms": 94.42, + "signatures/block": 33760.0, + "signatures/s": 399312.0, + "submit_p50-ms": 0.007054, + "submit_p95-ms": 0.007925, + "B/op": 7023402.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 4, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/catchup-8", + "iterations": 3, + "ns/op": 77376355.0, + "avg_cpu_cores": 3.992, + "components/block": 1.0, + "cpu-ms/block": 308.9, + "feed_lag_p50-ms": 0.00021, + "feed_lag_p95-ms": 0.000611, + "mean_width": 4.0, + "ready_p50-ms": 76.57, + "ready_p95-ms": 79.75, + "residual_p50-ms": 76.57, + "residual_p95-ms": 79.75, + "signatures/block": 33760.0, + "signatures/s": 436309.0, + "submit_p50-ms": 0.01021, + "submit_p95-ms": 0.01104, + "B/op": 7023402.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 4, + "target": 4, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 276775942.0, + "avg_cpu_cores": 1.101, + "components/block": 126.0, + "cpu-ms/block": 304.8, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 4.0, + "ready_p50-ms": 176.7, + "ready_p95-ms": 267.9, + "residual_p50-ms": 76.69, + "residual_p95-ms": 77.72, + "signatures/block": 33760.0, + "signatures/s": 121976.0, + "submit_p50-ms": 0.01302, + "submit_p95-ms": 0.01303, + "B/op": 7033605.0, + "allocs/op": 67539.0, + "source": "captured_2624657", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 286266781.0, + "avg_cpu_cores": 1.147, + "components/block": 126.0, + "cpu-ms/block": 328.2, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 4.0, + "ready_p50-ms": 186.0, + "ready_p95-ms": 277.2, + "residual_p50-ms": 88.39, + "residual_p95-ms": 92.31, + "signatures/block": 33760.0, + "signatures/s": 117932.0, + "submit_p50-ms": 0.0163, + "submit_p95-ms": 0.0338, + "B/op": 7033597.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 280082230.0, + "avg_cpu_cores": 1.127, + "components/block": 126.0, + "cpu-ms/block": 315.7, + "feed_lag_p50-ms": 100.1, + "feed_lag_p95-ms": 191.3, + "mean_width": 4.0, + "ready_p50-ms": 180.2, + "ready_p95-ms": 271.4, + "residual_p50-ms": 81.38, + "residual_p95-ms": 82.65, + "signatures/block": 33760.0, + "signatures/s": 120536.0, + "submit_p50-ms": 0.01659, + "submit_p95-ms": 0.02034, + "B/op": 7033605.0, + "allocs/op": 67539.0, + "source": "captured_2624657", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200915755.0, + "avg_cpu_cores": 1.5, + "components/block": 126.0, + "cpu-ms/block": 301.4, + "feed_lag_p50-ms": 0.5144, + "feed_lag_p95-ms": 0.9691, + "mean_width": 3.956, + "ready_p50-ms": 1.113, + "ready_p95-ms": 1.588, + "residual_p50-ms": 0.9998, + "residual_p95-ms": 1.239, + "signatures/block": 33760.0, + "signatures/s": 168031.0, + "submit_p50-ms": 0.001022, + "submit_p95-ms": 0.002404, + "B/op": 7193234.0, + "allocs/op": 68918.0, + "source": "captured_2624657", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200718712.0, + "avg_cpu_cores": 1.523, + "components/block": 126.0, + "cpu-ms/block": 305.7, + "feed_lag_p50-ms": 0.458, + "feed_lag_p95-ms": 0.9962, + "mean_width": 3.956, + "ready_p50-ms": 1.154, + "ready_p95-ms": 1.675, + "residual_p50-ms": 0.4901, + "residual_p95-ms": 1.164, + "signatures/block": 33760.0, + "signatures/s": 168196.0, + "submit_p50-ms": 0.001242, + "submit_p95-ms": 0.002976, + "B/op": 7194797.0, + "allocs/op": 68932.0, + "source": "captured_2624657", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200572819.0, + "avg_cpu_cores": 1.531, + "components/block": 126.0, + "cpu-ms/block": 307.1, + "feed_lag_p50-ms": 0.4611, + "feed_lag_p95-ms": 0.9844, + "mean_width": 3.956, + "ready_p50-ms": 1.135, + "ready_p95-ms": 1.701, + "residual_p50-ms": 0.6289, + "residual_p95-ms": 0.6701, + "signatures/block": 33760.0, + "signatures/s": 168318.0, + "submit_p50-ms": 0.001202, + "submit_p95-ms": 0.003256, + "B/op": 7193720.0, + "allocs/op": 68922.0, + "source": "captured_2624657", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/catchup-8", + "iterations": 3, + "ns/op": 39509312.0, + "avg_cpu_cores": 4.011, + "components/block": 1.0, + "cpu-ms/block": 158.5, + "feed_lag_p50-ms": 0.00025, + "feed_lag_p95-ms": 0.000411, + "mean_width": 8.0, + "ready_p50-ms": 39.88, + "ready_p95-ms": 40.18, + "residual_p50-ms": 39.88, + "residual_p95-ms": 40.18, + "signatures/block": 33760.0, + "signatures/s": 854482.0, + "submit_p50-ms": 0.004759, + "submit_p95-ms": 0.00501, + "B/op": 7023658.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 4, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/catchup-8", + "iterations": 3, + "ns/op": 44691175.0, + "avg_cpu_cores": 3.832, + "components/block": 1.0, + "cpu-ms/block": 171.2, + "feed_lag_p50-ms": 0.000501, + "feed_lag_p95-ms": 0.000771, + "mean_width": 8.0, + "ready_p50-ms": 42.16, + "ready_p95-ms": 52.97, + "residual_p50-ms": 42.16, + "residual_p95-ms": 52.97, + "signatures/block": 33760.0, + "signatures/s": 755406.0, + "submit_p50-ms": 0.005119, + "submit_p95-ms": 0.008055, + "B/op": 7023653.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 4, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/catchup-8", + "iterations": 3, + "ns/op": 44816763.0, + "avg_cpu_cores": 3.923, + "components/block": 1.0, + "cpu-ms/block": 175.8, + "feed_lag_p50-ms": 0.000301, + "feed_lag_p95-ms": 0.000481, + "mean_width": 8.0, + "ready_p50-ms": 42.65, + "ready_p95-ms": 51.93, + "residual_p50-ms": 42.65, + "residual_p95-ms": 51.93, + "signatures/block": 33760.0, + "signatures/s": 753290.0, + "submit_p50-ms": 0.003908, + "submit_p95-ms": 0.006643, + "B/op": 7023653.0, + "allocs/op": 67538.0, + "source": "captured_2624657", + "workers": 4, + "target": 8, + "scenario": "catchup", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 242531854.0, + "avg_cpu_cores": 0.6854, + "components/block": 126.0, + "cpu-ms/block": 166.2, + "feed_lag_p50-ms": 99.89, + "feed_lag_p95-ms": 191.1, + "mean_width": 8.0, + "ready_p50-ms": 142.2, + "ready_p95-ms": 233.4, + "residual_p50-ms": 41.44, + "residual_p95-ms": 44.73, + "signatures/block": 33760.0, + "signatures/s": 139198.0, + "submit_p50-ms": 0.01571, + "submit_p95-ms": 0.01829, + "B/op": 7033861.0, + "allocs/op": 67539.0, + "source": "captured_2624657", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 239652146.0, + "avg_cpu_cores": 0.6493, + "components/block": 126.0, + "cpu-ms/block": 155.6, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 191.4, + "mean_width": 8.0, + "ready_p50-ms": 139.2, + "ready_p95-ms": 230.4, + "residual_p50-ms": 39.49, + "residual_p95-ms": 40.03, + "signatures/block": 33760.0, + "signatures/s": 140871.0, + "submit_p50-ms": 0.01441, + "submit_p95-ms": 0.01704, + "B/op": 7033861.0, + "allocs/op": 67539.0, + "source": "captured_2624657", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 240787126.0, + "avg_cpu_cores": 0.6555, + "components/block": 126.0, + "cpu-ms/block": 157.8, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 191.4, + "mean_width": 8.0, + "ready_p50-ms": 140.6, + "ready_p95-ms": 231.8, + "residual_p50-ms": 40.74, + "residual_p95-ms": 41.39, + "signatures/block": 33760.0, + "signatures/s": 140207.0, + "submit_p50-ms": 0.01393, + "submit_p95-ms": 0.01417, + "B/op": 7033861.0, + "allocs/op": 67539.0, + "source": "captured_2624657", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200949184.0, + "avg_cpu_cores": 0.9006, + "components/block": 126.0, + "cpu-ms/block": 181.0, + "feed_lag_p50-ms": 0.4993, + "feed_lag_p95-ms": 0.9768, + "mean_width": 7.912, + "ready_p50-ms": 0.9266, + "ready_p95-ms": 1.401, + "residual_p50-ms": 1.066, + "residual_p95-ms": 1.141, + "signatures/block": 33760.0, + "signatures/s": 168003.0, + "submit_p50-ms": 0.001683, + "submit_p95-ms": 0.006322, + "B/op": 7225536.0, + "allocs/op": 68919.0, + "source": "captured_2624657", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200864502.0, + "avg_cpu_cores": 0.8069, + "components/block": 126.0, + "cpu-ms/block": 162.1, + "feed_lag_p50-ms": 0.5202, + "feed_lag_p95-ms": 1.012, + "mean_width": 7.912, + "ready_p50-ms": 0.8813, + "ready_p95-ms": 1.38, + "residual_p50-ms": 1.09, + "residual_p95-ms": 1.206, + "signatures/block": 33760.0, + "signatures/s": 168074.0, + "submit_p50-ms": 0.001102, + "submit_p95-ms": 0.002535, + "B/op": 7227181.0, + "allocs/op": 68933.0, + "source": "captured_2624657", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/captured_2624657/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200828651.0, + "avg_cpu_cores": 0.8102, + "components/block": 126.0, + "cpu-ms/block": 162.7, + "feed_lag_p50-ms": 0.517, + "feed_lag_p95-ms": 1.016, + "mean_width": 7.912, + "ready_p50-ms": 0.8773, + "ready_p95-ms": 1.385, + "residual_p50-ms": 0.9549, + "residual_p95-ms": 1.219, + "signatures/block": 33760.0, + "signatures/s": 168104.0, + "submit_p50-ms": 0.001112, + "submit_p95-ms": 0.002545, + "B/op": 7226312.0, + "allocs/op": 68925.0, + "source": "captured_2624657", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "captured-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/catchup-8", + "iterations": 3, + "ns/op": 165232107.0, + "avg_cpu_cores": 2.073, + "components/block": 1.0, + "cpu-ms/block": 342.6, + "feed_lag_p50-ms": 0.000241, + "feed_lag_p95-ms": 0.000481, + "mean_width": 4.0, + "ready_p50-ms": 163.8, + "ready_p95-ms": 171.9, + "residual_p50-ms": 163.8, + "residual_p95-ms": 171.9, + "signatures/block": 33760.0, + "signatures/s": 204319.0, + "submit_p50-ms": 0.004889, + "submit_p95-ms": 0.005961, + "B/op": 44570082.0, + "allocs/op": 67557.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "catchup", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/catchup-8", + "iterations": 3, + "ns/op": 166036574.0, + "avg_cpu_cores": 2.074, + "components/block": 1.0, + "cpu-ms/block": 344.4, + "feed_lag_p50-ms": 0.000211, + "feed_lag_p95-ms": 0.000471, + "mean_width": 4.0, + "ready_p50-ms": 164.3, + "ready_p95-ms": 172.6, + "residual_p50-ms": 164.3, + "residual_p95-ms": 172.6, + "signatures/block": 33760.0, + "signatures/s": 203329.0, + "submit_p50-ms": 0.004078, + "submit_p95-ms": 0.006131, + "B/op": 44615709.0, + "allocs/op": 67558.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "catchup", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/catchup-8", + "iterations": 3, + "ns/op": 158962907.0, + "avg_cpu_cores": 2.075, + "components/block": 1.0, + "cpu-ms/block": 329.9, + "feed_lag_p50-ms": 0.00025, + "feed_lag_p95-ms": 0.000461, + "mean_width": 4.0, + "ready_p50-ms": 159.6, + "ready_p95-ms": 160.2, + "residual_p50-ms": 159.6, + "residual_p95-ms": 160.2, + "signatures/block": 33760.0, + "signatures/s": 212377.0, + "submit_p50-ms": 0.004829, + "submit_p95-ms": 0.00547, + "B/op": 44707154.0, + "allocs/op": 67561.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "catchup", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 359264432.0, + "avg_cpu_cores": 0.9167, + "components/block": 689.0, + "cpu-ms/block": 329.3, + "feed_lag_p50-ms": 100.5, + "feed_lag_p95-ms": 190.7, + "mean_width": 4.0, + "ready_p50-ms": 259.3, + "ready_p95-ms": 349.4, + "residual_p50-ms": 159.3, + "residual_p95-ms": 160.9, + "signatures/block": 33760.0, + "signatures/s": 93970.0, + "submit_p50-ms": 0.01357, + "submit_p95-ms": 0.01492, + "B/op": 44535416.0, + "allocs/op": 67551.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 359629949.0, + "avg_cpu_cores": 0.918, + "components/block": 689.0, + "cpu-ms/block": 330.1, + "feed_lag_p50-ms": 100.7, + "feed_lag_p95-ms": 190.8, + "mean_width": 4.0, + "ready_p50-ms": 259.7, + "ready_p95-ms": 349.8, + "residual_p50-ms": 159.4, + "residual_p95-ms": 161.5, + "signatures/block": 33760.0, + "signatures/s": 93874.0, + "submit_p50-ms": 0.01362, + "submit_p95-ms": 0.01513, + "B/op": 44535658.0, + "allocs/op": 67552.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 359479815.0, + "avg_cpu_cores": 0.9171, + "components/block": 689.0, + "cpu-ms/block": 329.7, + "feed_lag_p50-ms": 100.4, + "feed_lag_p95-ms": 190.5, + "mean_width": 4.0, + "ready_p50-ms": 259.5, + "ready_p95-ms": 349.6, + "residual_p50-ms": 159.9, + "residual_p95-ms": 160.2, + "signatures/block": 33760.0, + "signatures/s": 93913.0, + "submit_p50-ms": 0.01571, + "submit_p95-ms": 0.01585, + "B/op": 44489704.0, + "allocs/op": 67550.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200661932.0, + "avg_cpu_cores": 1.879, + "components/block": 689.0, + "cpu-ms/block": 377.1, + "feed_lag_p50-ms": 0.04898, + "feed_lag_p95-ms": 3.657, + "mean_width": 3.77, + "ready_p50-ms": 0.9535, + "ready_p95-ms": 5.077, + "residual_p50-ms": 0.6731, + "residual_p95-ms": 0.9156, + "signatures/block": 33760.0, + "signatures/s": 168243.0, + "submit_p50-ms": 0.001192, + "submit_p95-ms": 0.5149, + "B/op": 45346429.0, + "allocs/op": 75220.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200375326.0, + "avg_cpu_cores": 1.856, + "components/block": 689.0, + "cpu-ms/block": 371.8, + "feed_lag_p50-ms": 0.04542, + "feed_lag_p95-ms": 2.251, + "mean_width": 3.77, + "ready_p50-ms": 0.9154, + "ready_p95-ms": 3.697, + "residual_p50-ms": 0.3591, + "residual_p95-ms": 0.4024, + "signatures/block": 33760.0, + "signatures/s": 168484.0, + "submit_p50-ms": 0.001102, + "submit_p95-ms": 0.5519, + "B/op": 45279114.0, + "allocs/op": 75174.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200405779.0, + "avg_cpu_cores": 1.798, + "components/block": 689.0, + "cpu-ms/block": 360.4, + "feed_lag_p50-ms": 0.03941, + "feed_lag_p95-ms": 1.051, + "mean_width": 3.77, + "ready_p50-ms": 0.8241, + "ready_p95-ms": 2.498, + "residual_p50-ms": 0.3923, + "residual_p95-ms": 0.5034, + "signatures/block": 33760.0, + "signatures/s": 168458.0, + "submit_p50-ms": 0.000852, + "submit_p95-ms": 0.5208, + "B/op": 45287578.0, + "allocs/op": 75182.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_4tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200381187.0, + "avg_cpu_cores": 0.02, + "components/block": 64.0, + "cpu-ms/block": 4.008, + "feed_lag_p50-ms": 0.5205, + "feed_lag_p95-ms": 0.9542, + "mean_width": 4.0, + "ready_p50-ms": 0.5746, + "ready_p95-ms": 1.006, + "residual_p50-ms": 0.06565, + "residual_p95-ms": 1.008, + "signatures/block": 256.0, + "signatures/s": 1278.0, + "submit_p50-ms": 0.001062, + "submit_p95-ms": 0.003106, + "B/op": 405549.0, + "allocs/op": 1227.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "sparse_4tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_4tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200548807.0, + "avg_cpu_cores": 0.03358, + "components/block": 64.0, + "cpu-ms/block": 6.734, + "feed_lag_p50-ms": 0.5788, + "feed_lag_p95-ms": 0.9736, + "mean_width": 4.0, + "ready_p50-ms": 0.6502, + "ready_p95-ms": 1.057, + "residual_p50-ms": 0.4003, + "residual_p95-ms": 0.8991, + "signatures/block": 256.0, + "signatures/s": 1276.0, + "submit_p50-ms": 0.002535, + "submit_p95-ms": 0.01151, + "B/op": 405570.0, + "allocs/op": 1228.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "sparse_4tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_4tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200692627.0, + "avg_cpu_cores": 0.02155, + "components/block": 64.0, + "cpu-ms/block": 4.324, + "feed_lag_p50-ms": 0.5307, + "feed_lag_p95-ms": 0.9496, + "mean_width": 4.0, + "ready_p50-ms": 0.5792, + "ready_p95-ms": 1.004, + "residual_p50-ms": 0.7962, + "residual_p95-ms": 0.9766, + "signatures/block": 256.0, + "signatures/s": 1276.0, + "submit_p50-ms": 0.001232, + "submit_p95-ms": 0.003516, + "B/op": 405549.0, + "allocs/op": 1227.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "sparse_4tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_7tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200734923.0, + "avg_cpu_cores": 0.02505, + "components/block": 37.0, + "cpu-ms/block": 5.029, + "feed_lag_p50-ms": 0.5435, + "feed_lag_p95-ms": 0.9882, + "mean_width": 3.507, + "ready_p50-ms": 0.6193, + "ready_p95-ms": 1.055, + "residual_p50-ms": 0.8195, + "residual_p95-ms": 1.028, + "signatures/block": 256.0, + "signatures/s": 1275.0, + "submit_p50-ms": 0.001703, + "submit_p95-ms": 0.005821, + "B/op": 376309.0, + "allocs/op": 931.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "sparse_7tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_7tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200839933.0, + "avg_cpu_cores": 0.0211, + "components/block": 37.0, + "cpu-ms/block": 4.237, + "feed_lag_p50-ms": 0.5533, + "feed_lag_p95-ms": 1.006, + "mean_width": 3.507, + "ready_p50-ms": 0.6087, + "ready_p95-ms": 1.06, + "residual_p50-ms": 0.9924, + "residual_p95-ms": 1.071, + "signatures/block": 256.0, + "signatures/s": 1275.0, + "submit_p50-ms": 0.001212, + "submit_p95-ms": 0.003216, + "B/op": 376301.0, + "allocs/op": 930.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "sparse_7tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_7tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200166050.0, + "avg_cpu_cores": 0.02238, + "components/block": 37.0, + "cpu-ms/block": 4.48, + "feed_lag_p50-ms": 0.5576, + "feed_lag_p95-ms": 1.003, + "mean_width": 3.507, + "ready_p50-ms": 0.6392, + "ready_p95-ms": 1.089, + "residual_p50-ms": 0.1596, + "residual_p95-ms": 0.1676, + "signatures/block": 256.0, + "signatures/s": 1279.0, + "submit_p50-ms": 0.001563, + "submit_p95-ms": 0.003787, + "B/op": 376301.0, + "allocs/op": 930.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "sparse_7tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_8tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200891132.0, + "avg_cpu_cores": 0.02598, + "components/block": 32.0, + "cpu-ms/block": 5.22, + "feed_lag_p50-ms": 0.6103, + "feed_lag_p95-ms": 1.029, + "mean_width": 4.0, + "ready_p50-ms": 0.6923, + "ready_p95-ms": 1.127, + "residual_p50-ms": 0.9305, + "residual_p95-ms": 1.072, + "signatures/block": 256.0, + "signatures/s": 1274.0, + "submit_p50-ms": 0.002044, + "submit_p95-ms": 0.01812, + "B/op": 370733.0, + "allocs/op": 875.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "sparse_8tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_8tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200748211.0, + "avg_cpu_cores": 0.01877, + "components/block": 32.0, + "cpu-ms/block": 3.768, + "feed_lag_p50-ms": 0.437, + "feed_lag_p95-ms": 1.023, + "mean_width": 4.0, + "ready_p50-ms": 0.5164, + "ready_p95-ms": 1.08, + "residual_p50-ms": 0.7419, + "residual_p95-ms": 0.764, + "signatures/block": 256.0, + "signatures/s": 1275.0, + "submit_p50-ms": 0.001562, + "submit_p95-ms": 0.003406, + "B/op": 370733.0, + "allocs/op": 875.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "sparse_8tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_4/sparse_8tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200821061.0, + "avg_cpu_cores": 0.02266, + "components/block": 32.0, + "cpu-ms/block": 4.551, + "feed_lag_p50-ms": 0.6271, + "feed_lag_p95-ms": 1.025, + "mean_width": 4.0, + "ready_p50-ms": 0.6833, + "ready_p95-ms": 1.099, + "residual_p50-ms": 0.8705, + "residual_p95-ms": 0.9203, + "signatures/block": 256.0, + "signatures/s": 1275.0, + "submit_p50-ms": 0.001843, + "submit_p95-ms": 0.01049, + "B/op": 370733.0, + "allocs/op": 875.0, + "source": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "sparse_8tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/catchup-8", + "iterations": 3, + "ns/op": 88134578.0, + "avg_cpu_cores": 2.097, + "components/block": 1.0, + "cpu-ms/block": 184.8, + "feed_lag_p50-ms": 0.000401, + "feed_lag_p95-ms": 0.000832, + "mean_width": 8.0, + "ready_p50-ms": 89.14, + "ready_p95-ms": 92.15, + "residual_p50-ms": 89.14, + "residual_p95-ms": 92.15, + "signatures/block": 33760.0, + "signatures/s": 383051.0, + "submit_p50-ms": 0.005991, + "submit_p95-ms": 0.0127, + "B/op": 44478298.0, + "allocs/op": 67549.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "catchup", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/catchup-8", + "iterations": 3, + "ns/op": 86113936.0, + "avg_cpu_cores": 2.09, + "components/block": 1.0, + "cpu-ms/block": 180.0, + "feed_lag_p50-ms": 0.00026, + "feed_lag_p95-ms": 0.000732, + "mean_width": 8.0, + "ready_p50-ms": 84.68, + "ready_p95-ms": 89.35, + "residual_p50-ms": 84.68, + "residual_p95-ms": 89.35, + "signatures/block": 33760.0, + "signatures/s": 392039.0, + "submit_p50-ms": 0.004137, + "submit_p95-ms": 0.00537, + "B/op": 44386656.0, + "allocs/op": 67544.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "catchup", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/catchup-8", + "iterations": 3, + "ns/op": 84405778.0, + "avg_cpu_cores": 2.098, + "components/block": 1.0, + "cpu-ms/block": 177.1, + "feed_lag_p50-ms": 0.00025, + "feed_lag_p95-ms": 0.000481, + "mean_width": 8.0, + "ready_p50-ms": 84.06, + "ready_p95-ms": 85.34, + "residual_p50-ms": 84.06, + "residual_p95-ms": 85.34, + "signatures/block": 33760.0, + "signatures/s": 399973.0, + "submit_p50-ms": 0.004048, + "submit_p95-ms": 0.004588, + "B/op": 44569941.0, + "allocs/op": 67554.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "catchup", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 284620670.0, + "avg_cpu_cores": 0.6238, + "components/block": 689.0, + "cpu-ms/block": 177.6, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 190.3, + "mean_width": 8.0, + "ready_p50-ms": 184.6, + "ready_p95-ms": 274.8, + "residual_p50-ms": 84.69, + "residual_p95-ms": 85.06, + "signatures/block": 33760.0, + "signatures/s": 118614.0, + "submit_p50-ms": 0.01488, + "submit_p95-ms": 0.01676, + "B/op": 44489685.0, + "allocs/op": 67548.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 285857000.0, + "avg_cpu_cores": 0.6282, + "components/block": 689.0, + "cpu-ms/block": 179.6, + "feed_lag_p50-ms": 100.4, + "feed_lag_p95-ms": 190.5, + "mean_width": 8.0, + "ready_p50-ms": 185.9, + "ready_p95-ms": 276.0, + "residual_p50-ms": 85.29, + "residual_p95-ms": 87.23, + "signatures/block": 33760.0, + "signatures/s": 118101.0, + "submit_p50-ms": 0.01511, + "submit_p95-ms": 0.01781, + "B/op": 44627120.0, + "allocs/op": 67555.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 290218004.0, + "avg_cpu_cores": 0.6484, + "components/block": 689.0, + "cpu-ms/block": 188.2, + "feed_lag_p50-ms": 100.7, + "feed_lag_p95-ms": 190.8, + "mean_width": 8.0, + "ready_p50-ms": 190.1, + "ready_p95-ms": 280.3, + "residual_p50-ms": 91.02, + "residual_p95-ms": 96.42, + "signatures/block": 33760.0, + "signatures/s": 116326.0, + "submit_p50-ms": 0.01466, + "submit_p95-ms": 0.0263, + "B/op": 44535536.0, + "allocs/op": 67551.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200393747.0, + "avg_cpu_cores": 0.9599, + "components/block": 689.0, + "cpu-ms/block": 192.4, + "feed_lag_p50-ms": 0.1208, + "feed_lag_p95-ms": 0.6496, + "mean_width": 7.001, + "ready_p50-ms": 0.5021, + "ready_p95-ms": 1.098, + "residual_p50-ms": 0.3487, + "residual_p95-ms": 0.6362, + "signatures/block": 33760.0, + "signatures/s": 168468.0, + "submit_p50-ms": 0.000601, + "submit_p95-ms": 0.002294, + "B/op": 45456104.0, + "allocs/op": 75154.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200299620.0, + "avg_cpu_cores": 0.9555, + "components/block": 689.0, + "cpu-ms/block": 191.4, + "feed_lag_p50-ms": 0.1164, + "feed_lag_p95-ms": 0.6451, + "mean_width": 7.001, + "ready_p50-ms": 0.4922, + "ready_p95-ms": 1.083, + "residual_p50-ms": 0.2595, + "residual_p95-ms": 0.397, + "signatures/block": 33760.0, + "signatures/s": 168547.0, + "submit_p50-ms": 0.000581, + "submit_p95-ms": 0.001764, + "B/op": 45500650.0, + "allocs/op": 75145.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200435552.0, + "avg_cpu_cores": 1.129, + "components/block": 689.0, + "cpu-ms/block": 226.3, + "feed_lag_p50-ms": 0.08438, + "feed_lag_p95-ms": 0.7172, + "mean_width": 7.001, + "ready_p50-ms": 0.5504, + "ready_p95-ms": 1.334, + "residual_p50-ms": 0.3419, + "residual_p95-ms": 0.6538, + "signatures/block": 33760.0, + "signatures/s": 168433.0, + "submit_p50-ms": 0.000902, + "submit_p95-ms": 0.08766, + "B/op": 45547920.0, + "allocs/op": 75161.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_4tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200244671.0, + "avg_cpu_cores": 0.03331, + "components/block": 64.0, + "cpu-ms/block": 6.669, + "feed_lag_p50-ms": 0.5335, + "feed_lag_p95-ms": 0.9768, + "mean_width": 4.0, + "ready_p50-ms": 0.6135, + "ready_p95-ms": 1.06, + "residual_p50-ms": 0.2598, + "residual_p95-ms": 0.2713, + "signatures/block": 256.0, + "signatures/s": 1278.0, + "submit_p50-ms": 0.002525, + "submit_p95-ms": 0.008676, + "B/op": 413741.0, + "allocs/op": 1227.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "sparse_4tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_4tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200802763.0, + "avg_cpu_cores": 0.02071, + "components/block": 64.0, + "cpu-ms/block": 4.159, + "feed_lag_p50-ms": 0.5448, + "feed_lag_p95-ms": 0.9858, + "mean_width": 4.0, + "ready_p50-ms": 0.5937, + "ready_p95-ms": 1.037, + "residual_p50-ms": 1.016, + "residual_p95-ms": 1.07, + "signatures/block": 256.0, + "signatures/s": 1275.0, + "submit_p50-ms": 0.001102, + "submit_p95-ms": 0.002816, + "B/op": 413741.0, + "allocs/op": 1227.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "sparse_4tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_4tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200470397.0, + "avg_cpu_cores": 0.02005, + "components/block": 64.0, + "cpu-ms/block": 4.019, + "feed_lag_p50-ms": 0.53, + "feed_lag_p95-ms": 0.9483, + "mean_width": 4.0, + "ready_p50-ms": 0.5769, + "ready_p95-ms": 1.0, + "residual_p50-ms": 0.3612, + "residual_p95-ms": 0.8751, + "signatures/block": 256.0, + "signatures/s": 1277.0, + "submit_p50-ms": 0.001022, + "submit_p95-ms": 0.003106, + "B/op": 413741.0, + "allocs/op": 1227.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "sparse_4tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_7tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200530663.0, + "avg_cpu_cores": 0.02064, + "components/block": 37.0, + "cpu-ms/block": 4.138, + "feed_lag_p50-ms": 0.5475, + "feed_lag_p95-ms": 1.006, + "mean_width": 6.919, + "ready_p50-ms": 0.6508, + "ready_p95-ms": 1.095, + "residual_p50-ms": 0.2603, + "residual_p95-ms": 1.101, + "signatures/block": 256.0, + "signatures/s": 1277.0, + "submit_p50-ms": 0.001413, + "submit_p95-ms": 0.003417, + "B/op": 381074.0, + "allocs/op": 931.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "sparse_7tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_7tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200704225.0, + "avg_cpu_cores": 0.0297, + "components/block": 37.0, + "cpu-ms/block": 5.961, + "feed_lag_p50-ms": 0.5458, + "feed_lag_p95-ms": 0.9953, + "mean_width": 6.919, + "ready_p50-ms": 0.6663, + "ready_p95-ms": 1.147, + "residual_p50-ms": 0.6189, + "residual_p95-ms": 1.129, + "signatures/block": 256.0, + "signatures/s": 1276.0, + "submit_p50-ms": 0.003116, + "submit_p95-ms": 0.008637, + "B/op": 381186.0, + "allocs/op": 932.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "sparse_7tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_7tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200734505.0, + "avg_cpu_cores": 0.01986, + "components/block": 37.0, + "cpu-ms/block": 3.987, + "feed_lag_p50-ms": 0.5511, + "feed_lag_p95-ms": 0.9991, + "mean_width": 6.919, + "ready_p50-ms": 0.6354, + "ready_p95-ms": 1.085, + "residual_p50-ms": 0.8372, + "residual_p95-ms": 1.059, + "signatures/block": 256.0, + "signatures/s": 1275.0, + "submit_p50-ms": 0.001192, + "submit_p95-ms": 0.003176, + "B/op": 381037.0, + "allocs/op": 930.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "sparse_7tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_8tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200612984.0, + "avg_cpu_cores": 0.01255, + "components/block": 32.0, + "cpu-ms/block": 2.517, + "feed_lag_p50-ms": 0.5718, + "feed_lag_p95-ms": 1.002, + "mean_width": 8.0, + "ready_p50-ms": 0.6479, + "ready_p95-ms": 1.064, + "residual_p50-ms": 0.4956, + "residual_p95-ms": 0.9127, + "signatures/block": 256.0, + "signatures/s": 1276.0, + "submit_p50-ms": 0.001432, + "submit_p95-ms": 0.00542, + "B/op": 420621.0, + "allocs/op": 878.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "sparse_8tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_8tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200664651.0, + "avg_cpu_cores": 0.01322, + "components/block": 32.0, + "cpu-ms/block": 2.652, + "feed_lag_p50-ms": 0.6362, + "feed_lag_p95-ms": 1.01, + "mean_width": 8.0, + "ready_p50-ms": 0.6894, + "ready_p95-ms": 1.085, + "residual_p50-ms": 0.688, + "residual_p95-ms": 0.7389, + "signatures/block": 256.0, + "signatures/s": 1276.0, + "submit_p50-ms": 0.001503, + "submit_p95-ms": 0.006863, + "B/op": 374829.0, + "allocs/op": 875.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "sparse_8tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_2/target_8/sparse_8tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200621059.0, + "avg_cpu_cores": 0.01625, + "components/block": 32.0, + "cpu-ms/block": 3.26, + "feed_lag_p50-ms": 0.6089, + "feed_lag_p95-ms": 1.027, + "mean_width": 8.0, + "ready_p50-ms": 0.6781, + "ready_p95-ms": 1.091, + "residual_p50-ms": 0.6671, + "residual_p95-ms": 0.8341, + "signatures/block": 256.0, + "signatures/s": 1276.0, + "submit_p50-ms": 0.002575, + "submit_p95-ms": 0.008015, + "B/op": 374941.0, + "allocs/op": 876.0, + "source": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "sparse_8tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/catchup-8", + "iterations": 3, + "ns/op": 95848314.0, + "avg_cpu_cores": 3.962, + "components/block": 1.0, + "cpu-ms/block": 379.7, + "feed_lag_p50-ms": 0.000501, + "feed_lag_p95-ms": 0.000882, + "mean_width": 4.0, + "ready_p50-ms": 97.21, + "ready_p95-ms": 97.49, + "residual_p50-ms": 97.21, + "residual_p95-ms": 97.49, + "signatures/block": 33760.0, + "signatures/s": 352223.0, + "submit_p50-ms": 0.006161, + "submit_p95-ms": 0.01999, + "B/op": 44616002.0, + "allocs/op": 67558.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "catchup", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/catchup-8", + "iterations": 3, + "ns/op": 93784047.0, + "avg_cpu_cores": 3.933, + "components/block": 1.0, + "cpu-ms/block": 368.9, + "feed_lag_p50-ms": 0.00026, + "feed_lag_p95-ms": 0.000581, + "mean_width": 4.0, + "ready_p50-ms": 97.49, + "ready_p95-ms": 98.78, + "residual_p50-ms": 97.49, + "residual_p95-ms": 98.78, + "signatures/block": 33760.0, + "signatures/s": 359976.0, + "submit_p50-ms": 0.004298, + "submit_p95-ms": 0.00538, + "B/op": 44570261.0, + "allocs/op": 67556.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "catchup", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/catchup-8", + "iterations": 3, + "ns/op": 83340419.0, + "avg_cpu_cores": 4.08, + "components/block": 1.0, + "cpu-ms/block": 340.1, + "feed_lag_p50-ms": 0.00026, + "feed_lag_p95-ms": 0.000481, + "mean_width": 4.0, + "ready_p50-ms": 83.48, + "ready_p95-ms": 84.27, + "residual_p50-ms": 83.48, + "residual_p95-ms": 84.27, + "signatures/block": 33760.0, + "signatures/s": 405086.0, + "submit_p50-ms": 0.004358, + "submit_p95-ms": 0.004779, + "B/op": 44615936.0, + "allocs/op": 67557.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "catchup", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 284770040.0, + "avg_cpu_cores": 1.189, + "components/block": 689.0, + "cpu-ms/block": 338.7, + "feed_lag_p50-ms": 100.2, + "feed_lag_p95-ms": 190.3, + "mean_width": 4.0, + "ready_p50-ms": 184.7, + "ready_p95-ms": 274.8, + "residual_p50-ms": 84.7, + "residual_p95-ms": 84.9, + "signatures/block": 33760.0, + "signatures/s": 118552.0, + "submit_p50-ms": 0.0142, + "submit_p95-ms": 0.01501, + "B/op": 44627381.0, + "allocs/op": 67557.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 283332704.0, + "avg_cpu_cores": 1.187, + "components/block": 689.0, + "cpu-ms/block": 336.2, + "feed_lag_p50-ms": 100.4, + "feed_lag_p95-ms": 190.5, + "mean_width": 4.0, + "ready_p50-ms": 183.3, + "ready_p95-ms": 273.5, + "residual_p50-ms": 83.42, + "residual_p95-ms": 83.48, + "signatures/block": 33760.0, + "signatures/s": 119153.0, + "submit_p50-ms": 0.01509, + "submit_p95-ms": 0.01671, + "B/op": 44764728.0, + "allocs/op": 67563.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 288602671.0, + "avg_cpu_cores": 1.226, + "components/block": 689.0, + "cpu-ms/block": 353.9, + "feed_lag_p50-ms": 100.4, + "feed_lag_p95-ms": 190.5, + "mean_width": 4.0, + "ready_p50-ms": 188.6, + "ready_p95-ms": 278.7, + "residual_p50-ms": 82.52, + "residual_p95-ms": 101.5, + "signatures/block": 33760.0, + "signatures/s": 116977.0, + "submit_p50-ms": 0.01444, + "submit_p95-ms": 0.01669, + "B/op": 44627397.0, + "allocs/op": 67557.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200391623.0, + "avg_cpu_cores": 1.7, + "components/block": 689.0, + "cpu-ms/block": 340.6, + "feed_lag_p50-ms": 0.1294, + "feed_lag_p95-ms": 0.66, + "mean_width": 3.77, + "ready_p50-ms": 0.4949, + "ready_p95-ms": 1.08, + "residual_p50-ms": 0.3539, + "residual_p95-ms": 0.5576, + "signatures/block": 33760.0, + "signatures/s": 168470.0, + "submit_p50-ms": 0.000611, + "submit_p95-ms": 0.001674, + "B/op": 45465394.0, + "allocs/op": 75165.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200606793.0, + "avg_cpu_cores": 1.834, + "components/block": 689.0, + "cpu-ms/block": 368.0, + "feed_lag_p50-ms": 0.09882, + "feed_lag_p95-ms": 0.6699, + "mean_width": 3.77, + "ready_p50-ms": 0.5168, + "ready_p95-ms": 1.161, + "residual_p50-ms": 0.5678, + "residual_p95-ms": 0.7021, + "signatures/block": 33760.0, + "signatures/s": 168289.0, + "submit_p50-ms": 0.000742, + "submit_p95-ms": 0.002705, + "B/op": 45554293.0, + "allocs/op": 75146.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200390250.0, + "avg_cpu_cores": 1.799, + "components/block": 689.0, + "cpu-ms/block": 360.4, + "feed_lag_p50-ms": 0.1082, + "feed_lag_p95-ms": 0.6858, + "mean_width": 3.77, + "ready_p50-ms": 0.5309, + "ready_p95-ms": 1.29, + "residual_p50-ms": 0.2986, + "residual_p95-ms": 0.5996, + "signatures/block": 33760.0, + "signatures/s": 168471.0, + "submit_p50-ms": 0.000731, + "submit_p95-ms": 0.002485, + "B/op": 45417898.0, + "allocs/op": 75147.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_4tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200708870.0, + "avg_cpu_cores": 0.02342, + "components/block": 64.0, + "cpu-ms/block": 4.7, + "feed_lag_p50-ms": 0.5677, + "feed_lag_p95-ms": 0.9727, + "mean_width": 4.0, + "ready_p50-ms": 0.6206, + "ready_p95-ms": 1.032, + "residual_p50-ms": 0.5738, + "residual_p95-ms": 1.11, + "signatures/block": 256.0, + "signatures/s": 1275.0, + "submit_p50-ms": 0.001583, + "submit_p95-ms": 0.004359, + "B/op": 423106.0, + "allocs/op": 1229.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "sparse_4tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_4tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200711469.0, + "avg_cpu_cores": 0.02988, + "components/block": 64.0, + "cpu-ms/block": 5.998, + "feed_lag_p50-ms": 0.5955, + "feed_lag_p95-ms": 1.001, + "mean_width": 4.0, + "ready_p50-ms": 0.669, + "ready_p95-ms": 1.073, + "residual_p50-ms": 0.6728, + "residual_p95-ms": 0.8759, + "signatures/block": 256.0, + "signatures/s": 1275.0, + "submit_p50-ms": 0.002254, + "submit_p95-ms": 0.00532, + "B/op": 422957.0, + "allocs/op": 1227.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "sparse_4tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_4tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200458906.0, + "avg_cpu_cores": 0.0246, + "components/block": 64.0, + "cpu-ms/block": 4.931, + "feed_lag_p50-ms": 0.5634, + "feed_lag_p95-ms": 0.9819, + "mean_width": 4.0, + "ready_p50-ms": 0.6169, + "ready_p95-ms": 1.037, + "residual_p50-ms": 0.4628, + "residual_p95-ms": 0.6882, + "signatures/block": 256.0, + "signatures/s": 1277.0, + "submit_p50-ms": 0.001663, + "submit_p95-ms": 0.004278, + "B/op": 422957.0, + "allocs/op": 1227.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "sparse_4tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_7tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200411787.0, + "avg_cpu_cores": 0.025, + "components/block": 37.0, + "cpu-ms/block": 5.011, + "feed_lag_p50-ms": 0.5475, + "feed_lag_p95-ms": 0.9961, + "mean_width": 3.507, + "ready_p50-ms": 0.6059, + "ready_p95-ms": 1.064, + "residual_p50-ms": 0.2112, + "residual_p95-ms": 0.8439, + "signatures/block": 256.0, + "signatures/s": 1277.0, + "submit_p50-ms": 0.001603, + "submit_p95-ms": 0.008125, + "B/op": 386365.0, + "allocs/op": 930.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "sparse_7tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_7tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200155541.0, + "avg_cpu_cores": 0.02172, + "components/block": 37.0, + "cpu-ms/block": 4.348, + "feed_lag_p50-ms": 0.5305, + "feed_lag_p95-ms": 0.995, + "mean_width": 3.507, + "ready_p50-ms": 0.5883, + "ready_p95-ms": 1.045, + "residual_p50-ms": 0.1636, + "residual_p95-ms": 0.1931, + "signatures/block": 256.0, + "signatures/s": 1279.0, + "submit_p50-ms": 0.001372, + "submit_p95-ms": 0.003166, + "B/op": 386365.0, + "allocs/op": 930.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "sparse_7tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_7tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200521630.0, + "avg_cpu_cores": 0.0282, + "components/block": 37.0, + "cpu-ms/block": 5.655, + "feed_lag_p50-ms": 0.5292, + "feed_lag_p95-ms": 0.9752, + "mean_width": 3.507, + "ready_p50-ms": 0.5981, + "ready_p95-ms": 1.088, + "residual_p50-ms": 0.5204, + "residual_p95-ms": 0.7328, + "signatures/block": 256.0, + "signatures/s": 1277.0, + "submit_p50-ms": 0.001944, + "submit_p95-ms": 0.0128, + "B/op": 386365.0, + "allocs/op": 930.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "sparse_7tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_8tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200734011.0, + "avg_cpu_cores": 0.01795, + "components/block": 32.0, + "cpu-ms/block": 3.602, + "feed_lag_p50-ms": 0.6295, + "feed_lag_p95-ms": 1.028, + "mean_width": 4.0, + "ready_p50-ms": 0.6833, + "ready_p95-ms": 1.083, + "residual_p50-ms": 0.6833, + "residual_p95-ms": 0.8221, + "signatures/block": 256.0, + "signatures/s": 1275.0, + "submit_p50-ms": 0.001363, + "submit_p95-ms": 0.002945, + "B/op": 379437.0, + "allocs/op": 875.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "sparse_8tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_8tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 201052107.0, + "avg_cpu_cores": 0.02754, + "components/block": 32.0, + "cpu-ms/block": 5.536, + "feed_lag_p50-ms": 0.6295, + "feed_lag_p95-ms": 1.034, + "mean_width": 4.0, + "ready_p50-ms": 0.7177, + "ready_p95-ms": 1.115, + "residual_p50-ms": 1.048, + "residual_p95-ms": 1.051, + "signatures/block": 256.0, + "signatures/s": 1273.0, + "submit_p50-ms": 0.001994, + "submit_p95-ms": 0.02698, + "B/op": 379437.0, + "allocs/op": 875.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "sparse_8tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_4/sparse_8tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200711809.0, + "avg_cpu_cores": 0.01892, + "components/block": 32.0, + "cpu-ms/block": 3.797, + "feed_lag_p50-ms": 0.6447, + "feed_lag_p95-ms": 1.037, + "mean_width": 4.0, + "ready_p50-ms": 0.6915, + "ready_p95-ms": 1.093, + "residual_p50-ms": 0.682, + "residual_p95-ms": 0.8024, + "signatures/block": 256.0, + "signatures/s": 1275.0, + "submit_p50-ms": 0.001643, + "submit_p95-ms": 0.003516, + "B/op": 379624.0, + "allocs/op": 877.0, + "source": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "sparse_8tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/catchup-8", + "iterations": 3, + "ns/op": 45816383.0, + "avg_cpu_cores": 4.073, + "components/block": 1.0, + "cpu-ms/block": 186.6, + "feed_lag_p50-ms": 0.0002, + "feed_lag_p95-ms": 0.000491, + "mean_width": 8.0, + "ready_p50-ms": 45.06, + "ready_p95-ms": 48.98, + "residual_p50-ms": 45.06, + "residual_p95-ms": 48.98, + "signatures/block": 33760.0, + "signatures/s": 736854.0, + "submit_p50-ms": 0.004338, + "submit_p95-ms": 0.004658, + "B/op": 44570576.0, + "allocs/op": 67557.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "catchup", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/catchup-8", + "iterations": 3, + "ns/op": 56218950.0, + "avg_cpu_cores": 3.966, + "components/block": 1.0, + "cpu-ms/block": 223.0, + "feed_lag_p50-ms": 0.000501, + "feed_lag_p95-ms": 0.000551, + "mean_width": 8.0, + "ready_p50-ms": 55.44, + "ready_p95-ms": 58.07, + "residual_p50-ms": 55.44, + "residual_p95-ms": 58.07, + "signatures/block": 33760.0, + "signatures/s": 600509.0, + "submit_p50-ms": 0.006312, + "submit_p95-ms": 0.0147, + "B/op": 44524608.0, + "allocs/op": 67553.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "catchup", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/catchup-8", + "iterations": 3, + "ns/op": 61550045.0, + "avg_cpu_cores": 3.871, + "components/block": 1.0, + "cpu-ms/block": 238.3, + "feed_lag_p50-ms": 0.000301, + "feed_lag_p95-ms": 0.000912, + "mean_width": 8.0, + "ready_p50-ms": 60.95, + "ready_p95-ms": 62.93, + "residual_p50-ms": 60.95, + "residual_p95-ms": 62.93, + "signatures/block": 33760.0, + "signatures/s": 548497.0, + "submit_p50-ms": 0.007384, + "submit_p95-ms": 0.01248, + "B/op": 44662146.0, + "allocs/op": 67562.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "catchup", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 248039377.0, + "avg_cpu_cores": 0.7751, + "components/block": 689.0, + "cpu-ms/block": 192.2, + "feed_lag_p50-ms": 100.4, + "feed_lag_p95-ms": 190.5, + "mean_width": 8.0, + "ready_p50-ms": 148.0, + "ready_p95-ms": 238.1, + "residual_p50-ms": 46.77, + "residual_p95-ms": 54.35, + "signatures/block": 33760.0, + "signatures/s": 136107.0, + "submit_p50-ms": 0.01645, + "submit_p95-ms": 0.04039, + "B/op": 44398781.0, + "allocs/op": 67546.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 244855756.0, + "avg_cpu_cores": 0.7431, + "components/block": 689.0, + "cpu-ms/block": 182.0, + "feed_lag_p50-ms": 100.4, + "feed_lag_p95-ms": 190.5, + "mean_width": 8.0, + "ready_p50-ms": 144.8, + "ready_p95-ms": 234.9, + "residual_p50-ms": 44.62, + "residual_p95-ms": 45.36, + "signatures/block": 33760.0, + "signatures/s": 137877.0, + "submit_p50-ms": 0.01356, + "submit_p95-ms": 0.01404, + "B/op": 44627629.0, + "allocs/op": 67556.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/tip_200ms_after_complete-8", + "iterations": 3, + "ns/op": 246211332.0, + "avg_cpu_cores": 0.7541, + "components/block": 689.0, + "cpu-ms/block": 185.7, + "feed_lag_p50-ms": 100.4, + "feed_lag_p95-ms": 190.5, + "mean_width": 8.0, + "ready_p50-ms": 146.2, + "ready_p95-ms": 236.3, + "residual_p50-ms": 46.81, + "residual_p95-ms": 47.71, + "signatures/block": 33760.0, + "signatures/s": 137118.0, + "submit_p50-ms": 0.01444, + "submit_p95-ms": 0.01546, + "B/op": 44490194.0, + "allocs/op": 67549.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200653199.0, + "avg_cpu_cores": 1.155, + "components/block": 689.0, + "cpu-ms/block": 231.7, + "feed_lag_p50-ms": 0.2466, + "feed_lag_p95-ms": 0.7849, + "mean_width": 7.001, + "ready_p50-ms": 0.5281, + "ready_p95-ms": 1.123, + "residual_p50-ms": 0.667, + "residual_p95-ms": 0.9134, + "signatures/block": 33760.0, + "signatures/s": 168250.0, + "submit_p50-ms": 0.000821, + "submit_p95-ms": 0.002454, + "B/op": 45684682.0, + "allocs/op": 75142.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200414118.0, + "avg_cpu_cores": 0.9499, + "components/block": 689.0, + "cpu-ms/block": 190.4, + "feed_lag_p50-ms": 0.295, + "feed_lag_p95-ms": 0.782, + "mean_width": 7.001, + "ready_p50-ms": 0.5114, + "ready_p95-ms": 1.017, + "residual_p50-ms": 0.2302, + "residual_p95-ms": 0.831, + "signatures/block": 33760.0, + "signatures/s": 168451.0, + "submit_p50-ms": 0.000531, + "submit_p95-ms": 0.001473, + "B/op": 45775912.0, + "allocs/op": 75143.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/tip_200ms_overlap-8", + "iterations": 3, + "ns/op": 200256049.0, + "avg_cpu_cores": 0.9622, + "components/block": 689.0, + "cpu-ms/block": 192.7, + "feed_lag_p50-ms": 0.2963, + "feed_lag_p95-ms": 0.7859, + "mean_width": 7.001, + "ready_p50-ms": 0.5143, + "ready_p95-ms": 1.015, + "residual_p50-ms": 0.1938, + "residual_p95-ms": 0.4353, + "signatures/block": 33760.0, + "signatures/s": 168584.0, + "submit_p50-ms": 0.000561, + "submit_p95-ms": 0.001704, + "B/op": 45825840.0, + "allocs/op": 75182.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_4tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200387649.0, + "avg_cpu_cores": 0.02538, + "components/block": 64.0, + "cpu-ms/block": 5.086, + "feed_lag_p50-ms": 0.5414, + "feed_lag_p95-ms": 0.9647, + "mean_width": 4.0, + "ready_p50-ms": 0.5928, + "ready_p95-ms": 1.033, + "residual_p50-ms": 0.5171, + "residual_p95-ms": 0.5489, + "signatures/block": 256.0, + "signatures/s": 1278.0, + "submit_p50-ms": 0.001523, + "submit_p95-ms": 0.005641, + "B/op": 439648.0, + "allocs/op": 1230.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "sparse_4tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_4tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200883060.0, + "avg_cpu_cores": 0.03008, + "components/block": 64.0, + "cpu-ms/block": 6.043, + "feed_lag_p50-ms": 0.5974, + "feed_lag_p95-ms": 0.9731, + "mean_width": 4.0, + "ready_p50-ms": 0.6814, + "ready_p95-ms": 1.066, + "residual_p50-ms": 1.116, + "residual_p95-ms": 1.146, + "signatures/block": 256.0, + "signatures/s": 1274.0, + "submit_p50-ms": 0.002054, + "submit_p95-ms": 0.007835, + "B/op": 439349.0, + "allocs/op": 1228.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "sparse_4tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_4tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200468704.0, + "avg_cpu_cores": 0.02652, + "components/block": 64.0, + "cpu-ms/block": 5.317, + "feed_lag_p50-ms": 0.5614, + "feed_lag_p95-ms": 0.9687, + "mean_width": 4.0, + "ready_p50-ms": 0.6253, + "ready_p95-ms": 1.041, + "residual_p50-ms": 0.3181, + "residual_p95-ms": 0.7522, + "signatures/block": 256.0, + "signatures/s": 1277.0, + "submit_p50-ms": 0.002074, + "submit_p95-ms": 0.00491, + "B/op": 439341.0, + "allocs/op": 1227.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "sparse_4tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_7tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200580146.0, + "avg_cpu_cores": 0.02269, + "components/block": 37.0, + "cpu-ms/block": 4.551, + "feed_lag_p50-ms": 0.523, + "feed_lag_p95-ms": 1.004, + "mean_width": 6.919, + "ready_p50-ms": 0.6231, + "ready_p95-ms": 1.099, + "residual_p50-ms": 0.448, + "residual_p95-ms": 1.093, + "signatures/block": 256.0, + "signatures/s": 1276.0, + "submit_p50-ms": 0.001423, + "submit_p95-ms": 0.00499, + "B/op": 395837.0, + "allocs/op": 930.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "sparse_7tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_7tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200704543.0, + "avg_cpu_cores": 0.02296, + "components/block": 37.0, + "cpu-ms/block": 4.607, + "feed_lag_p50-ms": 0.5328, + "feed_lag_p95-ms": 1.022, + "mean_width": 6.919, + "ready_p50-ms": 0.6489, + "ready_p95-ms": 1.107, + "residual_p50-ms": 0.648, + "residual_p95-ms": 1.079, + "signatures/block": 256.0, + "signatures/s": 1276.0, + "submit_p50-ms": 0.001783, + "submit_p95-ms": 0.008186, + "B/op": 395837.0, + "allocs/op": 930.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "sparse_7tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_7tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200601736.0, + "avg_cpu_cores": 0.02604, + "components/block": 37.0, + "cpu-ms/block": 5.224, + "feed_lag_p50-ms": 0.5685, + "feed_lag_p95-ms": 1.009, + "mean_width": 6.919, + "ready_p50-ms": 0.6868, + "ready_p95-ms": 1.128, + "residual_p50-ms": 0.6345, + "residual_p95-ms": 0.912, + "signatures/block": 256.0, + "signatures/s": 1276.0, + "submit_p50-ms": 0.001794, + "submit_p95-ms": 0.01362, + "B/op": 395912.0, + "allocs/op": 931.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "sparse_7tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_8tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200649189.0, + "avg_cpu_cores": 0.02026, + "components/block": 32.0, + "cpu-ms/block": 4.065, + "feed_lag_p50-ms": 0.587, + "feed_lag_p95-ms": 1.023, + "mean_width": 8.0, + "ready_p50-ms": 0.6574, + "ready_p95-ms": 1.106, + "residual_p50-ms": 0.5239, + "residual_p95-ms": 0.9372, + "signatures/block": 256.0, + "signatures/s": 1276.0, + "submit_p50-ms": 0.002274, + "submit_p95-ms": 0.02225, + "B/op": 387637.0, + "allocs/op": 876.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "sparse_8tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_8tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200762942.0, + "avg_cpu_cores": 0.0144, + "components/block": 32.0, + "cpu-ms/block": 2.89, + "feed_lag_p50-ms": 0.5812, + "feed_lag_p95-ms": 1.006, + "mean_width": 8.0, + "ready_p50-ms": 0.657, + "ready_p95-ms": 1.083, + "residual_p50-ms": 0.6634, + "residual_p95-ms": 1.04, + "signatures/block": 256.0, + "signatures/s": 1275.0, + "submit_p50-ms": 0.001613, + "submit_p95-ms": 0.01741, + "B/op": 387629.0, + "allocs/op": 875.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "sparse_8tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + }, + { + "name": "BenchmarkTransactionVerificationFlow/generated_1232B/workers_4/target_8/sparse_8tx_200ms_overlap-8", + "iterations": 3, + "ns/op": 200762972.0, + "avg_cpu_cores": 0.01549, + "components/block": 32.0, + "cpu-ms/block": 3.109, + "feed_lag_p50-ms": 0.6425, + "feed_lag_p95-ms": 1.017, + "mean_width": 8.0, + "ready_p50-ms": 0.6983, + "ready_p95-ms": 1.092, + "residual_p50-ms": 0.6307, + "residual_p95-ms": 1.064, + "signatures/block": 256.0, + "signatures/s": 1275.0, + "submit_p50-ms": 0.001583, + "submit_p95-ms": 0.017, + "B/op": 387629.0, + "allocs/op": 875.0, + "source": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "sparse_8tx_200ms_overlap", + "log": "generated-1232-pool-flow.log" + } + ], + "summary": [ + { + "family": "captured", + "workers": 2, + "target": 4, + "scenario": "catchup", + "samples": 9, + "sources": [ + "captured_2622581", + "captured_2622653", + "captured_2624657" + ], + "median": { + "ns/op": 155918266.0, + "cpu-ms/block": 317.8, + "mean_width": 4.0, + "ready_p50-ms": 156.2, + "ready_p95-ms": 162.0, + "submit_p95-ms": 0.007063, + "feed_lag_p95-ms": 0.000491, + "residual_p50-ms": 156.2, + "residual_p95-ms": 162.0, + "signatures/s": 216524.0 + } + }, + { + "family": "captured", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "samples": 9, + "sources": [ + "captured_2622581", + "captured_2622653", + "captured_2624657" + ], + "median": { + "ns/op": 349811570.0, + "cpu-ms/block": 306.6, + "mean_width": 4.0, + "ready_p50-ms": 249.2, + "ready_p95-ms": 340.4, + "submit_p95-ms": 0.01914, + "feed_lag_p95-ms": 191.2, + "residual_p50-ms": 150.1, + "residual_p95-ms": 153.0, + "signatures/s": 96509.0 + } + }, + { + "family": "captured", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "samples": 9, + "sources": [ + "captured_2622581", + "captured_2622653", + "captured_2624657" + ], + "median": { + "ns/op": 201052815.0, + "cpu-ms/block": 301.7, + "mean_width": 3.956, + "ready_p50-ms": 1.588, + "ready_p95-ms": 2.484, + "submit_p95-ms": 0.002805, + "feed_lag_p95-ms": 0.8607, + "residual_p50-ms": 0.9514, + "residual_p95-ms": 1.38, + "signatures/s": 167916.0 + } + }, + { + "family": "captured", + "workers": 2, + "target": 8, + "scenario": "catchup", + "samples": 9, + "sources": [ + "captured_2622581", + "captured_2622653", + "captured_2624657" + ], + "median": { + "ns/op": 77785688.0, + "cpu-ms/block": 158.7, + "mean_width": 8.0, + "ready_p50-ms": 76.4, + "ready_p95-ms": 81.28, + "submit_p95-ms": 0.006552, + "feed_lag_p95-ms": 0.000531, + "residual_p50-ms": 76.4, + "residual_p95-ms": 81.28, + "signatures/s": 434013.0 + } + }, + { + "family": "captured", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "samples": 9, + "sources": [ + "captured_2622581", + "captured_2622653", + "captured_2624657" + ], + "median": { + "ns/op": 276093363.0, + "cpu-ms/block": 154.0, + "mean_width": 8.0, + "ready_p50-ms": 176.0, + "ready_p95-ms": 267.2, + "submit_p95-ms": 0.01683, + "feed_lag_p95-ms": 191.3, + "residual_p50-ms": 75.77, + "residual_p95-ms": 77.62, + "signatures/s": 122277.0 + } + }, + { + "family": "captured", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "samples": 9, + "sources": [ + "captured_2622581", + "captured_2622653", + "captured_2624657" + ], + "median": { + "ns/op": 201053633.0, + "cpu-ms/block": 162.9, + "mean_width": 7.912, + "ready_p50-ms": 1.16, + "ready_p95-ms": 1.674, + "submit_p95-ms": 0.003086, + "feed_lag_p95-ms": 0.973, + "residual_p50-ms": 1.053, + "residual_p95-ms": 1.338, + "signatures/s": 167915.0 + } + }, + { + "family": "captured", + "workers": 4, + "target": 4, + "scenario": "catchup", + "samples": 9, + "sources": [ + "captured_2622581", + "captured_2622653", + "captured_2624657" + ], + "median": { + "ns/op": 78168545.0, + "cpu-ms/block": 308.9, + "mean_width": 4.0, + "ready_p50-ms": 76.63, + "ready_p95-ms": 80.96, + "submit_p95-ms": 0.006863, + "feed_lag_p95-ms": 0.000611, + "residual_p50-ms": 76.63, + "residual_p95-ms": 80.96, + "signatures/s": 431887.0 + } + }, + { + "family": "captured", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "samples": 9, + "sources": [ + "captured_2622581", + "captured_2622653", + "captured_2624657" + ], + "median": { + "ns/op": 277358928.0, + "cpu-ms/block": 307.9, + "mean_width": 4.0, + "ready_p50-ms": 176.8, + "ready_p95-ms": 268.0, + "submit_p95-ms": 0.01613, + "feed_lag_p95-ms": 191.3, + "residual_p50-ms": 76.69, + "residual_p95-ms": 79.21, + "signatures/s": 121720.0 + } + }, + { + "family": "captured", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "samples": 9, + "sources": [ + "captured_2622581", + "captured_2622653", + "captured_2624657" + ], + "median": { + "ns/op": 200821431.0, + "cpu-ms/block": 302.0, + "mean_width": 3.956, + "ready_p50-ms": 1.117, + "ready_p95-ms": 1.659, + "submit_p95-ms": 0.003016, + "feed_lag_p95-ms": 0.9812, + "residual_p50-ms": 0.9866, + "residual_p95-ms": 1.164, + "signatures/s": 168110.0 + } + }, + { + "family": "captured", + "workers": 4, + "target": 8, + "scenario": "catchup", + "samples": 9, + "sources": [ + "captured_2622581", + "captured_2622653", + "captured_2624657" + ], + "median": { + "ns/op": 39479657.0, + "cpu-ms/block": 158.5, + "mean_width": 8.0, + "ready_p50-ms": 39.31, + "ready_p95-ms": 40.18, + "submit_p95-ms": 0.00515, + "feed_lag_p95-ms": 0.000481, + "residual_p50-ms": 39.31, + "residual_p95-ms": 40.18, + "signatures/s": 855124.0 + } + }, + { + "family": "captured", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "samples": 9, + "sources": [ + "captured_2622581", + "captured_2622653", + "captured_2624657" + ], + "median": { + "ns/op": 241004536.0, + "cpu-ms/block": 160.8, + "mean_width": 8.0, + "ready_p50-ms": 141.0, + "ready_p95-ms": 232.2, + "submit_p95-ms": 0.01687, + "feed_lag_p95-ms": 191.3, + "residual_p50-ms": 41.37, + "residual_p95-ms": 43.41, + "signatures/s": 140080.0 + } + }, + { + "family": "captured", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "samples": 9, + "sources": [ + "captured_2622581", + "captured_2622653", + "captured_2624657" + ], + "median": { + "ns/op": 200864502.0, + "cpu-ms/block": 173.4, + "mean_width": 7.912, + "ready_p50-ms": 0.9103, + "ready_p95-ms": 1.39, + "submit_p95-ms": 0.004097, + "feed_lag_p95-ms": 1.002, + "residual_p50-ms": 0.9549, + "residual_p95-ms": 1.132, + "signatures/s": 168074.0 + } + }, + { + "family": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "catchup", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 165232107.0, + "cpu-ms/block": 342.6, + "mean_width": 4.0, + "ready_p50-ms": 163.8, + "ready_p95-ms": 171.9, + "submit_p95-ms": 0.005961, + "feed_lag_p95-ms": 0.000471, + "residual_p50-ms": 163.8, + "residual_p95-ms": 171.9, + "signatures/s": 204319.0 + } + }, + { + "family": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "sparse_4tx_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200548807.0, + "cpu-ms/block": 4.324, + "mean_width": 4.0, + "ready_p50-ms": 0.5792, + "ready_p95-ms": 1.006, + "submit_p95-ms": 0.003516, + "feed_lag_p95-ms": 0.9542, + "residual_p50-ms": 0.4003, + "residual_p95-ms": 0.9766, + "signatures/s": 1276.0 + } + }, + { + "family": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "sparse_7tx_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200734923.0, + "cpu-ms/block": 4.48, + "mean_width": 3.507, + "ready_p50-ms": 0.6193, + "ready_p95-ms": 1.06, + "submit_p95-ms": 0.003787, + "feed_lag_p95-ms": 1.003, + "residual_p50-ms": 0.8195, + "residual_p95-ms": 1.028, + "signatures/s": 1275.0 + } + }, + { + "family": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "sparse_8tx_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200821061.0, + "cpu-ms/block": 4.551, + "mean_width": 4.0, + "ready_p50-ms": 0.6833, + "ready_p95-ms": 1.099, + "submit_p95-ms": 0.01049, + "feed_lag_p95-ms": 1.025, + "residual_p50-ms": 0.8705, + "residual_p95-ms": 0.9203, + "signatures/s": 1275.0 + } + }, + { + "family": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_after_complete", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 359479815.0, + "cpu-ms/block": 329.7, + "mean_width": 4.0, + "ready_p50-ms": 259.5, + "ready_p95-ms": 349.6, + "submit_p95-ms": 0.01513, + "feed_lag_p95-ms": 190.7, + "residual_p50-ms": 159.4, + "residual_p95-ms": 160.9, + "signatures/s": 93913.0 + } + }, + { + "family": "generated_1232B", + "workers": 2, + "target": 4, + "scenario": "tip_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200405779.0, + "cpu-ms/block": 371.8, + "mean_width": 3.77, + "ready_p50-ms": 0.9154, + "ready_p95-ms": 3.697, + "submit_p95-ms": 0.5208, + "feed_lag_p95-ms": 2.251, + "residual_p50-ms": 0.3923, + "residual_p95-ms": 0.5034, + "signatures/s": 168458.0 + } + }, + { + "family": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "catchup", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 86113936.0, + "cpu-ms/block": 180.0, + "mean_width": 8.0, + "ready_p50-ms": 84.68, + "ready_p95-ms": 89.35, + "submit_p95-ms": 0.00537, + "feed_lag_p95-ms": 0.000732, + "residual_p50-ms": 84.68, + "residual_p95-ms": 89.35, + "signatures/s": 392039.0 + } + }, + { + "family": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "sparse_4tx_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200470397.0, + "cpu-ms/block": 4.159, + "mean_width": 4.0, + "ready_p50-ms": 0.5937, + "ready_p95-ms": 1.037, + "submit_p95-ms": 0.003106, + "feed_lag_p95-ms": 0.9768, + "residual_p50-ms": 0.3612, + "residual_p95-ms": 0.8751, + "signatures/s": 1277.0 + } + }, + { + "family": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "sparse_7tx_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200704225.0, + "cpu-ms/block": 4.138, + "mean_width": 6.919, + "ready_p50-ms": 0.6508, + "ready_p95-ms": 1.095, + "submit_p95-ms": 0.003417, + "feed_lag_p95-ms": 0.9991, + "residual_p50-ms": 0.6189, + "residual_p95-ms": 1.101, + "signatures/s": 1276.0 + } + }, + { + "family": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "sparse_8tx_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200621059.0, + "cpu-ms/block": 2.652, + "mean_width": 8.0, + "ready_p50-ms": 0.6781, + "ready_p95-ms": 1.085, + "submit_p95-ms": 0.006863, + "feed_lag_p95-ms": 1.01, + "residual_p50-ms": 0.6671, + "residual_p95-ms": 0.8341, + "signatures/s": 1276.0 + } + }, + { + "family": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_after_complete", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 285857000.0, + "cpu-ms/block": 179.6, + "mean_width": 8.0, + "ready_p50-ms": 185.9, + "ready_p95-ms": 276.0, + "submit_p95-ms": 0.01781, + "feed_lag_p95-ms": 190.5, + "residual_p50-ms": 85.29, + "residual_p95-ms": 87.23, + "signatures/s": 118101.0 + } + }, + { + "family": "generated_1232B", + "workers": 2, + "target": 8, + "scenario": "tip_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200393747.0, + "cpu-ms/block": 192.4, + "mean_width": 7.001, + "ready_p50-ms": 0.5021, + "ready_p95-ms": 1.098, + "submit_p95-ms": 0.002294, + "feed_lag_p95-ms": 0.6496, + "residual_p50-ms": 0.3419, + "residual_p95-ms": 0.6362, + "signatures/s": 168468.0 + } + }, + { + "family": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "catchup", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 93784047.0, + "cpu-ms/block": 368.9, + "mean_width": 4.0, + "ready_p50-ms": 97.21, + "ready_p95-ms": 97.49, + "submit_p95-ms": 0.00538, + "feed_lag_p95-ms": 0.000581, + "residual_p50-ms": 97.21, + "residual_p95-ms": 97.49, + "signatures/s": 359976.0 + } + }, + { + "family": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "sparse_4tx_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200708870.0, + "cpu-ms/block": 4.931, + "mean_width": 4.0, + "ready_p50-ms": 0.6206, + "ready_p95-ms": 1.037, + "submit_p95-ms": 0.004359, + "feed_lag_p95-ms": 0.9819, + "residual_p50-ms": 0.5738, + "residual_p95-ms": 0.8759, + "signatures/s": 1275.0 + } + }, + { + "family": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "sparse_7tx_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200411787.0, + "cpu-ms/block": 5.011, + "mean_width": 3.507, + "ready_p50-ms": 0.5981, + "ready_p95-ms": 1.064, + "submit_p95-ms": 0.008125, + "feed_lag_p95-ms": 0.995, + "residual_p50-ms": 0.2112, + "residual_p95-ms": 0.7328, + "signatures/s": 1277.0 + } + }, + { + "family": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "sparse_8tx_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200734011.0, + "cpu-ms/block": 3.797, + "mean_width": 4.0, + "ready_p50-ms": 0.6915, + "ready_p95-ms": 1.093, + "submit_p95-ms": 0.003516, + "feed_lag_p95-ms": 1.034, + "residual_p50-ms": 0.6833, + "residual_p95-ms": 0.8221, + "signatures/s": 1275.0 + } + }, + { + "family": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_after_complete", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 284770040.0, + "cpu-ms/block": 338.7, + "mean_width": 4.0, + "ready_p50-ms": 184.7, + "ready_p95-ms": 274.8, + "submit_p95-ms": 0.01669, + "feed_lag_p95-ms": 190.5, + "residual_p50-ms": 83.42, + "residual_p95-ms": 84.9, + "signatures/s": 118552.0 + } + }, + { + "family": "generated_1232B", + "workers": 4, + "target": 4, + "scenario": "tip_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200391623.0, + "cpu-ms/block": 360.4, + "mean_width": 3.77, + "ready_p50-ms": 0.5168, + "ready_p95-ms": 1.161, + "submit_p95-ms": 0.002485, + "feed_lag_p95-ms": 0.6699, + "residual_p50-ms": 0.3539, + "residual_p95-ms": 0.5996, + "signatures/s": 168470.0 + } + }, + { + "family": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "catchup", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 56218950.0, + "cpu-ms/block": 223.0, + "mean_width": 8.0, + "ready_p50-ms": 55.44, + "ready_p95-ms": 58.07, + "submit_p95-ms": 0.01248, + "feed_lag_p95-ms": 0.000551, + "residual_p50-ms": 55.44, + "residual_p95-ms": 58.07, + "signatures/s": 600509.0 + } + }, + { + "family": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "sparse_4tx_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200468704.0, + "cpu-ms/block": 5.317, + "mean_width": 4.0, + "ready_p50-ms": 0.6253, + "ready_p95-ms": 1.041, + "submit_p95-ms": 0.005641, + "feed_lag_p95-ms": 0.9687, + "residual_p50-ms": 0.5171, + "residual_p95-ms": 0.7522, + "signatures/s": 1277.0 + } + }, + { + "family": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "sparse_7tx_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200601736.0, + "cpu-ms/block": 4.607, + "mean_width": 6.919, + "ready_p50-ms": 0.6489, + "ready_p95-ms": 1.107, + "submit_p95-ms": 0.008186, + "feed_lag_p95-ms": 1.009, + "residual_p50-ms": 0.6345, + "residual_p95-ms": 1.079, + "signatures/s": 1276.0 + } + }, + { + "family": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "sparse_8tx_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200762942.0, + "cpu-ms/block": 3.109, + "mean_width": 8.0, + "ready_p50-ms": 0.6574, + "ready_p95-ms": 1.092, + "submit_p95-ms": 0.01741, + "feed_lag_p95-ms": 1.017, + "residual_p50-ms": 0.6307, + "residual_p95-ms": 1.04, + "signatures/s": 1275.0 + } + }, + { + "family": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_after_complete", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 246211332.0, + "cpu-ms/block": 185.7, + "mean_width": 8.0, + "ready_p50-ms": 146.2, + "ready_p95-ms": 236.3, + "submit_p95-ms": 0.01546, + "feed_lag_p95-ms": 190.5, + "residual_p50-ms": 46.77, + "residual_p95-ms": 47.71, + "signatures/s": 137118.0 + } + }, + { + "family": "generated_1232B", + "workers": 4, + "target": 8, + "scenario": "tip_200ms_overlap", + "samples": 3, + "sources": [ + "generated_1232B" + ], + "median": { + "ns/op": 200414118.0, + "cpu-ms/block": 192.7, + "mean_width": 7.001, + "ready_p50-ms": 0.5143, + "ready_p95-ms": 1.017, + "submit_p95-ms": 0.001704, + "feed_lag_p95-ms": 0.7849, + "residual_p50-ms": 0.2302, + "residual_p95-ms": 0.831, + "signatures/s": 168451.0 + } + } + ] +} diff --git a/docs/results/sigverify-streaming/2026-09-12-zen5/run_execution_contention.py b/docs/results/sigverify-streaming/2026-09-12-zen5/run_execution_contention.py new file mode 100644 index 00000000..e3e1a6a0 --- /dev/null +++ b/docs/results/sigverify-streaming/2026-09-12-zen5/run_execution_contention.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python3 +"""Compare actual transfer load/execute under separate-process sigverify load. + +This does not benchmark complete block replay, the shared Go scheduler, account +commit, or the dependency planner. Both processes use the same explicitly chosen +eight physical CPU cores. The signature workload uses the production verifier +pool; 200 ms availability is simulated, not a captured network trace. +""" + +import argparse +import json +import os +from pathlib import Path +import random +import shutil +import statistics +import subprocess +import time + + +def cpu_numbers(spec): + result = [] + for part in spec.split(","): + bounds = [int(value) for value in part.split("-")] + if len(bounds) == 1: + result.append(bounds[0]) + elif len(bounds) == 2 and bounds[0] <= bounds[1]: + result.extend(range(bounds[0], bounds[1] + 1)) + else: + raise ValueError(f"invalid CPU list: {spec}") + if len(result) != len(set(result)): + raise ValueError("CPU list contains duplicates") + return result + + +def cpu_topology(cpus): + rows = [] + for cpu in cpus: + root = Path(f"/sys/devices/system/cpu/cpu{cpu}/topology") + rows.append({ + "cpu": cpu, + "package": int((root / "physical_package_id").read_text()), + "core": int((root / "core_id").read_text()), + }) + return rows + + +def parse_benchmarks(path): + results = [] + for line in path.read_text().splitlines(): + fields = line.strip().split() + if len(fields) < 4 or not fields[0].startswith("Benchmark") or not fields[1].isdigit(): + continue + row = {"name": fields[0], "iterations": int(fields[1])} + for index in range(2, len(fields) - 1, 2): + try: + row[fields[index + 1]] = float(fields[index]) + except ValueError: + break + results.append(row) + return results + + +def atomic_json(path, payload): + temporary = path.with_suffix(".tmp") + temporary.write_text(json.dumps(payload, indent=2) + "\n") + temporary.replace(path) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--turbine-bin", required=True, type=Path) + parser.add_argument("--replay-bin", required=True, type=Path) + parser.add_argument("--fixtures", required=True, type=Path) + parser.add_argument("--slot", required=True, type=int) + parser.add_argument("--cpus", required=True, help="Exactly eight physical cores, one logical CPU each, e.g. 0-7") + parser.add_argument("--out", required=True, type=Path) + parser.add_argument("--samples", type=int, default=3) + parser.add_argument("--probe-time", default="500ms", help="Go benchmark benchtime for one execution sample") + parser.add_argument("--catchup-blocks", type=int, default=256) + parser.add_argument("--tip-blocks", type=int, default=30) + parser.add_argument("--warmup-ms", type=int, default=400) + parser.add_argument("--backend", default="r51") + parser.add_argument("--seed", type=int, default=1) + parser.add_argument("--timeout", type=int, default=120) + args = parser.parse_args() + if min(args.samples, args.timeout) < 1 or min(args.catchup_blocks, args.tip_blocks) < 2 or args.warmup_ms < 0: + parser.error("samples/timeout must be positive, block counts >=2, warmup nonnegative") + if not shutil.which("taskset"): + parser.error("this runner requires Linux taskset") + cpus = cpu_numbers(args.cpus) + topology = cpu_topology(cpus) + if len(cpus) != 8 or len({(row["package"], row["core"]) for row in topology}) != 8: + parser.error("select exactly eight distinct physical CPU cores, one logical CPU per core") + if not set(cpus).issubset(os.sched_getaffinity(0)): + parser.error("selected CPUs are outside this process's allowed affinity") + for name in ("turbine_bin", "replay_bin", "fixtures"): + path = getattr(args, name).resolve(strict=True) + setattr(args, name, path) + if not (args.fixtures / f"block-{args.slot}.json").is_file(): + parser.error("selected slot does not exist in fixture directory") + args.out = args.out.resolve() + args.out.mkdir(parents=True, exist_ok=False) + environment = os.environ.copy() + environment["GOMAXPROCS"] = "8" + # Do not inherit rendezvous paths or fixture limits from another experiment. + for name in list(environment): + if name.startswith("MITHRIL_SIGVERIFY_FLOW_"): + del environment[name] + affinity = ["taskset", "--cpu-list", args.cpus] + probe_command = affinity + [ + str(args.replay_bin), "-test.run=^$", + "-test.bench=^BenchmarkLoadAndExecuteTransferResultMode$/^lean$", + f"-test.benchtime={args.probe_time}", "-test.count=1", + f"-test.timeout={args.timeout}s", + ] + results = { + "description": __doc__, + "arguments": {key: str(value) if isinstance(value, Path) else value for key, value in vars(args).items()}, + "topology": topology, + "gomaxprocs_each_process": 8, + "execution_probe_command": probe_command, + "runs": [], + } + manifest = args.out / "comparison.json" + atomic_json(manifest, results) + + def execution_probe(path): + started = time.monotonic() + with path.open("w") as output: + subprocess.run(probe_command, env=environment, stdout=output, stderr=subprocess.STDOUT, + check=True, timeout=args.timeout) + rows = parse_benchmarks(path) + if len(rows) != 1 or "ns/op" not in rows[0]: + raise RuntimeError(f"expected exactly one execution result in {path}") + return {"wall_seconds": time.monotonic() - started, "benchmark": rows[0], "log": str(path)} + + conditions = [(workers, target, mode) for workers in (2, 4) for target in (4, 8) + for mode in ("catchup", "tip_200ms_overlap")] + rng = random.Random(args.seed) + try: + for sample in range(args.samples): + order = conditions.copy() + rng.shuffle(order) + for workers, target, mode in order: + name = f"sample-{sample + 1}-{mode}-workers{workers}-target{target}" + folder = args.out / name + folder.mkdir() + print(f"{name}: baseline execution", flush=True) + baseline = execution_probe(folder / "execution-baseline.log") + ready, start = folder / "verifier.ready", folder / "verifier.start" + load_env = environment | { + "MITHRIL_SIGVERIFY_FLOW_BACKEND": args.backend, + "MITHRIL_SIGVERIFY_FLOW_FIXTURES": str(args.fixtures), + "MITHRIL_SIGVERIFY_FLOW_READY": str(ready), + "MITHRIL_SIGVERIFY_FLOW_START": str(start), + } + blocks = args.catchup_blocks if mode == "catchup" else args.tip_blocks + benchmark = (f"^BenchmarkTransactionVerificationFlow$/^captured_{args.slot}$/" + f"^workers_{workers}$/^target_{target}$/^{mode}$") + load_command = affinity + [str(args.turbine_bin), "-test.run=^$", + f"-test.bench={benchmark}", f"-test.benchtime={blocks}x", + "-test.count=1", f"-test.timeout={args.timeout}s"] + load_log = folder / "sigverify-load.log" + with load_log.open("w") as output: + load = subprocess.Popen(load_command, env=load_env, stdout=output, stderr=subprocess.STDOUT) + try: + deadline = time.monotonic() + 30 + while not ready.exists(): + if load.poll() is not None: + raise RuntimeError(f"signature workload failed before rendezvous; inspect {load_log}") + if time.monotonic() > deadline: + raise TimeoutError("signature workload readiness timed out") + time.sleep(0.01) + start.touch() + time.sleep(args.warmup_ms / 1000) + if load.poll() is not None: + raise RuntimeError("signature load ended during warmup; increase --catchup-blocks/--tip-blocks") + print(f"{name}: concurrent execution", flush=True) + concurrent = execution_probe(folder / "execution-concurrent.log") + # Reject short load windows: otherwise the probe's late + # iterations could quietly measure unloaded execution. + if load.poll() is not None: + raise RuntimeError("signature load ended before execution probe; increase workload block counts") + exit_code = load.wait(timeout=args.timeout) + if exit_code: + raise RuntimeError(f"signature workload exited {exit_code}; inspect {load_log}") + finally: + if load.poll() is None: + load.terminate() # only the child benchmark process + try: + load.wait(timeout=5) + except subprocess.TimeoutExpired: + load.kill() + load.wait() + load_rows = parse_benchmarks(load_log) + if len(load_rows) != 1: + raise RuntimeError(f"expected one signature result in {load_log}") + ratio = concurrent["benchmark"]["ns/op"] / baseline["benchmark"]["ns/op"] + results["runs"].append({ + "sample": sample + 1, "workers": workers, "target": target, "mode": mode, + "baseline": baseline, "concurrent": concurrent, + "execution_slowdown_ratio": ratio, + "sigverify": load_rows[0], "sigverify_command": load_command, + "sigverify_log": str(load_log), + }) + atomic_json(manifest, results) + print(f"{name}: execution ratio {ratio:.3f}x", flush=True) + except Exception as error: + results["error"] = str(error) + atomic_json(manifest, results) + raise + + summary = [] + for workers, target, mode in conditions: + runs = [row for row in results["runs"] if (row["workers"], row["target"], row["mode"]) == (workers, target, mode)] + summary.append({ + "workers": workers, "target": target, "mode": mode, "samples": len(runs), + "median_execution_slowdown_ratio": statistics.median(row["execution_slowdown_ratio"] for row in runs), + "median_execution_baseline_ns": statistics.median(row["baseline"]["benchmark"]["ns/op"] for row in runs), + "median_execution_concurrent_ns": statistics.median(row["concurrent"]["benchmark"]["ns/op"] for row in runs), + "median_sigverify_residual_p95_ms": statistics.median(row["sigverify"]["residual_p95-ms"] for row in runs), + "median_sigverify_cpu_ms_per_block": statistics.median(row["sigverify"]["cpu-ms/block"] for row in runs), + }) + results["summary"] = summary + atomic_json(manifest, results) + print(json.dumps(summary, indent=2)) + + +if __name__ == "__main__": + main() diff --git a/docs/transaction_sigverify_streaming.md b/docs/transaction_sigverify_streaming.md index fc29745b..84d499b3 100644 --- a/docs/transaction_sigverify_streaming.md +++ b/docs/transaction_sigverify_streaming.md @@ -94,3 +94,6 @@ alongside signature work on the same eight physical cores. Its separate processes measure hardware/OS contention, excluding shared Go scheduler/heap effects, full-block dependency planning, commit and live network timing. Pool throughput alone is insufficient evidence of end-to-end replay improvement. + +Measured Zen 5 results, raw logs, and validation details are in the +[September 12 benchmark report](results/sigverify-streaming/2026-09-12-zen5/README.md). From 166ed56e548066a494da191e83907c662430f443 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sat, 12 Sep 2026 09:20:10 -0500 Subject: [PATCH 04/22] Avoid rebuilding cached shred component buffers at completion --- .../2026-09-12-zen5/README.md | 134 + .../2026-09-12-zen5/binaries.json | 4 + .../2026-09-12-zen5/commands.jsonl | 15 + .../2026-09-12-zen5/local-race.txt | 1 + .../2026-09-12-zen5/local-unit.txt | 3 + .../2026-09-12-zen5/method.json | 8 + .../2026-09-12-zen5/native-race.txt | 1 + .../2026-09-12-zen5/native-unit.txt | 1 + .../2026-09-12-zen5/run_native.py | 31 + .../2026-09-12-zen5/sample-1-baseline.txt | 13 + .../2026-09-12-zen5/sample-1-candidate.txt | 13 + .../2026-09-12-zen5/sample-2-baseline.txt | 13 + .../2026-09-12-zen5/sample-2-candidate.txt | 13 + .../2026-09-12-zen5/sample-3-baseline.txt | 13 + .../2026-09-12-zen5/sample-3-candidate.txt | 13 + .../2026-09-12-zen5/sample-4-baseline.txt | 13 + .../2026-09-12-zen5/sample-4-candidate.txt | 13 + .../2026-09-12-zen5/sample-5-baseline.txt | 13 + .../2026-09-12-zen5/sample-5-candidate.txt | 13 + .../2026-09-12-zen5/sample-6-baseline.txt | 13 + .../2026-09-12-zen5/sample-6-candidate.txt | 13 + .../2026-09-12-zen5/summarize.py | 54 + .../2026-09-12-zen5/summary.json | 3358 +++++++++++++++++ docs/transaction_sigverify_streaming.md | 12 +- pkg/turbine/entries.go | 47 +- pkg/turbine/entries_direct_compare_test.go | 100 + 26 files changed, 3911 insertions(+), 14 deletions(-) create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/README.md create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/binaries.json create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/commands.jsonl create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/local-race.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/local-unit.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/method.json create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/native-race.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/native-unit.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/run_native.py create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-1-baseline.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-1-candidate.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-2-baseline.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-2-candidate.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-3-baseline.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-3-candidate.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-4-baseline.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-4-candidate.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-5-baseline.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-5-candidate.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-6-baseline.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-6-candidate.txt create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/summarize.py create mode 100644 docs/results/sigverify-direct-cache/2026-09-12-zen5/summary.json create mode 100644 pkg/turbine/entries_direct_compare_test.go diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/README.md b/docs/results/sigverify-direct-cache/2026-09-12-zen5/README.md new file mode 100644 index 00000000..e462121c --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/README.md @@ -0,0 +1,134 @@ +# Direct shred-to-cache comparison on Zen 5 + +For 33,760 maximum-size transactions arriving over 200 ms, removing redundant +component-buffer construction reduced the work remaining after the final shred +from **25.71 to 11.44 ms**. Whole measured assembly allocated **307.49 to +114.25 MB per block**, a **62.8% reduction** in allocation volume. + +## Change and comparison + +Baseline is `11939e2fda29c60381370242e4a26308fd09c9f9`, which already includes +the new signature batching pool and shred-arrival overlap. Candidate changes +only production `pkg/turbine/entries.go`: completion compares each original +shred data slice directly against the corresponding cached bytes instead of +concatenating the slices into another temporary component buffer first. A cache +miss allocates one buffer at the known size, avoiding repeated append growth. + +Cache reuse still requires matching generation/range and every byte, including +padding. Preparation must finish before its data are read. Changed bytes or +lengths trigger fresh decoding and signature verification. Marker handling, +discarded UpdateParent prefixes, transaction ownership, and final block identity +checks retain their existing behavior. Worker counts, batch target, and arrival +scheduling are unchanged. + +This comparison isolates this optimization on our streaming branch. It is not +a comparison against `alpenglow-dev`, an older Narya revision, or overlap off. +The original streaming report's approximately 29 ms maximum-size tail was from +an earlier run; the fresh baseline here is 25.71 ms. Use these paired runs to +evaluate this change. + +## Method + +- AMD Ryzen 7 9700X, eight physical cores / sixteen hardware threads; Go 1.26.4, + native Narya `r51` at `c265ee9667131a556cd332c252b79de0860c7640`. +- `GOMAXPROCS=8`, affinity CPUs 0–7 (distinct physical cores), nice 10. Existing + Mithril and Lightbringer continued running. These are shared-host measurements; + the live validator was not replaced or restarted. +- Two signature workers, target eight, two preparation workers. No batching timer. +- Six baseline/candidate pairs with order alternating between pairs; five timed + blocks per condition in each process, for 30 timed blocks per variant/condition. + Tables show medians of the six reported values. The residual column is the + median of six per-run p50s, not a pooled percentile or confidence interval. +- Both fixtures contain 33,760 distinct valid one-signature generated transactions + at exactly 228 or 1,232 wire bytes. The earlier short-transaction report used a + captured fixture; this A/B uses generated fixtures on both sides. +- Tip scenarios schedule complete component bursts over 200 ms; catch-up offers + all shreds immediately. A 60 KiB transaction-byte budget defines benchmark + components, with actual entry overhead, FEC padding, and Merkle shreds. This + budget is a fixture choice, not a protocol requirement or recorded arrival trace. +- The unchanged `BenchmarkEntryPrefetchAssembly` exercises actual assembler + ingestion, component decoding, final Merkle identity checks, transaction + signature verification, and block publication. Fixture construction, packet + parsing, shred authentication, transaction execution, PoH, and commit are excluded. + +## Tip results with overlap enabled + +All arrows are baseline → candidate. MB denotes decimal megabytes. Allocation +volume is total Go `B/op` for the measured assembly, not peak memory or retained +heap. CPU-ms sums process CPU time across cores; it is not single-thread wall time. + +| Wire bytes/tx | Final-shred-to-ready | First-arrival-to-ready | CPU-ms/block | Allocated MB/block | +|---|---:|---:|---:|---:| +| 228 | 5.70 → **2.29 ms** | 207.24 → **203.11 ms** | 184.95 → 176.10 | 68.21 → **33.09** | +| 1,232 | 25.71 → **11.44 ms** | 228.25 → **212.79 ms** | 273.20 → 236.80 | 307.49 → **114.25** | + +The maximum-size residual fell **55.5% (2.25× faster)**; first-arrival-to-ready +fell 6.8% because the same approximately 201 ms arrival period remains. Each of +the six maximum-size pairs improved residual latency, with reductions of +49.7–60.5%. The median CPU reduction was 13.3%, but paired CPU changes ranged +from a 5.1% increase to a 16.4% decrease on the shared host. + +Median shred collection remained about 201 ms in both maximum-size variants. +The final signature join remained about 0.11 ms; this optimization removes +assembly copies and allocation work, not cryptographic work. The remaining +11.44 ms includes final assembly and identity checks and has not been separately +profiled in this candidate. These results are not a whole-validator replay speedup. + +## Catch-up and cache-miss checks + +The miss path also improves because it can allocate an exactly sized buffer. +Overlap off exercises full decoding/verification without a prefetch cache. +With all shreds available immediately, overlap on has limited time to prepare +transactions before completion, so it also exercises mostly missed cache work. + +| Arrival | Wire bytes/tx | Overlap | First-arrival-to-ready | Allocated MB/block | +|---|---:|---|---:|---:| +| Catch-up | 228 | Off | 86.28 → 84.63 ms | 59.26 → 32.25 | +| Catch-up | 228 | On | 87.25 → 82.78 ms | 61.99 → 34.79 | +| Catch-up | 1,232 | Off | 122.59 → 109.94 ms | 260.02 → 111.95 | +| Catch-up | 1,232 | On | 118.47 → 108.86 ms | 265.47 → 114.43 | +| Tip, 200 ms | 228 | Off | 286.10 → 284.51 ms | 59.26 → 32.25 | +| Tip, 200 ms | 1,232 | Off | 319.12 → 306.41 ms | 260.10 → 112.08 | + +No tested condition regressed at the median. Small short-transaction differences +should be treated cautiously given background load. No worker-policy or execution +contention experiment was added for this change. + +## Validation and artifacts + +- Local full Turbine race suite passed; Turbine, replay, and node package tests + and Turbine vet passed. +- Native full Turbine tests and three repetitions of targeted decoder/prefetch + race tests passed. +- Five-second local fuzz run completed 132,207 executions without a failure, + checking equivalence to bytewise comparison of concatenated data across varied + fragmentation, changed bytes, and different lengths. +- New regressions reject reuse of a successful signature verdict after signature + bytes change, and reject cached buffers of changed length. Existing padding, + cancellation, UpdateParent, and captured Agave decoding tests also passed. +- Every measured assembly asserts complete transaction coverage, exactly one + signature verification per retained transaction, expected block identity and + footer metadata, and absence of internal native-verifier fallback. + +`sample-*.txt` contains the native raw measurements with trailing whitespace +trimmed. `summary.json` contains all +parsed metrics, medians, and paired ratios; regenerate it with `python3 +summarize.py`. `method.json` records the baseline commit and candidate source +hashes, and `binaries.json` records both native binary hashes. The runner and +`commands.jsonl` record exact native commands; runner paths describe the isolated +benchmark directory on this host. Native validation logs are included alongside +the samples. + +To reproduce each variant on an AVX-512 IFMA host after checking out its source: + +```sh +go test -c -o /tmp/turbine-direct-cache.test ./pkg/turbine +GOMAXPROCS=8 MITHRIL_SIGVERIFY_FLOW_BACKEND=r51 \ +MITHRIL_SIGVERIFY_FLOW_COUNT=33760 \ +taskset --cpu-list 0-7 /tmp/turbine-direct-cache.test \ + -test.run='^$' -test.benchtime=5x -test.count=1 \ + -test.bench='^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*' +``` + +Choose distinct physical core IDs for the target host, and alternate the two +variants six times rather than running all baseline samples first. diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/binaries.json b/docs/results/sigverify-direct-cache/2026-09-12-zen5/binaries.json new file mode 100644 index 00000000..174a7aea --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/binaries.json @@ -0,0 +1,4 @@ +{ + "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test": "f36508c1d113ea8048ba8b516f4a62b0c15b54369efa41c6211bd4bd491c2da8", + "/srv/mithril-sigverify-streaming-20260912/direct-cache-20260912/candidate.test": "77de0df513f29ff66cb60632a99e45933b06fdba7b39a9726c3e81175cc5a11c" +} \ No newline at end of file diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/commands.jsonl b/docs/results/sigverify-direct-cache/2026-09-12-zen5/commands.jsonl new file mode 100644 index 00000000..09dfe220 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/commands.jsonl @@ -0,0 +1,15 @@ +{"name": "native-unit", "args": ["nice", "-n", "10", "/usr/local/go/bin/go", "test", "-overlay=/srv/mithril-sigverify-streaming-20260912/direct-cache-20260912/overlay.json", "-p", "2", "./pkg/turbine"], "seconds": 2.5677993297576904, "exit_code": 0} +{"name": "native-race", "args": ["nice", "-n", "10", "/usr/local/go/bin/go", "test", "-overlay=/srv/mithril-sigverify-streaming-20260912/direct-cache-20260912/overlay.json", "-race", "-p", "2", "./pkg/turbine", "-run", "TestDataShredBatchMatches|TestEntryDecode|TestEntryPrefetch", "-count=3"], "seconds": 4.120096921920776, "exit_code": 0} +{"name": "build", "args": ["nice", "-n", "10", "/usr/local/go/bin/go", "test", "-overlay=/srv/mithril-sigverify-streaming-20260912/direct-cache-20260912/overlay.json", "-c", "-p", "2", "-o", "/srv/mithril-sigverify-streaming-20260912/direct-cache-20260912/candidate.test", "./pkg/turbine"], "seconds": 1.5161521434783936, "exit_code": 0} +{"name": "sample-1-baseline", "args": ["nice", "-n", "10", "taskset", "-c", "0-7", "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", "-test.run=^$", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*", "-test.benchtime=5x", "-test.count=1", "-test.timeout=120s"], "seconds": 12.984313249588013, "exit_code": 0} +{"name": "sample-1-candidate", "args": ["nice", "-n", "10", "taskset", "-c", "0-7", "/srv/mithril-sigverify-streaming-20260912/direct-cache-20260912/candidate.test", "-test.run=^$", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*", "-test.benchtime=5x", "-test.count=1", "-test.timeout=120s"], "seconds": 12.433180570602417, "exit_code": 0} +{"name": "sample-2-candidate", "args": ["nice", "-n", "10", "taskset", "-c", "0-7", "/srv/mithril-sigverify-streaming-20260912/direct-cache-20260912/candidate.test", "-test.run=^$", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*", "-test.benchtime=5x", "-test.count=1", "-test.timeout=120s"], "seconds": 12.583751678466797, "exit_code": 0} +{"name": "sample-2-baseline", "args": ["nice", "-n", "10", "taskset", "-c", "0-7", "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", "-test.run=^$", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*", "-test.benchtime=5x", "-test.count=1", "-test.timeout=120s"], "seconds": 12.884093761444092, "exit_code": 0} +{"name": "sample-3-baseline", "args": ["nice", "-n", "10", "taskset", "-c", "0-7", "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", "-test.run=^$", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*", "-test.benchtime=5x", "-test.count=1", "-test.timeout=120s"], "seconds": 12.88422703742981, "exit_code": 0} +{"name": "sample-3-candidate", "args": ["nice", "-n", "10", "taskset", "-c", "0-7", "/srv/mithril-sigverify-streaming-20260912/direct-cache-20260912/candidate.test", "-test.run=^$", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*", "-test.benchtime=5x", "-test.count=1", "-test.timeout=120s"], "seconds": 12.63422966003418, "exit_code": 0} +{"name": "sample-4-candidate", "args": ["nice", "-n", "10", "taskset", "-c", "0-7", "/srv/mithril-sigverify-streaming-20260912/direct-cache-20260912/candidate.test", "-test.run=^$", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*", "-test.benchtime=5x", "-test.count=1", "-test.timeout=120s"], "seconds": 12.63463544845581, "exit_code": 0} +{"name": "sample-4-baseline", "args": ["nice", "-n", "10", "taskset", "-c", "0-7", "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", "-test.run=^$", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*", "-test.benchtime=5x", "-test.count=1", "-test.timeout=120s"], "seconds": 12.984484434127808, "exit_code": 0} +{"name": "sample-5-baseline", "args": ["nice", "-n", "10", "taskset", "-c", "0-7", "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", "-test.run=^$", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*", "-test.benchtime=5x", "-test.count=1", "-test.timeout=120s"], "seconds": 13.034557819366455, "exit_code": 0} +{"name": "sample-5-candidate", "args": ["nice", "-n", "10", "taskset", "-c", "0-7", "/srv/mithril-sigverify-streaming-20260912/direct-cache-20260912/candidate.test", "-test.run=^$", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*", "-test.benchtime=5x", "-test.count=1", "-test.timeout=120s"], "seconds": 12.936150312423706, "exit_code": 0} +{"name": "sample-6-candidate", "args": ["nice", "-n", "10", "taskset", "-c", "0-7", "/srv/mithril-sigverify-streaming-20260912/direct-cache-20260912/candidate.test", "-test.run=^$", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*", "-test.benchtime=5x", "-test.count=1", "-test.timeout=120s"], "seconds": 12.533514261245728, "exit_code": 0} +{"name": "sample-6-baseline", "args": ["nice", "-n", "10", "taskset", "-c", "0-7", "/srv/mithril-sigverify-streaming-20260912/bin/turbine-final.test", "-test.run=^$", "-test.bench=^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*", "-test.benchtime=5x", "-test.count=1", "-test.timeout=120s"], "seconds": 12.884147882461548, "exit_code": 0} diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/local-race.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/local-race.txt new file mode 100644 index 00000000..c19dec5e --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/local-race.txt @@ -0,0 +1 @@ +ok github.com/Overclock-Validator/mithril/pkg/turbine 10.823s diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/local-unit.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/local-unit.txt new file mode 100644 index 00000000..19a4c26d --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/local-unit.txt @@ -0,0 +1,3 @@ +ok github.com/Overclock-Validator/mithril/pkg/turbine 1.730s +ok github.com/Overclock-Validator/mithril/pkg/replay 1.489s +ok github.com/Overclock-Validator/mithril/cmd/mithril/node 0.955s diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/method.json b/docs/results/sigverify-direct-cache/2026-09-12-zen5/method.json new file mode 100644 index 00000000..e15e9498 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/method.json @@ -0,0 +1,8 @@ +{ + "base_commit": "11939e2fda29c60381370242e4a26308fd09c9f9", + "files": { + "entries.go": "d9c8c79bf9b4264ccbcb5028439b67d552947e690603283eaeb8c5525c90668d", + "entries_direct_compare_test.go": "afde5079ef3e99535a69077d01db41b141ebe69d94e9c290b5894d8804a7ef4e" + }, + "description": "Only entries.go production change, direct slice comparison on hits and exact-capacity component allocation on misses. Existing benchmark unchanged." +} \ No newline at end of file diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/native-race.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/native-race.txt new file mode 100644 index 00000000..23b12477 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/native-race.txt @@ -0,0 +1 @@ +ok github.com/Overclock-Validator/mithril/pkg/turbine 1.933s diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/native-unit.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/native-unit.txt new file mode 100644 index 00000000..fb5e9d2d --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/native-unit.txt @@ -0,0 +1 @@ +ok github.com/Overclock-Validator/mithril/pkg/turbine 0.971s diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/run_native.py b/docs/results/sigverify-direct-cache/2026-09-12-zen5/run_native.py new file mode 100644 index 00000000..d38acd18 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/run_native.py @@ -0,0 +1,31 @@ +from pathlib import Path +import hashlib,json,os,subprocess,time +root=Path('/srv/mithril-sigverify-streaming-20260912');out=root/'direct-cache-20260912';go='/usr/local/go/bin/go' +base=root/'bin/turbine-final.test';candidate=out/'candidate.test' +assert hashlib.sha256(base.read_bytes()).hexdigest()=='f36508c1d113ea8048ba8b516f4a62b0c15b54369efa41c6211bd4bd491c2da8' +method=json.loads((out/'method.json').read_text()) +for name,digest in method['files'].items(): assert hashlib.sha256((out/name).read_bytes()).hexdigest()==digest +results=out/'results';results.mkdir(exist_ok=False) +env=os.environ|{'PATH':'/usr/local/go/bin:'+os.environ.get('PATH',''),'GOMAXPROCS':'8','CGO_ENABLED':'1','GOAMD64':'v1','MITHRIL_SIGVERIFY_FLOW_BACKEND':'r51','MITHRIL_SIGVERIFY_FLOW_COUNT':'33760'} +def health(): + return {'service':subprocess.check_output(['systemctl','show','mithril-alpenglow-pr259.service','--property=ActiveState,SubState,MainPID,ExecMainStartTimestamp'],text=True),'load':Path('/proc/loadavg').read_text().strip()} +def run(name,args,timeout=180): + print('START',name,flush=True);start=time.time() + with (results/(name+'.txt')).open('w') as log: + p=subprocess.run(args,cwd=root/'source',env=env,stdout=log,stderr=subprocess.STDOUT,timeout=timeout) + with (results/'commands.jsonl').open('a') as record: record.write(json.dumps({'name':name,'args':args,'seconds':time.time()-start,'exit_code':p.returncode})+'\n') + if p.returncode: print((results/(name+'.txt')).read_text()[-8000:],flush=True);raise RuntimeError(name) + print('PASS',name,flush=True) +(results/'health-before.json').write_text(json.dumps(health(),indent=2)) +overlay='-overlay='+str(out/'overlay.json') +run('native-unit',['nice','-n','10',go,'test',overlay,'-p','2','./pkg/turbine']) +run('native-race',['nice','-n','10',go,'test',overlay,'-race','-p','2','./pkg/turbine','-run','TestDataShredBatchMatches|TestEntryDecode|TestEntryPrefetch','-count=3']) +run('build',['nice','-n','10',go,'test',overlay,'-c','-p','2','-o',str(candidate),'./pkg/turbine']) +(results/'binaries.json').write_text(json.dumps({str(p):hashlib.sha256(p.read_bytes()).hexdigest() for p in [base,candidate]},indent=2)) +bench='^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*' +for sample in range(6): + order=[('baseline',base),('candidate',candidate)] if sample%2==0 else [('candidate',candidate),('baseline',base)] + for label,binary in order: + run(f'sample-{sample+1}-{label}',['nice','-n','10','taskset','-c','0-7',str(binary),'-test.run=^$','-test.bench='+bench,'-test.benchtime=5x','-test.count=1','-test.timeout=120s']) +(results/'health-after.json').write_text(json.dumps(health(),indent=2)) +print('COMPLETE',flush=True) diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-1-baseline.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-1-baseline.txt new file mode 100644 index 00000000..3c36d02f --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-1-baseline.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 5 85293477 ns/op 1.991 avg_cpu_cores 1.118 collection_p50-ms 1.222 collection_p95-ms 4.256 completion_parse_p50-ms 4.496 completion_parse_p95-ms 75.08 completion_sigverify_p50-ms 76.07 completion_sigverify_p95-ms 128.0 components/block 169.8 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 84.59 full_to_ready_p50-ms 86.22 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 59303225 B/op 208748 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 5 82550451 ns/op 2.066 avg_cpu_cores 1.523 collection_p50-ms 1.637 collection_p95-ms 3.316 completion_parse_p50-ms 3.961 completion_parse_p95-ms 71.99 completion_sigverify_p50-ms 75.24 completion_sigverify_p95-ms 128.0 components/block 170.5 cpu-ms/block 8128 data_shreds/block 161.4 early_verified_tx/block 81.11 full_to_ready_p50-ms 82.92 full_to_ready_p95-ms 7.996 mean_width 0.001032 preparation_wait_p50-ms 0.3401 preparation_wait_p95-ms 33760 tx/block 62210889 B/op 208938 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 5 284431902 ns/op 0.6029 avg_cpu_cores 200.7 collection_p50-ms 200.8 collection_p95-ms 3.423 completion_parse_p50-ms 4.307 completion_parse_p95-ms 75.24 completion_sigverify_p50-ms 77.36 completion_sigverify_p95-ms 128.0 components/block 171.5 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 83.82 full_to_ready_p50-ms 84.72 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 59440300 B/op 208754 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 5 207349787 ns/op 0.8917 avg_cpu_cores 200.6 collection_p50-ms 201.2 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01740 completion_sigverify_p50-ms 0.02466 completion_sigverify_p95-ms 128.0 components/block 184.9 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 5.540 full_to_ready_p50-ms 10.49 full_to_ready_p95-ms 7.912 mean_width 0.02208 preparation_wait_p50-ms 0.02276 preparation_wait_p95-ms 33760 tx/block 68140628 B/op 211686 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 5 120393310 ns/op 1.967 avg_cpu_cores 6.412 collection_p50-ms 6.950 collection_p95-ms 4.396 completion_parse_p50-ms 5.383 completion_parse_p95-ms 83.47 completion_sigverify_p50-ms 85.24 completion_sigverify_p95-ms 691.0 components/block 236.8 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 111.9 full_to_ready_p50-ms 123.9 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260018664 B/op 235461 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 5 121769753 ns/op 2.062 avg_cpu_cores 9.028 collection_p50-ms 9.419 collection_p95-ms 4.405 completion_parse_p50-ms 6.850 completion_parse_p95-ms 83.24 completion_sigverify_p50-ms 85.39 completion_sigverify_p95-ms 691.0 components/block 251.1 cpu-ms/block 44160 data_shreds/block 2038 early_verified_tx/block 114.3 full_to_ready_p50-ms 116.6 full_to_ready_p95-ms 7.930 mean_width 0.005170 preparation_wait_p50-ms 0.006164 preparation_wait_p95-ms 33760 tx/block 265754008 B/op 236457 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 5 322283398 ns/op 0.7984 avg_cpu_cores 201.2 collection_p50-ms 202.9 collection_p95-ms 4.620 completion_parse_p50-ms 5.138 completion_parse_p95-ms 83.39 completion_sigverify_p50-ms 103.0 completion_sigverify_p95-ms 691.0 components/block 257.3 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 118.0 full_to_ready_p50-ms 134.5 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260100848 B/op 235465 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 5 227419935 ns/op 1.187 avg_cpu_cores 201.0 collection_p50-ms 201.2 collection_p95-ms 0.01162 completion_parse_p50-ms 0.01907 completion_parse_p95-ms 0.3091 completion_sigverify_p50-ms 0.4848 completion_sigverify_p95-ms 691.0 components/block 270.0 cpu-ms/block 44160 data_shreds/block 33721 early_verified_tx/block 25.27 full_to_ready_p50-ms 29.18 full_to_ready_p95-ms 7.001 mean_width 0.1238 preparation_wait_p50-ms 0.1314 preparation_wait_p95-ms 33760 tx/block 307386140 B/op 250045 allocs/op +PASS diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-1-candidate.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-1-candidate.txt new file mode 100644 index 00000000..dc5478d4 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-1-candidate.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 5 83134892 ns/op 1.997 avg_cpu_cores 1.178 collection_p50-ms 1.282 collection_p95-ms 3.263 completion_parse_p50-ms 4.674 completion_parse_p95-ms 75.84 completion_sigverify_p50-ms 77.02 completion_sigverify_p95-ms 128.0 components/block 166.0 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 81.77 full_to_ready_p50-ms 84.78 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32178240 B/op 207227 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 5 82695024 ns/op 2.045 avg_cpu_cores 1.573 collection_p50-ms 1.693 collection_p95-ms 3.629 completion_parse_p50-ms 4.856 completion_parse_p95-ms 73.77 completion_sigverify_p50-ms 78.48 completion_sigverify_p95-ms 128.0 components/block 169.1 cpu-ms/block 8128 data_shreds/block 53.80 early_verified_tx/block 80.42 full_to_ready_p50-ms 87.90 full_to_ready_p95-ms 7.997 mean_width 0.0007710 preparation_wait_p50-ms 0.03901 preparation_wait_p95-ms 33760 tx/block 34804320 B/op 207381 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 5 282658756 ns/op 0.5921 avg_cpu_cores 200.8 collection_p50-ms 201.0 collection_p95-ms 3.658 completion_parse_p50-ms 4.300 completion_parse_p95-ms 75.38 completion_sigverify_p50-ms 75.46 completion_sigverify_p95-ms 128.0 components/block 167.4 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 82.09 full_to_ready_p50-ms 83.07 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32233064 B/op 207230 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 5 203125335 ns/op 0.8610 avg_cpu_cores 200.8 collection_p50-ms 201.0 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01565 completion_sigverify_p50-ms 0.01665 completion_sigverify_p95-ms 128.0 components/block 174.9 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 2.212 full_to_ready_p50-ms 2.384 full_to_ready_p95-ms 7.912 mean_width 0.01656 preparation_wait_p50-ms 0.01853 preparation_wait_p95-ms 33760 tx/block 33093758 B/op 210058 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 5 110251450 ns/op 1.891 avg_cpu_cores 6.539 collection_p50-ms 6.757 collection_p95-ms 3.750 completion_parse_p50-ms 4.958 completion_parse_p95-ms 85.91 completion_sigverify_p50-ms 88.56 completion_sigverify_p95-ms 691.0 components/block 208.5 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 103.8 full_to_ready_p50-ms 104.7 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 111932865 B/op 227176 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 5 107413848 ns/op 2.009 avg_cpu_cores 9.056 collection_p50-ms 9.607 collection_p95-ms 3.786 completion_parse_p50-ms 4.344 completion_parse_p95-ms 80.71 completion_sigverify_p50-ms 83.94 completion_sigverify_p95-ms 691.0 components/block 215.8 cpu-ms/block 44160 data_shreds/block 2058 early_verified_tx/block 96.75 full_to_ready_p50-ms 104.7 full_to_ready_p95-ms 7.930 mean_width 0.003566 preparation_wait_p50-ms 0.006684 preparation_wait_p95-ms 33760 tx/block 114424409 B/op 228118 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 5 302887790 ns/op 0.6968 avg_cpu_cores 201.3 collection_p50-ms 201.5 collection_p95-ms 3.559 completion_parse_p50-ms 4.550 completion_parse_p95-ms 84.29 completion_sigverify_p50-ms 85.42 completion_sigverify_p95-ms 691.0 components/block 211.1 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 101.9 full_to_ready_p50-ms 106.5 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 112097940 B/op 227185 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 5 213778904 ns/op 1.327 avg_cpu_cores 201.0 collection_p50-ms 201.4 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.1053 completion_sigverify_p50-ms 0.1169 completion_sigverify_p95-ms 691.0 components/block 283.7 cpu-ms/block 44160 data_shreds/block 33760 early_verified_tx/block 12.72 full_to_ready_p50-ms 12.87 full_to_ready_p95-ms 7.001 mean_width 0.1121 preparation_wait_p50-ms 0.1170 preparation_wait_p95-ms 33760 tx/block 114137292 B/op 241053 allocs/op +PASS diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-2-baseline.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-2-baseline.txt new file mode 100644 index 00000000..9b8f53eb --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-2-baseline.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 5 86659017 ns/op 1.995 avg_cpu_cores 1.144 collection_p50-ms 1.292 collection_p95-ms 4.107 completion_parse_p50-ms 4.424 completion_parse_p95-ms 77.20 completion_sigverify_p50-ms 78.30 completion_sigverify_p95-ms 128.0 components/block 172.9 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 85.56 full_to_ready_p50-ms 87.67 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 59220556 B/op 208743 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 5 86399060 ns/op 2.039 avg_cpu_cores 1.618 collection_p50-ms 1.801 collection_p95-ms 4.302 completion_parse_p50-ms 4.956 completion_parse_p95-ms 75.09 completion_sigverify_p50-ms 77.26 completion_sigverify_p95-ms 128.0 components/block 176.2 cpu-ms/block 8128 data_shreds/block 215.2 early_verified_tx/block 84.87 full_to_ready_p50-ms 87.09 full_to_ready_p95-ms 7.996 mean_width 0.4134 preparation_wait_p50-ms 0.8909 preparation_wait_p95-ms 33760 tx/block 62291787 B/op 208932 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 5 295572582 ns/op 0.6474 avg_cpu_cores 201.2 collection_p50-ms 201.3 collection_p95-ms 4.790 completion_parse_p50-ms 5.857 completion_parse_p95-ms 84.85 completion_sigverify_p50-ms 94.81 completion_sigverify_p95-ms 128.0 components/block 191.3 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 96.63 full_to_ready_p50-ms 109.6 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 59193052 B/op 208741 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 5 207101457 ns/op 0.8795 avg_cpu_cores 200.8 collection_p50-ms 201.1 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01769 completion_sigverify_p50-ms 0.02059 completion_sigverify_p95-ms 128.0 components/block 182.1 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 5.453 full_to_ready_p50-ms 9.838 full_to_ready_p95-ms 7.912 mean_width 0.02266 preparation_wait_p50-ms 0.02356 preparation_wait_p95-ms 33760 tx/block 68303752 B/op 211688 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 5 120279413 ns/op 1.963 avg_cpu_cores 6.307 collection_p50-ms 6.770 collection_p95-ms 4.339 completion_parse_p50-ms 5.810 completion_parse_p95-ms 83.41 completion_sigverify_p50-ms 83.86 completion_sigverify_p95-ms 691.0 components/block 236.1 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 112.0 full_to_ready_p50-ms 121.7 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260018088 B/op 235459 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 5 118298008 ns/op 2.033 avg_cpu_cores 8.346 collection_p50-ms 9.341 collection_p95-ms 3.943 completion_parse_p50-ms 4.342 completion_parse_p95-ms 79.88 completion_sigverify_p50-ms 82.24 completion_sigverify_p95-ms 691.0 components/block 240.4 cpu-ms/block 44160 data_shreds/block 1862 early_verified_tx/block 109.8 full_to_ready_p50-ms 113.1 full_to_ready_p95-ms 7.936 mean_width 0.003836 preparation_wait_p50-ms 0.004838 preparation_wait_p95-ms 33760 tx/block 265236585 B/op 236375 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 5 320159693 ns/op 0.7944 avg_cpu_cores 201.3 collection_p50-ms 201.8 collection_p95-ms 4.424 completion_parse_p50-ms 4.719 completion_parse_p95-ms 88.85 completion_sigverify_p50-ms 95.88 completion_sigverify_p95-ms 691.0 components/block 254.3 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 117.5 full_to_ready_p50-ms 125.4 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260128558 B/op 235467 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 5 228077236 ns/op 1.181 avg_cpu_cores 201.0 collection_p50-ms 201.4 collection_p95-ms 0 completion_parse_p50-ms 0.01689 completion_parse_p95-ms 0.1095 completion_sigverify_p50-ms 0.3459 completion_sigverify_p95-ms 691.0 components/block 269.4 cpu-ms/block 44160 data_shreds/block 33750 early_verified_tx/block 25.85 full_to_ready_p50-ms 31.67 full_to_ready_p95-ms 7.001 mean_width 0.1293 preparation_wait_p50-ms 0.1316 preparation_wait_p95-ms 33760 tx/block 307341900 B/op 250055 allocs/op +PASS diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-2-candidate.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-2-candidate.txt new file mode 100644 index 00000000..3e93e910 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-2-candidate.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 5 86241226 ns/op 1.993 avg_cpu_cores 1.244 collection_p50-ms 1.299 collection_p95-ms 3.632 completion_parse_p50-ms 4.785 completion_parse_p95-ms 77.77 completion_sigverify_p50-ms 81.78 completion_sigverify_p95-ms 128.0 components/block 171.9 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 83.95 full_to_ready_p50-ms 89.64 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32178248 B/op 207227 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 5 81680215 ns/op 2.062 avg_cpu_cores 1.645 collection_p50-ms 1.663 collection_p95-ms 3.510 completion_parse_p50-ms 4.122 completion_parse_p95-ms 73.07 completion_sigverify_p50-ms 75.29 completion_sigverify_p95-ms 128.0 components/block 168.5 cpu-ms/block 8128 data_shreds/block 53.80 early_verified_tx/block 79.46 full_to_ready_p50-ms 82.06 full_to_ready_p95-ms 7.996 mean_width 0.0009020 preparation_wait_p50-ms 0.3958 preparation_wait_p95-ms 33760 tx/block 34810161 B/op 207404 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 5 283720123 ns/op 0.5926 avg_cpu_cores 201.0 collection_p50-ms 201.1 collection_p95-ms 4.150 completion_parse_p50-ms 5.979 completion_parse_p95-ms 75.42 completion_sigverify_p50-ms 76.75 completion_sigverify_p95-ms 128.0 components/block 168.1 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 82.65 full_to_ready_p50-ms 84.17 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32315624 B/op 207235 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 5 202974173 ns/op 0.8767 avg_cpu_cores 200.8 collection_p50-ms 200.9 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01610 completion_sigverify_p50-ms 0.02322 completion_sigverify_p95-ms 128.0 components/block 177.9 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 2.265 full_to_ready_p50-ms 2.340 full_to_ready_p95-ms 7.912 mean_width 0.01836 preparation_wait_p50-ms 0.01868 preparation_wait_p95-ms 33760 tx/block 33013977 B/op 210063 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 5 108052777 ns/op 1.902 avg_cpu_cores 6.441 collection_p50-ms 6.951 collection_p95-ms 3.540 completion_parse_p50-ms 4.962 completion_parse_p95-ms 84.58 completion_sigverify_p50-ms 85.80 completion_sigverify_p95-ms 691.0 components/block 205.6 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 102.2 full_to_ready_p50-ms 106.2 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 111905424 B/op 227175 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 5 113945740 ns/op 1.995 avg_cpu_cores 9.282 collection_p50-ms 11.00 collection_p95-ms 3.909 completion_parse_p50-ms 4.912 completion_parse_p95-ms 84.29 completion_sigverify_p50-ms 94.37 completion_sigverify_p95-ms 691.0 components/block 227.4 cpu-ms/block 44160 data_shreds/block 2048 early_verified_tx/block 104.1 full_to_ready_p50-ms 111.8 full_to_ready_p95-ms 7.929 mean_width 0.003519 preparation_wait_p50-ms 0.006271 preparation_wait_p95-ms 33760 tx/block 114311740 B/op 228106 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 5 304716913 ns/op 0.7074 avg_cpu_cores 201.6 collection_p50-ms 201.7 collection_p95-ms 4.281 completion_parse_p50-ms 4.819 completion_parse_p95-ms 84.11 completion_sigverify_p50-ms 89.27 completion_sigverify_p95-ms 691.0 components/block 215.6 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 102.5 full_to_ready_p50-ms 109.4 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 112180404 B/op 227190 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 5 212222544 ns/op 1.082 avg_cpu_cores 200.9 collection_p50-ms 201.5 collection_p95-ms 0 completion_parse_p50-ms 0.01717 completion_parse_p95-ms 0.09849 completion_sigverify_p50-ms 0.4180 completion_sigverify_p95-ms 691.0 components/block 229.6 cpu-ms/block 44160 data_shreds/block 33741 early_verified_tx/block 11.01 full_to_ready_p50-ms 11.46 full_to_ready_p95-ms 7.001 mean_width 0.1016 preparation_wait_p50-ms 0.1048 preparation_wait_p95-ms 33760 tx/block 114254763 B/op 241078 allocs/op +PASS diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-3-baseline.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-3-baseline.txt new file mode 100644 index 00000000..d6b81ef7 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-3-baseline.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 5 85727666 ns/op 1.994 avg_cpu_cores 1.123 collection_p50-ms 1.319 collection_p95-ms 4.203 completion_parse_p50-ms 4.528 completion_parse_p95-ms 75.36 completion_sigverify_p50-ms 76.36 completion_sigverify_p95-ms 128.0 components/block 171.0 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 85.03 full_to_ready_p50-ms 86.29 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 59248208 B/op 208745 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 5 93032467 ns/op 2.047 avg_cpu_cores 1.604 collection_p50-ms 1.819 collection_p95-ms 4.104 completion_parse_p50-ms 5.156 completion_parse_p95-ms 80.57 completion_sigverify_p50-ms 87.12 completion_sigverify_p95-ms 128.0 components/block 190.4 cpu-ms/block 8128 data_shreds/block 107.6 early_verified_tx/block 91.48 full_to_ready_p50-ms 95.41 full_to_ready_p95-ms 7.997 mean_width 0.0006920 preparation_wait_p50-ms 0.04334 preparation_wait_p95-ms 33760 tx/block 61931662 B/op 208897 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 5 285476051 ns/op 0.6068 avg_cpu_cores 200.9 collection_p50-ms 201.0 collection_p95-ms 3.352 completion_parse_p50-ms 4.118 completion_parse_p95-ms 76.14 completion_sigverify_p50-ms 77.78 completion_sigverify_p95-ms 128.0 components/block 173.2 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 84.82 full_to_ready_p50-ms 85.15 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 59303190 B/op 208749 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 5 207130969 ns/op 0.8933 avg_cpu_cores 200.6 collection_p50-ms 200.7 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01884 completion_sigverify_p50-ms 0.02053 completion_sigverify_p95-ms 128.0 components/block 185.0 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 5.428 full_to_ready_p50-ms 10.36 full_to_ready_p95-ms 7.912 mean_width 0.02344 preparation_wait_p50-ms 0.02789 preparation_wait_p95-ms 33760 tx/block 68193945 B/op 211684 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 5 121503561 ns/op 1.942 avg_cpu_cores 6.518 collection_p50-ms 6.678 collection_p95-ms 4.399 completion_parse_p50-ms 4.914 completion_parse_p95-ms 83.32 completion_sigverify_p50-ms 85.03 completion_sigverify_p95-ms 691.0 components/block 236.0 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 111.7 full_to_ready_p50-ms 122.4 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260018412 B/op 235460 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 5 117837845 ns/op 2.080 avg_cpu_cores 8.353 collection_p50-ms 10.64 collection_p95-ms 4.156 completion_parse_p50-ms 5.325 completion_parse_p95-ms 78.84 completion_sigverify_p50-ms 83.36 completion_sigverify_p95-ms 691.0 components/block 245.1 cpu-ms/block 44160 data_shreds/block 1891 early_verified_tx/block 108.9 full_to_ready_p50-ms 111.3 full_to_ready_p95-ms 7.936 mean_width 0.005110 preparation_wait_p50-ms 0.006629 preparation_wait_p95-ms 33760 tx/block 265386019 B/op 236393 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 5 318075945 ns/op 0.7698 avg_cpu_cores 201.2 collection_p50-ms 201.7 collection_p95-ms 4.535 completion_parse_p50-ms 6.282 completion_parse_p95-ms 86.26 completion_sigverify_p50-ms 88.65 completion_sigverify_p95-ms 691.0 components/block 244.8 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 116.5 full_to_ready_p50-ms 120.7 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260100819 B/op 235465 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 5 227891859 ns/op 1.192 avg_cpu_cores 200.9 collection_p50-ms 201.7 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.1033 completion_sigverify_p50-ms 0.1248 completion_sigverify_p95-ms 691.0 components/block 271.8 cpu-ms/block 44160 data_shreds/block 33760 early_verified_tx/block 25.57 full_to_ready_p50-ms 31.02 full_to_ready_p95-ms 7.001 mean_width 0.1267 preparation_wait_p50-ms 0.1306 preparation_wait_p95-ms 33760 tx/block 307494656 B/op 250070 allocs/op +PASS diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-3-candidate.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-3-candidate.txt new file mode 100644 index 00000000..1ca2e352 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-3-candidate.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 5 85101485 ns/op 1.996 avg_cpu_cores 1.142 collection_p50-ms 1.251 collection_p95-ms 3.633 completion_parse_p50-ms 4.608 completion_parse_p95-ms 77.59 completion_sigverify_p50-ms 79.04 completion_sigverify_p95-ms 128.0 components/block 169.8 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 84.13 full_to_ready_p50-ms 85.35 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32233233 B/op 207230 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 5 83717807 ns/op 2.049 avg_cpu_cores 1.555 collection_p50-ms 1.796 collection_p95-ms 3.655 completion_parse_p50-ms 4.307 completion_parse_p95-ms 75.73 completion_sigverify_p50-ms 77.56 completion_sigverify_p95-ms 128.0 components/block 171.5 cpu-ms/block 8128 data_shreds/block 107.6 early_verified_tx/block 81.90 full_to_ready_p50-ms 84.20 full_to_ready_p95-ms 7.996 mean_width 0.0009510 preparation_wait_p50-ms 0.9772 preparation_wait_p95-ms 33760 tx/block 34781204 B/op 207396 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 5 283918809 ns/op 0.5965 avg_cpu_cores 200.7 collection_p50-ms 201.0 collection_p95-ms 3.509 completion_parse_p50-ms 4.352 completion_parse_p95-ms 76.49 completion_sigverify_p50-ms 79.83 completion_sigverify_p95-ms 128.0 components/block 169.4 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 83.91 full_to_ready_p50-ms 86.88 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32260635 B/op 207231 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 5 203102557 ns/op 0.8531 avg_cpu_cores 200.7 collection_p50-ms 201.0 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01636 completion_sigverify_p50-ms 0.01724 completion_sigverify_p95-ms 128.0 components/block 173.3 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 2.283 full_to_ready_p50-ms 2.706 full_to_ready_p95-ms 7.912 mean_width 0.01703 preparation_wait_p50-ms 0.01848 preparation_wait_p95-ms 33760 tx/block 33091550 B/op 210049 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 5 108851790 ns/op 1.903 avg_cpu_cores 5.880 collection_p50-ms 6.600 collection_p95-ms 3.485 completion_parse_p50-ms 4.888 completion_parse_p95-ms 85.29 completion_sigverify_p50-ms 87.68 completion_sigverify_p95-ms 691.0 components/block 207.2 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 102.1 full_to_ready_p50-ms 105.1 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 112042787 B/op 227182 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 5 105118924 ns/op 1.992 avg_cpu_cores 8.424 collection_p50-ms 9.203 collection_p95-ms 3.571 completion_parse_p50-ms 4.459 completion_parse_p95-ms 79.01 completion_sigverify_p50-ms 82.87 completion_sigverify_p95-ms 691.0 components/block 209.4 cpu-ms/block 44160 data_shreds/block 2009 early_verified_tx/block 95.95 full_to_ready_p50-ms 102.7 full_to_ready_p95-ms 7.932 mean_width 0.002874 preparation_wait_p50-ms 0.004791 preparation_wait_p95-ms 33760 tx/block 114420668 B/op 228093 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 5 311473242 ns/op 0.7254 avg_cpu_cores 201.3 collection_p50-ms 201.6 collection_p95-ms 4.354 completion_parse_p50-ms 4.596 completion_parse_p95-ms 86.07 completion_sigverify_p50-ms 101.8 completion_sigverify_p95-ms 691.0 components/block 226.0 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 106.9 full_to_ready_p50-ms 121.5 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 112015353 B/op 227181 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 5 213548260 ns/op 1.136 avg_cpu_cores 201.1 collection_p50-ms 203.1 collection_p95-ms 0 completion_parse_p50-ms 0.01487 completion_parse_p95-ms 0.1838 completion_sigverify_p50-ms 0.5611 completion_sigverify_p95-ms 691.0 components/block 242.6 cpu-ms/block 44160 data_shreds/block 33721 early_verified_tx/block 11.67 full_to_ready_p50-ms 13.52 full_to_ready_p95-ms 7.001 mean_width 0.1105 preparation_wait_p50-ms 0.1124 preparation_wait_p95-ms 33760 tx/block 114250187 B/op 241068 allocs/op +PASS diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-4-baseline.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-4-baseline.txt new file mode 100644 index 00000000..f1c04f78 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-4-baseline.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 5 85908997 ns/op 1.990 avg_cpu_cores 1.114 collection_p50-ms 1.748 collection_p95-ms 4.036 completion_parse_p50-ms 4.731 completion_parse_p95-ms 75.53 completion_sigverify_p50-ms 76.38 completion_sigverify_p95-ms 128.0 components/block 171.0 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 85.02 full_to_ready_p50-ms 87.14 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 59303256 B/op 208748 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 5 83287865 ns/op 2.055 avg_cpu_cores 1.528 collection_p50-ms 1.624 collection_p95-ms 3.147 completion_parse_p50-ms 4.146 completion_parse_p95-ms 73.56 completion_sigverify_p50-ms 75.42 completion_sigverify_p95-ms 128.0 components/block 171.2 cpu-ms/block 8128 data_shreds/block 53.80 early_verified_tx/block 81.77 full_to_ready_p50-ms 82.43 full_to_ready_p95-ms 7.997 mean_width 0.0007500 preparation_wait_p50-ms 0.0007910 preparation_wait_p95-ms 33760 tx/block 62015553 B/op 208909 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 5 286588469 ns/op 0.6114 avg_cpu_cores 200.8 collection_p50-ms 201.1 collection_p95-ms 3.437 completion_parse_p50-ms 3.868 completion_parse_p95-ms 76.77 completion_sigverify_p50-ms 79.74 completion_sigverify_p95-ms 128.0 components/block 175.2 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 85.44 full_to_ready_p50-ms 88.49 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 59275379 B/op 208745 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 5 208123357 ns/op 0.9981 avg_cpu_cores 200.8 collection_p50-ms 201.3 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.02067 completion_sigverify_p50-ms 0.02899 completion_sigverify_p95-ms 128.0 components/block 207.7 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 7.752 full_to_ready_p50-ms 10.18 full_to_ready_p95-ms 7.912 mean_width 0.02546 preparation_wait_p50-ms 0.02901 preparation_wait_p95-ms 33760 tx/block 68363801 B/op 211716 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 5 134090384 ns/op 1.916 avg_cpu_cores 7.863 collection_p50-ms 9.017 collection_p95-ms 5.347 completion_parse_p50-ms 5.887 completion_parse_p95-ms 90.59 completion_sigverify_p50-ms 103.3 completion_sigverify_p95-ms 691.0 components/block 256.9 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 124.8 full_to_ready_p50-ms 146.3 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260045872 B/op 235462 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 5 118644204 ns/op 2.083 avg_cpu_cores 8.456 collection_p50-ms 8.783 collection_p95-ms 3.887 completion_parse_p50-ms 4.874 completion_parse_p95-ms 80.59 completion_sigverify_p50-ms 82.30 completion_sigverify_p95-ms 691.0 components/block 247.2 cpu-ms/block 44160 data_shreds/block 1931 early_verified_tx/block 109.2 full_to_ready_p50-ms 112.4 full_to_ready_p95-ms 7.935 mean_width 0.004411 preparation_wait_p50-ms 0.004846 preparation_wait_p95-ms 33760 tx/block 265440401 B/op 236406 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 5 316166377 ns/op 0.7691 avg_cpu_cores 201.3 collection_p50-ms 201.6 collection_p95-ms 4.191 completion_parse_p50-ms 4.500 completion_parse_p95-ms 84.55 completion_sigverify_p50-ms 87.88 completion_sigverify_p95-ms 691.0 components/block 243.2 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 115.2 full_to_ready_p50-ms 119.9 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 259963968 B/op 235459 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 5 230004734 ns/op 1.196 avg_cpu_cores 201.3 collection_p50-ms 201.8 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.1175 completion_sigverify_p50-ms 0.1314 completion_sigverify_p95-ms 691.0 components/block 275.1 cpu-ms/block 44160 data_shreds/block 33760 early_verified_tx/block 28.30 full_to_ready_p50-ms 32.14 full_to_ready_p95-ms 7.001 mean_width 0.1213 preparation_wait_p50-ms 0.1297 preparation_wait_p95-ms 33760 tx/block 307660395 B/op 250071 allocs/op +PASS diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-4-candidate.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-4-candidate.txt new file mode 100644 index 00000000..929720e8 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-4-candidate.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 5 83997761 ns/op 1.990 avg_cpu_cores 1.213 collection_p50-ms 1.275 collection_p95-ms 3.383 completion_parse_p50-ms 4.718 completion_parse_p95-ms 76.12 completion_sigverify_p50-ms 77.08 completion_sigverify_p95-ms 128.0 components/block 167.2 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 82.75 full_to_ready_p50-ms 84.96 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32260841 B/op 207232 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 5 82378390 ns/op 2.052 avg_cpu_cores 1.582 collection_p50-ms 1.764 collection_p95-ms 3.427 completion_parse_p50-ms 4.179 completion_parse_p95-ms 73.70 completion_sigverify_p50-ms 77.03 completion_sigverify_p95-ms 128.0 components/block 169.1 cpu-ms/block 8128 data_shreds/block 107.6 early_verified_tx/block 80.55 full_to_ready_p50-ms 83.25 full_to_ready_p95-ms 7.996 mean_width 0.0007110 preparation_wait_p50-ms 0.5494 preparation_wait_p95-ms 33760 tx/block 34778107 B/op 207384 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 5 285362255 ns/op 0.6030 avg_cpu_cores 201.1 collection_p50-ms 201.1 collection_p95-ms 4.124 completion_parse_p50-ms 4.297 completion_parse_p95-ms 75.54 completion_sigverify_p50-ms 83.57 completion_sigverify_p95-ms 128.0 components/block 172.1 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 83.22 full_to_ready_p50-ms 91.69 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32287976 B/op 207232 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 5 203641348 ns/op 1.007 avg_cpu_cores 200.9 collection_p50-ms 201.2 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01979 completion_sigverify_p50-ms 0.02696 completion_sigverify_p95-ms 128.0 components/block 205.1 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 2.728 full_to_ready_p50-ms 2.801 full_to_ready_p95-ms 7.912 mean_width 0.02069 preparation_wait_p50-ms 0.02411 preparation_wait_p95-ms 33760 tx/block 33091185 B/op 210047 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 5 109632016 ns/op 1.892 avg_cpu_cores 6.787 collection_p50-ms 7.832 collection_p95-ms 3.642 completion_parse_p50-ms 4.960 completion_parse_p95-ms 85.31 completion_sigverify_p50-ms 88.30 completion_sigverify_p95-ms 691.0 components/block 207.4 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 102.1 full_to_ready_p50-ms 108.2 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 111905504 B/op 227176 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 5 106324511 ns/op 2.007 avg_cpu_cores 8.568 collection_p50-ms 9.414 collection_p95-ms 3.655 completion_parse_p50-ms 4.119 completion_parse_p95-ms 79.89 completion_sigverify_p50-ms 84.03 completion_sigverify_p95-ms 691.0 components/block 213.4 cpu-ms/block 44160 data_shreds/block 1862 early_verified_tx/block 96.25 full_to_ready_p50-ms 103.4 full_to_ready_p95-ms 7.936 mean_width 0.003455 preparation_wait_p50-ms 0.006185 preparation_wait_p95-ms 33760 tx/block 114547820 B/op 228037 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 5 311267426 ns/op 0.7263 avg_cpu_cores 201.1 collection_p50-ms 202.1 collection_p95-ms 3.621 completion_parse_p50-ms 5.061 completion_parse_p95-ms 87.90 completion_sigverify_p50-ms 103.1 completion_sigverify_p95-ms 691.0 components/block 226.1 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 105.2 full_to_ready_p50-ms 119.8 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 111932950 B/op 227177 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 5 212794779 ns/op 1.109 avg_cpu_cores 201.0 collection_p50-ms 202.1 collection_p95-ms 0 completion_parse_p50-ms 0.01383 completion_parse_p95-ms 0.1078 completion_sigverify_p50-ms 0.3044 completion_sigverify_p95-ms 691.0 components/block 236.0 cpu-ms/block 44160 data_shreds/block 33750 early_verified_tx/block 11.18 full_to_ready_p50-ms 12.44 full_to_ready_p95-ms 7.001 mean_width 0.1053 preparation_wait_p50-ms 0.1134 preparation_wait_p95-ms 33760 tx/block 114247308 B/op 241055 allocs/op +PASS diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-5-baseline.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-5-baseline.txt new file mode 100644 index 00000000..51f7d9c5 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-5-baseline.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 5 86664771 ns/op 1.983 avg_cpu_cores 1.195 collection_p50-ms 1.430 collection_p95-ms 4.332 completion_parse_p50-ms 5.166 completion_parse_p95-ms 76.40 completion_sigverify_p50-ms 77.61 completion_sigverify_p95-ms 128.0 components/block 171.9 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 86.21 full_to_ready_p50-ms 87.53 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 59275827 B/op 208747 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 5 100446776 ns/op 2.026 avg_cpu_cores 2.064 collection_p50-ms 2.165 collection_p95-ms 4.494 completion_parse_p50-ms 5.885 completion_parse_p95-ms 89.05 completion_sigverify_p50-ms 89.98 completion_sigverify_p95-ms 128.0 components/block 203.5 cpu-ms/block 8128 data_shreds/block 215.2 early_verified_tx/block 100.3 full_to_ready_p50-ms 104.8 full_to_ready_p95-ms 7.996 mean_width 0.0008210 preparation_wait_p50-ms 0.001052 preparation_wait_p95-ms 33760 tx/block 61973884 B/op 208905 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 5 286167802 ns/op 0.6098 avg_cpu_cores 200.7 collection_p50-ms 200.8 collection_p95-ms 3.934 completion_parse_p50-ms 4.419 completion_parse_p95-ms 76.59 completion_sigverify_p50-ms 77.91 completion_sigverify_p95-ms 128.0 components/block 174.5 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 85.40 full_to_ready_p50-ms 86.57 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 59193008 B/op 208742 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 5 207732756 ns/op 0.9023 avg_cpu_cores 200.9 collection_p50-ms 201.1 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01991 completion_sigverify_p50-ms 0.03155 completion_sigverify_p95-ms 128.0 components/block 187.4 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 7.562 full_to_ready_p50-ms 9.053 full_to_ready_p95-ms 7.912 mean_width 0.02246 preparation_wait_p50-ms 0.02459 preparation_wait_p95-ms 33760 tx/block 68138699 B/op 211680 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 5 123685493 ns/op 1.960 avg_cpu_cores 6.351 collection_p50-ms 7.320 collection_p95-ms 4.749 completion_parse_p50-ms 4.856 completion_parse_p95-ms 84.69 completion_sigverify_p50-ms 86.93 completion_sigverify_p95-ms 691.0 components/block 242.4 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 118.8 full_to_ready_p50-ms 123.1 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260100822 B/op 235464 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 5 119201510 ns/op 2.078 avg_cpu_cores 8.512 collection_p50-ms 9.358 collection_p95-ms 4.359 completion_parse_p50-ms 4.711 completion_parse_p95-ms 80.38 completion_sigverify_p50-ms 82.91 completion_sigverify_p95-ms 691.0 components/block 247.7 cpu-ms/block 44160 data_shreds/block 1950 early_verified_tx/block 110.3 full_to_ready_p50-ms 113.1 full_to_ready_p95-ms 7.933 mean_width 0.004432 preparation_wait_p50-ms 0.006141 preparation_wait_p95-ms 33760 tx/block 265605086 B/op 236424 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 5 326825723 ns/op 0.8088 avg_cpu_cores 201.9 collection_p50-ms 202.2 collection_p95-ms 4.877 completion_parse_p50-ms 5.064 completion_parse_p95-ms 94.15 completion_sigverify_p50-ms 106.9 completion_sigverify_p95-ms 691.0 components/block 264.3 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 127.9 full_to_ready_p50-ms 139.6 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260100937 B/op 235466 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 5 229345156 ns/op 1.197 avg_cpu_cores 201.0 collection_p50-ms 201.0 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.1071 completion_sigverify_p50-ms 0.1224 completion_sigverify_p95-ms 691.0 components/block 274.6 cpu-ms/block 44160 data_shreds/block 33760 early_verified_tx/block 28.39 full_to_ready_p50-ms 31.10 full_to_ready_p95-ms 7.001 mean_width 0.1263 preparation_wait_p50-ms 0.1313 preparation_wait_p95-ms 33760 tx/block 307580156 B/op 250079 allocs/op +PASS diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-5-candidate.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-5-candidate.txt new file mode 100644 index 00000000..555f7b28 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-5-candidate.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 5 86939212 ns/op 2.001 avg_cpu_cores 1.138 collection_p50-ms 1.528 collection_p95-ms 3.315 completion_parse_p50-ms 4.947 completion_parse_p95-ms 80.42 completion_sigverify_p50-ms 81.95 completion_sigverify_p95-ms 128.0 components/block 173.9 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 86.28 full_to_ready_p50-ms 89.75 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32343236 B/op 207237 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 5 93051198 ns/op 2.052 avg_cpu_cores 1.908 collection_p50-ms 2.115 collection_p95-ms 3.802 completion_parse_p50-ms 5.595 completion_parse_p95-ms 83.19 completion_sigverify_p50-ms 89.15 completion_sigverify_p95-ms 128.0 components/block 191.0 cpu-ms/block 8128 data_shreds/block 161.4 early_verified_tx/block 89.81 full_to_ready_p50-ms 98.24 full_to_ready_p95-ms 7.995 mean_width 0.1421 preparation_wait_p50-ms 1.063 preparation_wait_p95-ms 33760 tx/block 34757734 B/op 207410 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 5 285099098 ns/op 0.6058 avg_cpu_cores 200.8 collection_p50-ms 201.0 collection_p95-ms 3.323 completion_parse_p50-ms 4.328 completion_parse_p95-ms 77.77 completion_sigverify_p50-ms 77.90 completion_sigverify_p95-ms 128.0 components/block 172.7 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 84.74 full_to_ready_p50-ms 85.63 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32205774 B/op 207229 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 5 203024092 ns/op 0.8731 avg_cpu_cores 200.6 collection_p50-ms 200.9 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01703 completion_sigverify_p50-ms 0.02449 completion_sigverify_p95-ms 128.0 components/block 177.3 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 2.292 full_to_ready_p50-ms 2.390 full_to_ready_p95-ms 7.912 mean_width 0.01816 preparation_wait_p50-ms 0.01985 preparation_wait_p95-ms 33760 tx/block 33175731 B/op 210061 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 5 116086892 ns/op 1.896 avg_cpu_cores 7.729 collection_p50-ms 9.382 collection_p95-ms 3.985 completion_parse_p50-ms 6.018 completion_parse_p95-ms 84.53 completion_sigverify_p50-ms 100.6 completion_sigverify_p95-ms 691.0 components/block 220.2 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 109.4 full_to_ready_p50-ms 119.2 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 111960304 B/op 227177 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 5 115194449 ns/op 1.969 avg_cpu_cores 9.432 collection_p50-ms 10.35 collection_p95-ms 3.882 completion_parse_p50-ms 4.979 completion_parse_p95-ms 85.65 completion_sigverify_p50-ms 94.03 completion_sigverify_p95-ms 691.0 components/block 226.9 cpu-ms/block 44160 data_shreds/block 1735 early_verified_tx/block 104.0 full_to_ready_p50-ms 111.4 full_to_ready_p95-ms 7.941 mean_width 0.004026 preparation_wait_p50-ms 0.005541 preparation_wait_p95-ms 33760 tx/block 114430456 B/op 227975 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 5 308111487 ns/op 0.7254 avg_cpu_cores 201.5 collection_p50-ms 201.6 collection_p95-ms 4.284 completion_parse_p50-ms 5.167 completion_parse_p95-ms 88.66 completion_sigverify_p50-ms 94.67 completion_sigverify_p95-ms 691.0 components/block 223.5 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 109.1 full_to_ready_p50-ms 113.6 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 112125502 B/op 227188 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 5 212791787 ns/op 1.116 avg_cpu_cores 200.9 collection_p50-ms 201.0 collection_p95-ms 0.007484 completion_parse_p50-ms 0.01159 completion_parse_p95-ms 0.3225 completion_sigverify_p50-ms 0.3603 completion_sigverify_p95-ms 691.0 components/block 237.6 cpu-ms/block 44160 data_shreds/block 33731 early_verified_tx/block 11.63 full_to_ready_p50-ms 13.06 full_to_ready_p95-ms 7.001 mean_width 0.1056 preparation_wait_p50-ms 0.1122 preparation_wait_p95-ms 33760 tx/block 114082224 B/op 241044 allocs/op +PASS diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-6-baseline.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-6-baseline.txt new file mode 100644 index 00000000..a38e0522 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-6-baseline.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 5 87321492 ns/op 1.992 avg_cpu_cores 1.139 collection_p50-ms 1.213 collection_p95-ms 4.206 completion_parse_p50-ms 4.314 completion_parse_p95-ms 76.92 completion_sigverify_p50-ms 79.11 completion_sigverify_p95-ms 128.0 components/block 173.9 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 86.31 full_to_ready_p50-ms 87.54 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 59110704 B/op 208737 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 5 88098284 ns/op 2.039 avg_cpu_cores 1.612 collection_p50-ms 1.726 collection_p95-ms 3.683 completion_parse_p50-ms 4.031 completion_parse_p95-ms 78.16 completion_sigverify_p50-ms 83.11 completion_sigverify_p95-ms 128.0 components/block 179.7 cpu-ms/block 8128 data_shreds/block 107.6 early_verified_tx/block 87.14 full_to_ready_p50-ms 92.06 full_to_ready_p95-ms 7.997 mean_width 0.0007710 preparation_wait_p50-ms 0.0008420 preparation_wait_p95-ms 33760 tx/block 61902907 B/op 208887 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 5 286038446 ns/op 0.6079 avg_cpu_cores 200.9 collection_p50-ms 201.1 collection_p95-ms 3.731 completion_parse_p50-ms 4.179 completion_parse_p95-ms 76.35 completion_sigverify_p50-ms 78.24 completion_sigverify_p95-ms 128.0 components/block 173.9 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 85.53 full_to_ready_p50-ms 85.90 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 59248048 B/op 208744 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 5 206915717 ns/op 0.8863 avg_cpu_cores 200.6 collection_p50-ms 200.9 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01665 completion_sigverify_p50-ms 0.02066 completion_sigverify_p95-ms 128.0 components/block 183.4 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 5.859 full_to_ready_p50-ms 9.065 full_to_ready_p95-ms 7.912 mean_width 0.02179 preparation_wait_p50-ms 0.02377 preparation_wait_p95-ms 33760 tx/block 68223072 B/op 211693 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 5 125985246 ns/op 1.958 avg_cpu_cores 6.541 collection_p50-ms 7.319 collection_p95-ms 5.022 completion_parse_p50-ms 5.127 completion_parse_p95-ms 86.20 completion_sigverify_p50-ms 88.08 completion_sigverify_p95-ms 691.0 components/block 246.7 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 118.0 full_to_ready_p50-ms 130.3 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 259990851 B/op 235458 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 5 118097846 ns/op 2.073 avg_cpu_cores 8.234 collection_p50-ms 9.436 collection_p95-ms 3.897 completion_parse_p50-ms 4.584 completion_parse_p95-ms 80.46 completion_sigverify_p50-ms 84.12 completion_sigverify_p95-ms 691.0 components/block 244.8 cpu-ms/block 44160 data_shreds/block 2009 early_verified_tx/block 110.1 full_to_ready_p50-ms 111.6 full_to_ready_p95-ms 7.932 mean_width 0.003857 preparation_wait_p50-ms 0.005016 preparation_wait_p95-ms 33760 tx/block 265493705 B/op 236438 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 5 318006733 ns/op 0.7756 avg_cpu_cores 201.6 collection_p50-ms 201.9 collection_p95-ms 5.760 completion_parse_p50-ms 6.369 completion_parse_p95-ms 84.75 completion_sigverify_p50-ms 86.11 completion_sigverify_p95-ms 691.0 components/block 246.7 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 117.0 full_to_ready_p50-ms 120.6 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 260100320 B/op 235463 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 5 228423719 ns/op 1.210 avg_cpu_cores 201.1 collection_p50-ms 201.6 collection_p95-ms 0 completion_parse_p50-ms 0.02031 completion_parse_p95-ms 0.1011 completion_sigverify_p50-ms 0.5984 completion_sigverify_p95-ms 691.0 components/block 276.4 cpu-ms/block 44160 data_shreds/block 33731 early_verified_tx/block 25.52 full_to_ready_p50-ms 30.97 full_to_ready_p95-ms 7.001 mean_width 0.1290 preparation_wait_p50-ms 0.1313 preparation_wait_p95-ms 33760 tx/block 307480184 B/op 250043 allocs/op +PASS diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-6-candidate.txt b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-6-candidate.txt new file mode 100644 index 00000000..22942a52 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/sample-6-candidate.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 5 84167116 ns/op 1.988 avg_cpu_cores 1.144 collection_p50-ms 1.232 collection_p95-ms 3.413 completion_parse_p50-ms 4.888 completion_parse_p95-ms 76.76 completion_sigverify_p50-ms 78.17 completion_sigverify_p95-ms 128.0 components/block 167.3 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 82.76 full_to_ready_p50-ms 84.18 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32288243 B/op 207233 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 5 82857969 ns/op 2.043 avg_cpu_cores 1.523 collection_p50-ms 1.630 collection_p95-ms 3.449 completion_parse_p50-ms 5.611 completion_parse_p95-ms 74.33 completion_sigverify_p50-ms 75.86 completion_sigverify_p95-ms 128.0 components/block 169.3 cpu-ms/block 8128 data_shreds/block 215.2 early_verified_tx/block 81.41 full_to_ready_p50-ms 83.26 full_to_ready_p95-ms 7.997 mean_width 0.0009220 preparation_wait_p50-ms 0.07145 preparation_wait_p95-ms 33760 tx/block 34911190 B/op 207376 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 5 290467977 ns/op 0.6306 avg_cpu_cores 200.7 collection_p50-ms 201.2 collection_p95-ms 3.626 completion_parse_p50-ms 5.792 completion_parse_p95-ms 82.06 completion_sigverify_p50-ms 91.61 completion_sigverify_p95-ms 128.0 components/block 183.2 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 91.79 full_to_ready_p50-ms 98.00 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32205832 B/op 207229 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 5 203268498 ns/op 0.8573 avg_cpu_cores 200.9 collection_p50-ms 201.0 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.01763 completion_sigverify_p50-ms 0.02967 completion_sigverify_p95-ms 128.0 components/block 174.3 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 2.370 full_to_ready_p50-ms 2.511 full_to_ready_p95-ms 7.912 mean_width 0.01763 preparation_wait_p50-ms 0.02097 preparation_wait_p95-ms 33760 tx/block 33202425 B/op 210058 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 5 113739331 ns/op 1.899 avg_cpu_cores 6.908 collection_p50-ms 8.387 collection_p95-ms 3.847 completion_parse_p50-ms 5.097 completion_parse_p95-ms 89.13 completion_sigverify_p50-ms 91.48 completion_sigverify_p95-ms 691.0 components/block 216.0 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 106.6 full_to_ready_p50-ms 111.0 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 112042899 B/op 227183 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 5 110309323 ns/op 2.000 avg_cpu_cores 9.223 collection_p50-ms 9.799 collection_p95-ms 3.510 completion_parse_p50-ms 4.880 completion_parse_p95-ms 80.54 completion_sigverify_p50-ms 90.01 completion_sigverify_p95-ms 691.0 components/block 220.6 cpu-ms/block 44160 data_shreds/block 2078 early_verified_tx/block 99.70 full_to_ready_p50-ms 109.9 full_to_ready_p95-ms 7.929 mean_width 0.003385 preparation_wait_p50-ms 0.006176 preparation_wait_p95-ms 33760 tx/block 114532560 B/op 228120 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 5 302321983 ns/op 0.6997 avg_cpu_cores 201.0 collection_p50-ms 201.5 collection_p95-ms 3.910 completion_parse_p50-ms 4.540 completion_parse_p95-ms 83.33 completion_sigverify_p50-ms 85.29 completion_sigverify_p95-ms 691.0 components/block 211.5 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 101.1 full_to_ready_p50-ms 105.9 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 112070532 B/op 227185 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 5 212242945 ns/op 1.089 avg_cpu_cores 200.8 collection_p50-ms 201.3 collection_p95-ms 0 completion_parse_p50-ms 0.01212 completion_parse_p95-ms 0.09627 completion_sigverify_p50-ms 0.3047 completion_sigverify_p95-ms 691.0 components/block 231.2 cpu-ms/block 44160 data_shreds/block 33750 early_verified_tx/block 11.24 full_to_ready_p50-ms 11.41 full_to_ready_p95-ms 7.001 mean_width 0.1010 preparation_wait_p50-ms 0.1062 preparation_wait_p95-ms 33760 tx/block 114276446 B/op 241076 allocs/op +PASS diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/summarize.py b/docs/results/sigverify-direct-cache/2026-09-12-zen5/summarize.py new file mode 100644 index 00000000..fe0980e0 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/summarize.py @@ -0,0 +1,54 @@ +"""Summarize alternating native baseline/candidate assembly benchmark runs.""" + +import json +import statistics +from pathlib import Path + + +root = Path(__file__).resolve().parent +rows = [] +for path in sorted(root.glob("sample-*.txt")): + for line in path.read_text().splitlines(): + if not line.startswith("BenchmarkEntryPrefetchAssembly/"): + continue + fields = line.split() + rows.append({ + "sample": int(path.stem.split("-")[1]), + "variant": path.stem.split("-")[2], + "name": fields[0], + "iterations": int(fields[1]), + "metrics": { + fields[i + 1]: float(fields[i]) + for i in range(2, len(fields), 2) + }, + }) + +assert len(rows) == 6 * 2 * 8 +summary = [] +for name in sorted({row["name"] for row in rows}): + variants = { + variant: [row for row in rows if row["name"] == name and row["variant"] == variant] + for variant in ("baseline", "candidate") + } + for samples in variants.values(): + assert len(samples) == 6 + assert sorted(row["sample"] for row in samples) == list(range(1, 7)) + assert all(row["iterations"] == 5 for row in samples) + medians = { + variant: { + metric: statistics.median(row["metrics"][metric] for row in samples) + for metric in samples[0]["metrics"] + } + for variant, samples in variants.items() + } + paired = { + metric: [ + next(row for row in variants["candidate"] if row["sample"] == sample)["metrics"][metric] + / next(row for row in variants["baseline"] if row["sample"] == sample)["metrics"][metric] + for sample in range(1, 7) + ] + for metric in ("ns/op", "cpu-ms/block", "B/op", "full_to_ready_p50-ms") + } + summary.append({"name": name, "medians": medians, "paired_ratios": paired}) + +(root / "summary.json").write_text(json.dumps({"samples": rows, "summary": summary}, indent=2) + "\n") diff --git a/docs/results/sigverify-direct-cache/2026-09-12-zen5/summary.json b/docs/results/sigverify-direct-cache/2026-09-12-zen5/summary.json new file mode 100644 index 00000000..5d9f76d8 --- /dev/null +++ b/docs/results/sigverify-direct-cache/2026-09-12-zen5/summary.json @@ -0,0 +1,3358 @@ +{ + "samples": [ + { + "sample": 1, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 85293477.0, + "avg_cpu_cores": 1.991, + "collection_p50-ms": 1.118, + "collection_p95-ms": 1.222, + "completion_parse_p50-ms": 4.256, + "completion_parse_p95-ms": 4.496, + "completion_sigverify_p50-ms": 75.08, + "completion_sigverify_p95-ms": 76.07, + "components/block": 128.0, + "cpu-ms/block": 169.8, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 84.59, + "full_to_ready_p95-ms": 86.22, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59303225.0, + "allocs/op": 208748.0 + } + }, + { + "sample": 1, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 82550451.0, + "avg_cpu_cores": 2.066, + "collection_p50-ms": 1.523, + "collection_p95-ms": 1.637, + "completion_parse_p50-ms": 3.316, + "completion_parse_p95-ms": 3.961, + "completion_sigverify_p50-ms": 71.99, + "completion_sigverify_p95-ms": 75.24, + "components/block": 128.0, + "cpu-ms/block": 170.5, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 161.4, + "full_to_ready_p50-ms": 81.11, + "full_to_ready_p95-ms": 82.92, + "mean_width": 7.996, + "preparation_wait_p50-ms": 0.001032, + "preparation_wait_p95-ms": 0.3401, + "tx/block": 33760.0, + "B/op": 62210889.0, + "allocs/op": 208938.0 + } + }, + { + "sample": 1, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 284431902.0, + "avg_cpu_cores": 0.6029, + "collection_p50-ms": 200.7, + "collection_p95-ms": 200.8, + "completion_parse_p50-ms": 3.423, + "completion_parse_p95-ms": 4.307, + "completion_sigverify_p50-ms": 75.24, + "completion_sigverify_p95-ms": 77.36, + "components/block": 128.0, + "cpu-ms/block": 171.5, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 83.82, + "full_to_ready_p95-ms": 84.72, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59440300.0, + "allocs/op": 208754.0 + } + }, + { + "sample": 1, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 207349787.0, + "avg_cpu_cores": 0.8917, + "collection_p50-ms": 200.6, + "collection_p95-ms": 201.2, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.0174, + "completion_sigverify_p95-ms": 0.02466, + "components/block": 128.0, + "cpu-ms/block": 184.9, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 5.54, + "full_to_ready_p95-ms": 10.49, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02208, + "preparation_wait_p95-ms": 0.02276, + "tx/block": 33760.0, + "B/op": 68140628.0, + "allocs/op": 211686.0 + } + }, + { + "sample": 1, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 120393310.0, + "avg_cpu_cores": 1.967, + "collection_p50-ms": 6.412, + "collection_p95-ms": 6.95, + "completion_parse_p50-ms": 4.396, + "completion_parse_p95-ms": 5.383, + "completion_sigverify_p50-ms": 83.47, + "completion_sigverify_p95-ms": 85.24, + "components/block": 691.0, + "cpu-ms/block": 236.8, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 111.9, + "full_to_ready_p95-ms": 123.9, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260018664.0, + "allocs/op": 235461.0 + } + }, + { + "sample": 1, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 121769753.0, + "avg_cpu_cores": 2.062, + "collection_p50-ms": 9.028, + "collection_p95-ms": 9.419, + "completion_parse_p50-ms": 4.405, + "completion_parse_p95-ms": 6.85, + "completion_sigverify_p50-ms": 83.24, + "completion_sigverify_p95-ms": 85.39, + "components/block": 691.0, + "cpu-ms/block": 251.1, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 2038.0, + "full_to_ready_p50-ms": 114.3, + "full_to_ready_p95-ms": 116.6, + "mean_width": 7.93, + "preparation_wait_p50-ms": 0.00517, + "preparation_wait_p95-ms": 0.006164, + "tx/block": 33760.0, + "B/op": 265754008.0, + "allocs/op": 236457.0 + } + }, + { + "sample": 1, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 322283398.0, + "avg_cpu_cores": 0.7984, + "collection_p50-ms": 201.2, + "collection_p95-ms": 202.9, + "completion_parse_p50-ms": 4.62, + "completion_parse_p95-ms": 5.138, + "completion_sigverify_p50-ms": 83.39, + "completion_sigverify_p95-ms": 103.0, + "components/block": 691.0, + "cpu-ms/block": 257.3, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 118.0, + "full_to_ready_p95-ms": 134.5, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260100848.0, + "allocs/op": 235465.0 + } + }, + { + "sample": 1, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 227419935.0, + "avg_cpu_cores": 1.187, + "collection_p50-ms": 201.0, + "collection_p95-ms": 201.2, + "completion_parse_p50-ms": 0.01162, + "completion_parse_p95-ms": 0.01907, + "completion_sigverify_p50-ms": 0.3091, + "completion_sigverify_p95-ms": 0.4848, + "components/block": 691.0, + "cpu-ms/block": 270.0, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33721.0, + "full_to_ready_p50-ms": 25.27, + "full_to_ready_p95-ms": 29.18, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1238, + "preparation_wait_p95-ms": 0.1314, + "tx/block": 33760.0, + "B/op": 307386140.0, + "allocs/op": 250045.0 + } + }, + { + "sample": 1, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 83134892.0, + "avg_cpu_cores": 1.997, + "collection_p50-ms": 1.178, + "collection_p95-ms": 1.282, + "completion_parse_p50-ms": 3.263, + "completion_parse_p95-ms": 4.674, + "completion_sigverify_p50-ms": 75.84, + "completion_sigverify_p95-ms": 77.02, + "components/block": 128.0, + "cpu-ms/block": 166.0, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 81.77, + "full_to_ready_p95-ms": 84.78, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32178240.0, + "allocs/op": 207227.0 + } + }, + { + "sample": 1, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 82695024.0, + "avg_cpu_cores": 2.045, + "collection_p50-ms": 1.573, + "collection_p95-ms": 1.693, + "completion_parse_p50-ms": 3.629, + "completion_parse_p95-ms": 4.856, + "completion_sigverify_p50-ms": 73.77, + "completion_sigverify_p95-ms": 78.48, + "components/block": 128.0, + "cpu-ms/block": 169.1, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 53.8, + "full_to_ready_p50-ms": 80.42, + "full_to_ready_p95-ms": 87.9, + "mean_width": 7.997, + "preparation_wait_p50-ms": 0.000771, + "preparation_wait_p95-ms": 0.03901, + "tx/block": 33760.0, + "B/op": 34804320.0, + "allocs/op": 207381.0 + } + }, + { + "sample": 1, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 282658756.0, + "avg_cpu_cores": 0.5921, + "collection_p50-ms": 200.8, + "collection_p95-ms": 201.0, + "completion_parse_p50-ms": 3.658, + "completion_parse_p95-ms": 4.3, + "completion_sigverify_p50-ms": 75.38, + "completion_sigverify_p95-ms": 75.46, + "components/block": 128.0, + "cpu-ms/block": 167.4, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 82.09, + "full_to_ready_p95-ms": 83.07, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32233064.0, + "allocs/op": 207230.0 + } + }, + { + "sample": 1, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 203125335.0, + "avg_cpu_cores": 0.861, + "collection_p50-ms": 200.8, + "collection_p95-ms": 201.0, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01565, + "completion_sigverify_p95-ms": 0.01665, + "components/block": 128.0, + "cpu-ms/block": 174.9, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 2.212, + "full_to_ready_p95-ms": 2.384, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.01656, + "preparation_wait_p95-ms": 0.01853, + "tx/block": 33760.0, + "B/op": 33093758.0, + "allocs/op": 210058.0 + } + }, + { + "sample": 1, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 110251450.0, + "avg_cpu_cores": 1.891, + "collection_p50-ms": 6.539, + "collection_p95-ms": 6.757, + "completion_parse_p50-ms": 3.75, + "completion_parse_p95-ms": 4.958, + "completion_sigverify_p50-ms": 85.91, + "completion_sigverify_p95-ms": 88.56, + "components/block": 691.0, + "cpu-ms/block": 208.5, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 103.8, + "full_to_ready_p95-ms": 104.7, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 111932865.0, + "allocs/op": 227176.0 + } + }, + { + "sample": 1, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 107413848.0, + "avg_cpu_cores": 2.009, + "collection_p50-ms": 9.056, + "collection_p95-ms": 9.607, + "completion_parse_p50-ms": 3.786, + "completion_parse_p95-ms": 4.344, + "completion_sigverify_p50-ms": 80.71, + "completion_sigverify_p95-ms": 83.94, + "components/block": 691.0, + "cpu-ms/block": 215.8, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 2058.0, + "full_to_ready_p50-ms": 96.75, + "full_to_ready_p95-ms": 104.7, + "mean_width": 7.93, + "preparation_wait_p50-ms": 0.003566, + "preparation_wait_p95-ms": 0.006684, + "tx/block": 33760.0, + "B/op": 114424409.0, + "allocs/op": 228118.0 + } + }, + { + "sample": 1, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 302887790.0, + "avg_cpu_cores": 0.6968, + "collection_p50-ms": 201.3, + "collection_p95-ms": 201.5, + "completion_parse_p50-ms": 3.559, + "completion_parse_p95-ms": 4.55, + "completion_sigverify_p50-ms": 84.29, + "completion_sigverify_p95-ms": 85.42, + "components/block": 691.0, + "cpu-ms/block": 211.1, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 101.9, + "full_to_ready_p95-ms": 106.5, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 112097940.0, + "allocs/op": 227185.0 + } + }, + { + "sample": 1, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 213778904.0, + "avg_cpu_cores": 1.327, + "collection_p50-ms": 201.0, + "collection_p95-ms": 201.4, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.1053, + "completion_sigverify_p95-ms": 0.1169, + "components/block": 691.0, + "cpu-ms/block": 283.7, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 12.72, + "full_to_ready_p95-ms": 12.87, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1121, + "preparation_wait_p95-ms": 0.117, + "tx/block": 33760.0, + "B/op": 114137292.0, + "allocs/op": 241053.0 + } + }, + { + "sample": 2, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 86659017.0, + "avg_cpu_cores": 1.995, + "collection_p50-ms": 1.144, + "collection_p95-ms": 1.292, + "completion_parse_p50-ms": 4.107, + "completion_parse_p95-ms": 4.424, + "completion_sigverify_p50-ms": 77.2, + "completion_sigverify_p95-ms": 78.3, + "components/block": 128.0, + "cpu-ms/block": 172.9, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 85.56, + "full_to_ready_p95-ms": 87.67, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59220556.0, + "allocs/op": 208743.0 + } + }, + { + "sample": 2, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 86399060.0, + "avg_cpu_cores": 2.039, + "collection_p50-ms": 1.618, + "collection_p95-ms": 1.801, + "completion_parse_p50-ms": 4.302, + "completion_parse_p95-ms": 4.956, + "completion_sigverify_p50-ms": 75.09, + "completion_sigverify_p95-ms": 77.26, + "components/block": 128.0, + "cpu-ms/block": 176.2, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 215.2, + "full_to_ready_p50-ms": 84.87, + "full_to_ready_p95-ms": 87.09, + "mean_width": 7.996, + "preparation_wait_p50-ms": 0.4134, + "preparation_wait_p95-ms": 0.8909, + "tx/block": 33760.0, + "B/op": 62291787.0, + "allocs/op": 208932.0 + } + }, + { + "sample": 2, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 295572582.0, + "avg_cpu_cores": 0.6474, + "collection_p50-ms": 201.2, + "collection_p95-ms": 201.3, + "completion_parse_p50-ms": 4.79, + "completion_parse_p95-ms": 5.857, + "completion_sigverify_p50-ms": 84.85, + "completion_sigverify_p95-ms": 94.81, + "components/block": 128.0, + "cpu-ms/block": 191.3, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 96.63, + "full_to_ready_p95-ms": 109.6, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59193052.0, + "allocs/op": 208741.0 + } + }, + { + "sample": 2, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 207101457.0, + "avg_cpu_cores": 0.8795, + "collection_p50-ms": 200.8, + "collection_p95-ms": 201.1, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01769, + "completion_sigverify_p95-ms": 0.02059, + "components/block": 128.0, + "cpu-ms/block": 182.1, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 5.453, + "full_to_ready_p95-ms": 9.838, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02266, + "preparation_wait_p95-ms": 0.02356, + "tx/block": 33760.0, + "B/op": 68303752.0, + "allocs/op": 211688.0 + } + }, + { + "sample": 2, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 120279413.0, + "avg_cpu_cores": 1.963, + "collection_p50-ms": 6.307, + "collection_p95-ms": 6.77, + "completion_parse_p50-ms": 4.339, + "completion_parse_p95-ms": 5.81, + "completion_sigverify_p50-ms": 83.41, + "completion_sigverify_p95-ms": 83.86, + "components/block": 691.0, + "cpu-ms/block": 236.1, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 112.0, + "full_to_ready_p95-ms": 121.7, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260018088.0, + "allocs/op": 235459.0 + } + }, + { + "sample": 2, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 118298008.0, + "avg_cpu_cores": 2.033, + "collection_p50-ms": 8.346, + "collection_p95-ms": 9.341, + "completion_parse_p50-ms": 3.943, + "completion_parse_p95-ms": 4.342, + "completion_sigverify_p50-ms": 79.88, + "completion_sigverify_p95-ms": 82.24, + "components/block": 691.0, + "cpu-ms/block": 240.4, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 1862.0, + "full_to_ready_p50-ms": 109.8, + "full_to_ready_p95-ms": 113.1, + "mean_width": 7.936, + "preparation_wait_p50-ms": 0.003836, + "preparation_wait_p95-ms": 0.004838, + "tx/block": 33760.0, + "B/op": 265236585.0, + "allocs/op": 236375.0 + } + }, + { + "sample": 2, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 320159693.0, + "avg_cpu_cores": 0.7944, + "collection_p50-ms": 201.3, + "collection_p95-ms": 201.8, + "completion_parse_p50-ms": 4.424, + "completion_parse_p95-ms": 4.719, + "completion_sigverify_p50-ms": 88.85, + "completion_sigverify_p95-ms": 95.88, + "components/block": 691.0, + "cpu-ms/block": 254.3, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 117.5, + "full_to_ready_p95-ms": 125.4, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260128558.0, + "allocs/op": 235467.0 + } + }, + { + "sample": 2, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 228077236.0, + "avg_cpu_cores": 1.181, + "collection_p50-ms": 201.0, + "collection_p95-ms": 201.4, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.01689, + "completion_sigverify_p50-ms": 0.1095, + "completion_sigverify_p95-ms": 0.3459, + "components/block": 691.0, + "cpu-ms/block": 269.4, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33750.0, + "full_to_ready_p50-ms": 25.85, + "full_to_ready_p95-ms": 31.67, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1293, + "preparation_wait_p95-ms": 0.1316, + "tx/block": 33760.0, + "B/op": 307341900.0, + "allocs/op": 250055.0 + } + }, + { + "sample": 2, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 86241226.0, + "avg_cpu_cores": 1.993, + "collection_p50-ms": 1.244, + "collection_p95-ms": 1.299, + "completion_parse_p50-ms": 3.632, + "completion_parse_p95-ms": 4.785, + "completion_sigverify_p50-ms": 77.77, + "completion_sigverify_p95-ms": 81.78, + "components/block": 128.0, + "cpu-ms/block": 171.9, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 83.95, + "full_to_ready_p95-ms": 89.64, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32178248.0, + "allocs/op": 207227.0 + } + }, + { + "sample": 2, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 81680215.0, + "avg_cpu_cores": 2.062, + "collection_p50-ms": 1.645, + "collection_p95-ms": 1.663, + "completion_parse_p50-ms": 3.51, + "completion_parse_p95-ms": 4.122, + "completion_sigverify_p50-ms": 73.07, + "completion_sigverify_p95-ms": 75.29, + "components/block": 128.0, + "cpu-ms/block": 168.5, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 53.8, + "full_to_ready_p50-ms": 79.46, + "full_to_ready_p95-ms": 82.06, + "mean_width": 7.996, + "preparation_wait_p50-ms": 0.000902, + "preparation_wait_p95-ms": 0.3958, + "tx/block": 33760.0, + "B/op": 34810161.0, + "allocs/op": 207404.0 + } + }, + { + "sample": 2, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 283720123.0, + "avg_cpu_cores": 0.5926, + "collection_p50-ms": 201.0, + "collection_p95-ms": 201.1, + "completion_parse_p50-ms": 4.15, + "completion_parse_p95-ms": 5.979, + "completion_sigverify_p50-ms": 75.42, + "completion_sigverify_p95-ms": 76.75, + "components/block": 128.0, + "cpu-ms/block": 168.1, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 82.65, + "full_to_ready_p95-ms": 84.17, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32315624.0, + "allocs/op": 207235.0 + } + }, + { + "sample": 2, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 202974173.0, + "avg_cpu_cores": 0.8767, + "collection_p50-ms": 200.8, + "collection_p95-ms": 200.9, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.0161, + "completion_sigverify_p95-ms": 0.02322, + "components/block": 128.0, + "cpu-ms/block": 177.9, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 2.265, + "full_to_ready_p95-ms": 2.34, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.01836, + "preparation_wait_p95-ms": 0.01868, + "tx/block": 33760.0, + "B/op": 33013977.0, + "allocs/op": 210063.0 + } + }, + { + "sample": 2, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 108052777.0, + "avg_cpu_cores": 1.902, + "collection_p50-ms": 6.441, + "collection_p95-ms": 6.951, + "completion_parse_p50-ms": 3.54, + "completion_parse_p95-ms": 4.962, + "completion_sigverify_p50-ms": 84.58, + "completion_sigverify_p95-ms": 85.8, + "components/block": 691.0, + "cpu-ms/block": 205.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 102.2, + "full_to_ready_p95-ms": 106.2, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 111905424.0, + "allocs/op": 227175.0 + } + }, + { + "sample": 2, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 113945740.0, + "avg_cpu_cores": 1.995, + "collection_p50-ms": 9.282, + "collection_p95-ms": 11.0, + "completion_parse_p50-ms": 3.909, + "completion_parse_p95-ms": 4.912, + "completion_sigverify_p50-ms": 84.29, + "completion_sigverify_p95-ms": 94.37, + "components/block": 691.0, + "cpu-ms/block": 227.4, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 2048.0, + "full_to_ready_p50-ms": 104.1, + "full_to_ready_p95-ms": 111.8, + "mean_width": 7.929, + "preparation_wait_p50-ms": 0.003519, + "preparation_wait_p95-ms": 0.006271, + "tx/block": 33760.0, + "B/op": 114311740.0, + "allocs/op": 228106.0 + } + }, + { + "sample": 2, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 304716913.0, + "avg_cpu_cores": 0.7074, + "collection_p50-ms": 201.6, + "collection_p95-ms": 201.7, + "completion_parse_p50-ms": 4.281, + "completion_parse_p95-ms": 4.819, + "completion_sigverify_p50-ms": 84.11, + "completion_sigverify_p95-ms": 89.27, + "components/block": 691.0, + "cpu-ms/block": 215.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 102.5, + "full_to_ready_p95-ms": 109.4, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 112180404.0, + "allocs/op": 227190.0 + } + }, + { + "sample": 2, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 212222544.0, + "avg_cpu_cores": 1.082, + "collection_p50-ms": 200.9, + "collection_p95-ms": 201.5, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.01717, + "completion_sigverify_p50-ms": 0.09849, + "completion_sigverify_p95-ms": 0.418, + "components/block": 691.0, + "cpu-ms/block": 229.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33741.0, + "full_to_ready_p50-ms": 11.01, + "full_to_ready_p95-ms": 11.46, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1016, + "preparation_wait_p95-ms": 0.1048, + "tx/block": 33760.0, + "B/op": 114254763.0, + "allocs/op": 241078.0 + } + }, + { + "sample": 3, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 85727666.0, + "avg_cpu_cores": 1.994, + "collection_p50-ms": 1.123, + "collection_p95-ms": 1.319, + "completion_parse_p50-ms": 4.203, + "completion_parse_p95-ms": 4.528, + "completion_sigverify_p50-ms": 75.36, + "completion_sigverify_p95-ms": 76.36, + "components/block": 128.0, + "cpu-ms/block": 171.0, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 85.03, + "full_to_ready_p95-ms": 86.29, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59248208.0, + "allocs/op": 208745.0 + } + }, + { + "sample": 3, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 93032467.0, + "avg_cpu_cores": 2.047, + "collection_p50-ms": 1.604, + "collection_p95-ms": 1.819, + "completion_parse_p50-ms": 4.104, + "completion_parse_p95-ms": 5.156, + "completion_sigverify_p50-ms": 80.57, + "completion_sigverify_p95-ms": 87.12, + "components/block": 128.0, + "cpu-ms/block": 190.4, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 107.6, + "full_to_ready_p50-ms": 91.48, + "full_to_ready_p95-ms": 95.41, + "mean_width": 7.997, + "preparation_wait_p50-ms": 0.000692, + "preparation_wait_p95-ms": 0.04334, + "tx/block": 33760.0, + "B/op": 61931662.0, + "allocs/op": 208897.0 + } + }, + { + "sample": 3, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 285476051.0, + "avg_cpu_cores": 0.6068, + "collection_p50-ms": 200.9, + "collection_p95-ms": 201.0, + "completion_parse_p50-ms": 3.352, + "completion_parse_p95-ms": 4.118, + "completion_sigverify_p50-ms": 76.14, + "completion_sigverify_p95-ms": 77.78, + "components/block": 128.0, + "cpu-ms/block": 173.2, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 84.82, + "full_to_ready_p95-ms": 85.15, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59303190.0, + "allocs/op": 208749.0 + } + }, + { + "sample": 3, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 207130969.0, + "avg_cpu_cores": 0.8933, + "collection_p50-ms": 200.6, + "collection_p95-ms": 200.7, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01884, + "completion_sigverify_p95-ms": 0.02053, + "components/block": 128.0, + "cpu-ms/block": 185.0, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 5.428, + "full_to_ready_p95-ms": 10.36, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02344, + "preparation_wait_p95-ms": 0.02789, + "tx/block": 33760.0, + "B/op": 68193945.0, + "allocs/op": 211684.0 + } + }, + { + "sample": 3, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 121503561.0, + "avg_cpu_cores": 1.942, + "collection_p50-ms": 6.518, + "collection_p95-ms": 6.678, + "completion_parse_p50-ms": 4.399, + "completion_parse_p95-ms": 4.914, + "completion_sigverify_p50-ms": 83.32, + "completion_sigverify_p95-ms": 85.03, + "components/block": 691.0, + "cpu-ms/block": 236.0, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 111.7, + "full_to_ready_p95-ms": 122.4, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260018412.0, + "allocs/op": 235460.0 + } + }, + { + "sample": 3, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 117837845.0, + "avg_cpu_cores": 2.08, + "collection_p50-ms": 8.353, + "collection_p95-ms": 10.64, + "completion_parse_p50-ms": 4.156, + "completion_parse_p95-ms": 5.325, + "completion_sigverify_p50-ms": 78.84, + "completion_sigverify_p95-ms": 83.36, + "components/block": 691.0, + "cpu-ms/block": 245.1, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 1891.0, + "full_to_ready_p50-ms": 108.9, + "full_to_ready_p95-ms": 111.3, + "mean_width": 7.936, + "preparation_wait_p50-ms": 0.00511, + "preparation_wait_p95-ms": 0.006629, + "tx/block": 33760.0, + "B/op": 265386019.0, + "allocs/op": 236393.0 + } + }, + { + "sample": 3, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 318075945.0, + "avg_cpu_cores": 0.7698, + "collection_p50-ms": 201.2, + "collection_p95-ms": 201.7, + "completion_parse_p50-ms": 4.535, + "completion_parse_p95-ms": 6.282, + "completion_sigverify_p50-ms": 86.26, + "completion_sigverify_p95-ms": 88.65, + "components/block": 691.0, + "cpu-ms/block": 244.8, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 116.5, + "full_to_ready_p95-ms": 120.7, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260100819.0, + "allocs/op": 235465.0 + } + }, + { + "sample": 3, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 227891859.0, + "avg_cpu_cores": 1.192, + "collection_p50-ms": 200.9, + "collection_p95-ms": 201.7, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.1033, + "completion_sigverify_p95-ms": 0.1248, + "components/block": 691.0, + "cpu-ms/block": 271.8, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 25.57, + "full_to_ready_p95-ms": 31.02, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1267, + "preparation_wait_p95-ms": 0.1306, + "tx/block": 33760.0, + "B/op": 307494656.0, + "allocs/op": 250070.0 + } + }, + { + "sample": 3, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 85101485.0, + "avg_cpu_cores": 1.996, + "collection_p50-ms": 1.142, + "collection_p95-ms": 1.251, + "completion_parse_p50-ms": 3.633, + "completion_parse_p95-ms": 4.608, + "completion_sigverify_p50-ms": 77.59, + "completion_sigverify_p95-ms": 79.04, + "components/block": 128.0, + "cpu-ms/block": 169.8, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 84.13, + "full_to_ready_p95-ms": 85.35, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32233233.0, + "allocs/op": 207230.0 + } + }, + { + "sample": 3, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 83717807.0, + "avg_cpu_cores": 2.049, + "collection_p50-ms": 1.555, + "collection_p95-ms": 1.796, + "completion_parse_p50-ms": 3.655, + "completion_parse_p95-ms": 4.307, + "completion_sigverify_p50-ms": 75.73, + "completion_sigverify_p95-ms": 77.56, + "components/block": 128.0, + "cpu-ms/block": 171.5, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 107.6, + "full_to_ready_p50-ms": 81.9, + "full_to_ready_p95-ms": 84.2, + "mean_width": 7.996, + "preparation_wait_p50-ms": 0.000951, + "preparation_wait_p95-ms": 0.9772, + "tx/block": 33760.0, + "B/op": 34781204.0, + "allocs/op": 207396.0 + } + }, + { + "sample": 3, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 283918809.0, + "avg_cpu_cores": 0.5965, + "collection_p50-ms": 200.7, + "collection_p95-ms": 201.0, + "completion_parse_p50-ms": 3.509, + "completion_parse_p95-ms": 4.352, + "completion_sigverify_p50-ms": 76.49, + "completion_sigverify_p95-ms": 79.83, + "components/block": 128.0, + "cpu-ms/block": 169.4, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 83.91, + "full_to_ready_p95-ms": 86.88, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32260635.0, + "allocs/op": 207231.0 + } + }, + { + "sample": 3, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 203102557.0, + "avg_cpu_cores": 0.8531, + "collection_p50-ms": 200.7, + "collection_p95-ms": 201.0, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01636, + "completion_sigverify_p95-ms": 0.01724, + "components/block": 128.0, + "cpu-ms/block": 173.3, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 2.283, + "full_to_ready_p95-ms": 2.706, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.01703, + "preparation_wait_p95-ms": 0.01848, + "tx/block": 33760.0, + "B/op": 33091550.0, + "allocs/op": 210049.0 + } + }, + { + "sample": 3, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 108851790.0, + "avg_cpu_cores": 1.903, + "collection_p50-ms": 5.88, + "collection_p95-ms": 6.6, + "completion_parse_p50-ms": 3.485, + "completion_parse_p95-ms": 4.888, + "completion_sigverify_p50-ms": 85.29, + "completion_sigverify_p95-ms": 87.68, + "components/block": 691.0, + "cpu-ms/block": 207.2, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 102.1, + "full_to_ready_p95-ms": 105.1, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 112042787.0, + "allocs/op": 227182.0 + } + }, + { + "sample": 3, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 105118924.0, + "avg_cpu_cores": 1.992, + "collection_p50-ms": 8.424, + "collection_p95-ms": 9.203, + "completion_parse_p50-ms": 3.571, + "completion_parse_p95-ms": 4.459, + "completion_sigverify_p50-ms": 79.01, + "completion_sigverify_p95-ms": 82.87, + "components/block": 691.0, + "cpu-ms/block": 209.4, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 2009.0, + "full_to_ready_p50-ms": 95.95, + "full_to_ready_p95-ms": 102.7, + "mean_width": 7.932, + "preparation_wait_p50-ms": 0.002874, + "preparation_wait_p95-ms": 0.004791, + "tx/block": 33760.0, + "B/op": 114420668.0, + "allocs/op": 228093.0 + } + }, + { + "sample": 3, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 311473242.0, + "avg_cpu_cores": 0.7254, + "collection_p50-ms": 201.3, + "collection_p95-ms": 201.6, + "completion_parse_p50-ms": 4.354, + "completion_parse_p95-ms": 4.596, + "completion_sigverify_p50-ms": 86.07, + "completion_sigverify_p95-ms": 101.8, + "components/block": 691.0, + "cpu-ms/block": 226.0, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 106.9, + "full_to_ready_p95-ms": 121.5, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 112015353.0, + "allocs/op": 227181.0 + } + }, + { + "sample": 3, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 213548260.0, + "avg_cpu_cores": 1.136, + "collection_p50-ms": 201.1, + "collection_p95-ms": 203.1, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.01487, + "completion_sigverify_p50-ms": 0.1838, + "completion_sigverify_p95-ms": 0.5611, + "components/block": 691.0, + "cpu-ms/block": 242.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33721.0, + "full_to_ready_p50-ms": 11.67, + "full_to_ready_p95-ms": 13.52, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1105, + "preparation_wait_p95-ms": 0.1124, + "tx/block": 33760.0, + "B/op": 114250187.0, + "allocs/op": 241068.0 + } + }, + { + "sample": 4, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 85908997.0, + "avg_cpu_cores": 1.99, + "collection_p50-ms": 1.114, + "collection_p95-ms": 1.748, + "completion_parse_p50-ms": 4.036, + "completion_parse_p95-ms": 4.731, + "completion_sigverify_p50-ms": 75.53, + "completion_sigverify_p95-ms": 76.38, + "components/block": 128.0, + "cpu-ms/block": 171.0, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 85.02, + "full_to_ready_p95-ms": 87.14, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59303256.0, + "allocs/op": 208748.0 + } + }, + { + "sample": 4, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 83287865.0, + "avg_cpu_cores": 2.055, + "collection_p50-ms": 1.528, + "collection_p95-ms": 1.624, + "completion_parse_p50-ms": 3.147, + "completion_parse_p95-ms": 4.146, + "completion_sigverify_p50-ms": 73.56, + "completion_sigverify_p95-ms": 75.42, + "components/block": 128.0, + "cpu-ms/block": 171.2, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 53.8, + "full_to_ready_p50-ms": 81.77, + "full_to_ready_p95-ms": 82.43, + "mean_width": 7.997, + "preparation_wait_p50-ms": 0.00075, + "preparation_wait_p95-ms": 0.000791, + "tx/block": 33760.0, + "B/op": 62015553.0, + "allocs/op": 208909.0 + } + }, + { + "sample": 4, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 286588469.0, + "avg_cpu_cores": 0.6114, + "collection_p50-ms": 200.8, + "collection_p95-ms": 201.1, + "completion_parse_p50-ms": 3.437, + "completion_parse_p95-ms": 3.868, + "completion_sigverify_p50-ms": 76.77, + "completion_sigverify_p95-ms": 79.74, + "components/block": 128.0, + "cpu-ms/block": 175.2, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 85.44, + "full_to_ready_p95-ms": 88.49, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59275379.0, + "allocs/op": 208745.0 + } + }, + { + "sample": 4, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 208123357.0, + "avg_cpu_cores": 0.9981, + "collection_p50-ms": 200.8, + "collection_p95-ms": 201.3, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.02067, + "completion_sigverify_p95-ms": 0.02899, + "components/block": 128.0, + "cpu-ms/block": 207.7, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 7.752, + "full_to_ready_p95-ms": 10.18, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02546, + "preparation_wait_p95-ms": 0.02901, + "tx/block": 33760.0, + "B/op": 68363801.0, + "allocs/op": 211716.0 + } + }, + { + "sample": 4, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 134090384.0, + "avg_cpu_cores": 1.916, + "collection_p50-ms": 7.863, + "collection_p95-ms": 9.017, + "completion_parse_p50-ms": 5.347, + "completion_parse_p95-ms": 5.887, + "completion_sigverify_p50-ms": 90.59, + "completion_sigverify_p95-ms": 103.3, + "components/block": 691.0, + "cpu-ms/block": 256.9, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 124.8, + "full_to_ready_p95-ms": 146.3, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260045872.0, + "allocs/op": 235462.0 + } + }, + { + "sample": 4, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 118644204.0, + "avg_cpu_cores": 2.083, + "collection_p50-ms": 8.456, + "collection_p95-ms": 8.783, + "completion_parse_p50-ms": 3.887, + "completion_parse_p95-ms": 4.874, + "completion_sigverify_p50-ms": 80.59, + "completion_sigverify_p95-ms": 82.3, + "components/block": 691.0, + "cpu-ms/block": 247.2, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 1931.0, + "full_to_ready_p50-ms": 109.2, + "full_to_ready_p95-ms": 112.4, + "mean_width": 7.935, + "preparation_wait_p50-ms": 0.004411, + "preparation_wait_p95-ms": 0.004846, + "tx/block": 33760.0, + "B/op": 265440401.0, + "allocs/op": 236406.0 + } + }, + { + "sample": 4, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 316166377.0, + "avg_cpu_cores": 0.7691, + "collection_p50-ms": 201.3, + "collection_p95-ms": 201.6, + "completion_parse_p50-ms": 4.191, + "completion_parse_p95-ms": 4.5, + "completion_sigverify_p50-ms": 84.55, + "completion_sigverify_p95-ms": 87.88, + "components/block": 691.0, + "cpu-ms/block": 243.2, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 115.2, + "full_to_ready_p95-ms": 119.9, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 259963968.0, + "allocs/op": 235459.0 + } + }, + { + "sample": 4, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 230004734.0, + "avg_cpu_cores": 1.196, + "collection_p50-ms": 201.3, + "collection_p95-ms": 201.8, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.1175, + "completion_sigverify_p95-ms": 0.1314, + "components/block": 691.0, + "cpu-ms/block": 275.1, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 28.3, + "full_to_ready_p95-ms": 32.14, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1213, + "preparation_wait_p95-ms": 0.1297, + "tx/block": 33760.0, + "B/op": 307660395.0, + "allocs/op": 250071.0 + } + }, + { + "sample": 4, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 83997761.0, + "avg_cpu_cores": 1.99, + "collection_p50-ms": 1.213, + "collection_p95-ms": 1.275, + "completion_parse_p50-ms": 3.383, + "completion_parse_p95-ms": 4.718, + "completion_sigverify_p50-ms": 76.12, + "completion_sigverify_p95-ms": 77.08, + "components/block": 128.0, + "cpu-ms/block": 167.2, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 82.75, + "full_to_ready_p95-ms": 84.96, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32260841.0, + "allocs/op": 207232.0 + } + }, + { + "sample": 4, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 82378390.0, + "avg_cpu_cores": 2.052, + "collection_p50-ms": 1.582, + "collection_p95-ms": 1.764, + "completion_parse_p50-ms": 3.427, + "completion_parse_p95-ms": 4.179, + "completion_sigverify_p50-ms": 73.7, + "completion_sigverify_p95-ms": 77.03, + "components/block": 128.0, + "cpu-ms/block": 169.1, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 107.6, + "full_to_ready_p50-ms": 80.55, + "full_to_ready_p95-ms": 83.25, + "mean_width": 7.996, + "preparation_wait_p50-ms": 0.000711, + "preparation_wait_p95-ms": 0.5494, + "tx/block": 33760.0, + "B/op": 34778107.0, + "allocs/op": 207384.0 + } + }, + { + "sample": 4, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 285362255.0, + "avg_cpu_cores": 0.603, + "collection_p50-ms": 201.1, + "collection_p95-ms": 201.1, + "completion_parse_p50-ms": 4.124, + "completion_parse_p95-ms": 4.297, + "completion_sigverify_p50-ms": 75.54, + "completion_sigverify_p95-ms": 83.57, + "components/block": 128.0, + "cpu-ms/block": 172.1, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 83.22, + "full_to_ready_p95-ms": 91.69, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32287976.0, + "allocs/op": 207232.0 + } + }, + { + "sample": 4, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 203641348.0, + "avg_cpu_cores": 1.007, + "collection_p50-ms": 200.9, + "collection_p95-ms": 201.2, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01979, + "completion_sigverify_p95-ms": 0.02696, + "components/block": 128.0, + "cpu-ms/block": 205.1, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 2.728, + "full_to_ready_p95-ms": 2.801, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02069, + "preparation_wait_p95-ms": 0.02411, + "tx/block": 33760.0, + "B/op": 33091185.0, + "allocs/op": 210047.0 + } + }, + { + "sample": 4, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 109632016.0, + "avg_cpu_cores": 1.892, + "collection_p50-ms": 6.787, + "collection_p95-ms": 7.832, + "completion_parse_p50-ms": 3.642, + "completion_parse_p95-ms": 4.96, + "completion_sigverify_p50-ms": 85.31, + "completion_sigverify_p95-ms": 88.3, + "components/block": 691.0, + "cpu-ms/block": 207.4, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 102.1, + "full_to_ready_p95-ms": 108.2, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 111905504.0, + "allocs/op": 227176.0 + } + }, + { + "sample": 4, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 106324511.0, + "avg_cpu_cores": 2.007, + "collection_p50-ms": 8.568, + "collection_p95-ms": 9.414, + "completion_parse_p50-ms": 3.655, + "completion_parse_p95-ms": 4.119, + "completion_sigverify_p50-ms": 79.89, + "completion_sigverify_p95-ms": 84.03, + "components/block": 691.0, + "cpu-ms/block": 213.4, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 1862.0, + "full_to_ready_p50-ms": 96.25, + "full_to_ready_p95-ms": 103.4, + "mean_width": 7.936, + "preparation_wait_p50-ms": 0.003455, + "preparation_wait_p95-ms": 0.006185, + "tx/block": 33760.0, + "B/op": 114547820.0, + "allocs/op": 228037.0 + } + }, + { + "sample": 4, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 311267426.0, + "avg_cpu_cores": 0.7263, + "collection_p50-ms": 201.1, + "collection_p95-ms": 202.1, + "completion_parse_p50-ms": 3.621, + "completion_parse_p95-ms": 5.061, + "completion_sigverify_p50-ms": 87.9, + "completion_sigverify_p95-ms": 103.1, + "components/block": 691.0, + "cpu-ms/block": 226.1, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 105.2, + "full_to_ready_p95-ms": 119.8, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 111932950.0, + "allocs/op": 227177.0 + } + }, + { + "sample": 4, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 212794779.0, + "avg_cpu_cores": 1.109, + "collection_p50-ms": 201.0, + "collection_p95-ms": 202.1, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.01383, + "completion_sigverify_p50-ms": 0.1078, + "completion_sigverify_p95-ms": 0.3044, + "components/block": 691.0, + "cpu-ms/block": 236.0, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33750.0, + "full_to_ready_p50-ms": 11.18, + "full_to_ready_p95-ms": 12.44, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1053, + "preparation_wait_p95-ms": 0.1134, + "tx/block": 33760.0, + "B/op": 114247308.0, + "allocs/op": 241055.0 + } + }, + { + "sample": 5, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 86664771.0, + "avg_cpu_cores": 1.983, + "collection_p50-ms": 1.195, + "collection_p95-ms": 1.43, + "completion_parse_p50-ms": 4.332, + "completion_parse_p95-ms": 5.166, + "completion_sigverify_p50-ms": 76.4, + "completion_sigverify_p95-ms": 77.61, + "components/block": 128.0, + "cpu-ms/block": 171.9, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 86.21, + "full_to_ready_p95-ms": 87.53, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59275827.0, + "allocs/op": 208747.0 + } + }, + { + "sample": 5, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 100446776.0, + "avg_cpu_cores": 2.026, + "collection_p50-ms": 2.064, + "collection_p95-ms": 2.165, + "completion_parse_p50-ms": 4.494, + "completion_parse_p95-ms": 5.885, + "completion_sigverify_p50-ms": 89.05, + "completion_sigverify_p95-ms": 89.98, + "components/block": 128.0, + "cpu-ms/block": 203.5, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 215.2, + "full_to_ready_p50-ms": 100.3, + "full_to_ready_p95-ms": 104.8, + "mean_width": 7.996, + "preparation_wait_p50-ms": 0.000821, + "preparation_wait_p95-ms": 0.001052, + "tx/block": 33760.0, + "B/op": 61973884.0, + "allocs/op": 208905.0 + } + }, + { + "sample": 5, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 286167802.0, + "avg_cpu_cores": 0.6098, + "collection_p50-ms": 200.7, + "collection_p95-ms": 200.8, + "completion_parse_p50-ms": 3.934, + "completion_parse_p95-ms": 4.419, + "completion_sigverify_p50-ms": 76.59, + "completion_sigverify_p95-ms": 77.91, + "components/block": 128.0, + "cpu-ms/block": 174.5, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 85.4, + "full_to_ready_p95-ms": 86.57, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59193008.0, + "allocs/op": 208742.0 + } + }, + { + "sample": 5, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 207732756.0, + "avg_cpu_cores": 0.9023, + "collection_p50-ms": 200.9, + "collection_p95-ms": 201.1, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01991, + "completion_sigverify_p95-ms": 0.03155, + "components/block": 128.0, + "cpu-ms/block": 187.4, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 7.562, + "full_to_ready_p95-ms": 9.053, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02246, + "preparation_wait_p95-ms": 0.02459, + "tx/block": 33760.0, + "B/op": 68138699.0, + "allocs/op": 211680.0 + } + }, + { + "sample": 5, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 123685493.0, + "avg_cpu_cores": 1.96, + "collection_p50-ms": 6.351, + "collection_p95-ms": 7.32, + "completion_parse_p50-ms": 4.749, + "completion_parse_p95-ms": 4.856, + "completion_sigverify_p50-ms": 84.69, + "completion_sigverify_p95-ms": 86.93, + "components/block": 691.0, + "cpu-ms/block": 242.4, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 118.8, + "full_to_ready_p95-ms": 123.1, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260100822.0, + "allocs/op": 235464.0 + } + }, + { + "sample": 5, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 119201510.0, + "avg_cpu_cores": 2.078, + "collection_p50-ms": 8.512, + "collection_p95-ms": 9.358, + "completion_parse_p50-ms": 4.359, + "completion_parse_p95-ms": 4.711, + "completion_sigverify_p50-ms": 80.38, + "completion_sigverify_p95-ms": 82.91, + "components/block": 691.0, + "cpu-ms/block": 247.7, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 1950.0, + "full_to_ready_p50-ms": 110.3, + "full_to_ready_p95-ms": 113.1, + "mean_width": 7.933, + "preparation_wait_p50-ms": 0.004432, + "preparation_wait_p95-ms": 0.006141, + "tx/block": 33760.0, + "B/op": 265605086.0, + "allocs/op": 236424.0 + } + }, + { + "sample": 5, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 326825723.0, + "avg_cpu_cores": 0.8088, + "collection_p50-ms": 201.9, + "collection_p95-ms": 202.2, + "completion_parse_p50-ms": 4.877, + "completion_parse_p95-ms": 5.064, + "completion_sigverify_p50-ms": 94.15, + "completion_sigverify_p95-ms": 106.9, + "components/block": 691.0, + "cpu-ms/block": 264.3, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 127.9, + "full_to_ready_p95-ms": 139.6, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260100937.0, + "allocs/op": 235466.0 + } + }, + { + "sample": 5, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 229345156.0, + "avg_cpu_cores": 1.197, + "collection_p50-ms": 201.0, + "collection_p95-ms": 201.0, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.1071, + "completion_sigverify_p95-ms": 0.1224, + "components/block": 691.0, + "cpu-ms/block": 274.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 28.39, + "full_to_ready_p95-ms": 31.1, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1263, + "preparation_wait_p95-ms": 0.1313, + "tx/block": 33760.0, + "B/op": 307580156.0, + "allocs/op": 250079.0 + } + }, + { + "sample": 5, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 86939212.0, + "avg_cpu_cores": 2.001, + "collection_p50-ms": 1.138, + "collection_p95-ms": 1.528, + "completion_parse_p50-ms": 3.315, + "completion_parse_p95-ms": 4.947, + "completion_sigverify_p50-ms": 80.42, + "completion_sigverify_p95-ms": 81.95, + "components/block": 128.0, + "cpu-ms/block": 173.9, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 86.28, + "full_to_ready_p95-ms": 89.75, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32343236.0, + "allocs/op": 207237.0 + } + }, + { + "sample": 5, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 93051198.0, + "avg_cpu_cores": 2.052, + "collection_p50-ms": 1.908, + "collection_p95-ms": 2.115, + "completion_parse_p50-ms": 3.802, + "completion_parse_p95-ms": 5.595, + "completion_sigverify_p50-ms": 83.19, + "completion_sigverify_p95-ms": 89.15, + "components/block": 128.0, + "cpu-ms/block": 191.0, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 161.4, + "full_to_ready_p50-ms": 89.81, + "full_to_ready_p95-ms": 98.24, + "mean_width": 7.995, + "preparation_wait_p50-ms": 0.1421, + "preparation_wait_p95-ms": 1.063, + "tx/block": 33760.0, + "B/op": 34757734.0, + "allocs/op": 207410.0 + } + }, + { + "sample": 5, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 285099098.0, + "avg_cpu_cores": 0.6058, + "collection_p50-ms": 200.8, + "collection_p95-ms": 201.0, + "completion_parse_p50-ms": 3.323, + "completion_parse_p95-ms": 4.328, + "completion_sigverify_p50-ms": 77.77, + "completion_sigverify_p95-ms": 77.9, + "components/block": 128.0, + "cpu-ms/block": 172.7, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 84.74, + "full_to_ready_p95-ms": 85.63, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32205774.0, + "allocs/op": 207229.0 + } + }, + { + "sample": 5, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 203024092.0, + "avg_cpu_cores": 0.8731, + "collection_p50-ms": 200.6, + "collection_p95-ms": 200.9, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01703, + "completion_sigverify_p95-ms": 0.02449, + "components/block": 128.0, + "cpu-ms/block": 177.3, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 2.292, + "full_to_ready_p95-ms": 2.39, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.01816, + "preparation_wait_p95-ms": 0.01985, + "tx/block": 33760.0, + "B/op": 33175731.0, + "allocs/op": 210061.0 + } + }, + { + "sample": 5, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 116086892.0, + "avg_cpu_cores": 1.896, + "collection_p50-ms": 7.729, + "collection_p95-ms": 9.382, + "completion_parse_p50-ms": 3.985, + "completion_parse_p95-ms": 6.018, + "completion_sigverify_p50-ms": 84.53, + "completion_sigverify_p95-ms": 100.6, + "components/block": 691.0, + "cpu-ms/block": 220.2, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 109.4, + "full_to_ready_p95-ms": 119.2, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 111960304.0, + "allocs/op": 227177.0 + } + }, + { + "sample": 5, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 115194449.0, + "avg_cpu_cores": 1.969, + "collection_p50-ms": 9.432, + "collection_p95-ms": 10.35, + "completion_parse_p50-ms": 3.882, + "completion_parse_p95-ms": 4.979, + "completion_sigverify_p50-ms": 85.65, + "completion_sigverify_p95-ms": 94.03, + "components/block": 691.0, + "cpu-ms/block": 226.9, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 1735.0, + "full_to_ready_p50-ms": 104.0, + "full_to_ready_p95-ms": 111.4, + "mean_width": 7.941, + "preparation_wait_p50-ms": 0.004026, + "preparation_wait_p95-ms": 0.005541, + "tx/block": 33760.0, + "B/op": 114430456.0, + "allocs/op": 227975.0 + } + }, + { + "sample": 5, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 308111487.0, + "avg_cpu_cores": 0.7254, + "collection_p50-ms": 201.5, + "collection_p95-ms": 201.6, + "completion_parse_p50-ms": 4.284, + "completion_parse_p95-ms": 5.167, + "completion_sigverify_p50-ms": 88.66, + "completion_sigverify_p95-ms": 94.67, + "components/block": 691.0, + "cpu-ms/block": 223.5, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 109.1, + "full_to_ready_p95-ms": 113.6, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 112125502.0, + "allocs/op": 227188.0 + } + }, + { + "sample": 5, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 212791787.0, + "avg_cpu_cores": 1.116, + "collection_p50-ms": 200.9, + "collection_p95-ms": 201.0, + "completion_parse_p50-ms": 0.007484, + "completion_parse_p95-ms": 0.01159, + "completion_sigverify_p50-ms": 0.3225, + "completion_sigverify_p95-ms": 0.3603, + "components/block": 691.0, + "cpu-ms/block": 237.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33731.0, + "full_to_ready_p50-ms": 11.63, + "full_to_ready_p95-ms": 13.06, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1056, + "preparation_wait_p95-ms": 0.1122, + "tx/block": 33760.0, + "B/op": 114082224.0, + "allocs/op": 241044.0 + } + }, + { + "sample": 6, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 87321492.0, + "avg_cpu_cores": 1.992, + "collection_p50-ms": 1.139, + "collection_p95-ms": 1.213, + "completion_parse_p50-ms": 4.206, + "completion_parse_p95-ms": 4.314, + "completion_sigverify_p50-ms": 76.92, + "completion_sigverify_p95-ms": 79.11, + "components/block": 128.0, + "cpu-ms/block": 173.9, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 86.31, + "full_to_ready_p95-ms": 87.54, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59110704.0, + "allocs/op": 208737.0 + } + }, + { + "sample": 6, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 88098284.0, + "avg_cpu_cores": 2.039, + "collection_p50-ms": 1.612, + "collection_p95-ms": 1.726, + "completion_parse_p50-ms": 3.683, + "completion_parse_p95-ms": 4.031, + "completion_sigverify_p50-ms": 78.16, + "completion_sigverify_p95-ms": 83.11, + "components/block": 128.0, + "cpu-ms/block": 179.7, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 107.6, + "full_to_ready_p50-ms": 87.14, + "full_to_ready_p95-ms": 92.06, + "mean_width": 7.997, + "preparation_wait_p50-ms": 0.000771, + "preparation_wait_p95-ms": 0.000842, + "tx/block": 33760.0, + "B/op": 61902907.0, + "allocs/op": 208887.0 + } + }, + { + "sample": 6, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 286038446.0, + "avg_cpu_cores": 0.6079, + "collection_p50-ms": 200.9, + "collection_p95-ms": 201.1, + "completion_parse_p50-ms": 3.731, + "completion_parse_p95-ms": 4.179, + "completion_sigverify_p50-ms": 76.35, + "completion_sigverify_p95-ms": 78.24, + "components/block": 128.0, + "cpu-ms/block": 173.9, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 85.53, + "full_to_ready_p95-ms": 85.9, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59248048.0, + "allocs/op": 208744.0 + } + }, + { + "sample": 6, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 206915717.0, + "avg_cpu_cores": 0.8863, + "collection_p50-ms": 200.6, + "collection_p95-ms": 200.9, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01665, + "completion_sigverify_p95-ms": 0.02066, + "components/block": 128.0, + "cpu-ms/block": 183.4, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 5.859, + "full_to_ready_p95-ms": 9.065, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02179, + "preparation_wait_p95-ms": 0.02377, + "tx/block": 33760.0, + "B/op": 68223072.0, + "allocs/op": 211693.0 + } + }, + { + "sample": 6, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 125985246.0, + "avg_cpu_cores": 1.958, + "collection_p50-ms": 6.541, + "collection_p95-ms": 7.319, + "completion_parse_p50-ms": 5.022, + "completion_parse_p95-ms": 5.127, + "completion_sigverify_p50-ms": 86.2, + "completion_sigverify_p95-ms": 88.08, + "components/block": 691.0, + "cpu-ms/block": 246.7, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 118.0, + "full_to_ready_p95-ms": 130.3, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 259990851.0, + "allocs/op": 235458.0 + } + }, + { + "sample": 6, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 118097846.0, + "avg_cpu_cores": 2.073, + "collection_p50-ms": 8.234, + "collection_p95-ms": 9.436, + "completion_parse_p50-ms": 3.897, + "completion_parse_p95-ms": 4.584, + "completion_sigverify_p50-ms": 80.46, + "completion_sigverify_p95-ms": 84.12, + "components/block": 691.0, + "cpu-ms/block": 244.8, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 2009.0, + "full_to_ready_p50-ms": 110.1, + "full_to_ready_p95-ms": 111.6, + "mean_width": 7.932, + "preparation_wait_p50-ms": 0.003857, + "preparation_wait_p95-ms": 0.005016, + "tx/block": 33760.0, + "B/op": 265493705.0, + "allocs/op": 236438.0 + } + }, + { + "sample": 6, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 318006733.0, + "avg_cpu_cores": 0.7756, + "collection_p50-ms": 201.6, + "collection_p95-ms": 201.9, + "completion_parse_p50-ms": 5.76, + "completion_parse_p95-ms": 6.369, + "completion_sigverify_p50-ms": 84.75, + "completion_sigverify_p95-ms": 86.11, + "components/block": 691.0, + "cpu-ms/block": 246.7, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 117.0, + "full_to_ready_p95-ms": 120.6, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260100320.0, + "allocs/op": 235463.0 + } + }, + { + "sample": 6, + "variant": "baseline", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 228423719.0, + "avg_cpu_cores": 1.21, + "collection_p50-ms": 201.1, + "collection_p95-ms": 201.6, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.02031, + "completion_sigverify_p50-ms": 0.1011, + "completion_sigverify_p95-ms": 0.5984, + "components/block": 691.0, + "cpu-ms/block": 276.4, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33731.0, + "full_to_ready_p50-ms": 25.52, + "full_to_ready_p95-ms": 30.97, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.129, + "preparation_wait_p95-ms": 0.1313, + "tx/block": 33760.0, + "B/op": 307480184.0, + "allocs/op": 250043.0 + } + }, + { + "sample": 6, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 84167116.0, + "avg_cpu_cores": 1.988, + "collection_p50-ms": 1.144, + "collection_p95-ms": 1.232, + "completion_parse_p50-ms": 3.413, + "completion_parse_p95-ms": 4.888, + "completion_sigverify_p50-ms": 76.76, + "completion_sigverify_p95-ms": 78.17, + "components/block": 128.0, + "cpu-ms/block": 167.3, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 82.76, + "full_to_ready_p95-ms": 84.18, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32288243.0, + "allocs/op": 207233.0 + } + }, + { + "sample": 6, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 82857969.0, + "avg_cpu_cores": 2.043, + "collection_p50-ms": 1.523, + "collection_p95-ms": 1.63, + "completion_parse_p50-ms": 3.449, + "completion_parse_p95-ms": 5.611, + "completion_sigverify_p50-ms": 74.33, + "completion_sigverify_p95-ms": 75.86, + "components/block": 128.0, + "cpu-ms/block": 169.3, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 215.2, + "full_to_ready_p50-ms": 81.41, + "full_to_ready_p95-ms": 83.26, + "mean_width": 7.997, + "preparation_wait_p50-ms": 0.000922, + "preparation_wait_p95-ms": 0.07145, + "tx/block": 33760.0, + "B/op": 34911190.0, + "allocs/op": 207376.0 + } + }, + { + "sample": 6, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 290467977.0, + "avg_cpu_cores": 0.6306, + "collection_p50-ms": 200.7, + "collection_p95-ms": 201.2, + "completion_parse_p50-ms": 3.626, + "completion_parse_p95-ms": 5.792, + "completion_sigverify_p50-ms": 82.06, + "completion_sigverify_p95-ms": 91.61, + "components/block": 128.0, + "cpu-ms/block": 183.2, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 91.79, + "full_to_ready_p95-ms": 98.0, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32205832.0, + "allocs/op": 207229.0 + } + }, + { + "sample": 6, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 203268498.0, + "avg_cpu_cores": 0.8573, + "collection_p50-ms": 200.9, + "collection_p95-ms": 201.0, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.01763, + "completion_sigverify_p95-ms": 0.02967, + "components/block": 128.0, + "cpu-ms/block": 174.3, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 2.37, + "full_to_ready_p95-ms": 2.511, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.01763, + "preparation_wait_p95-ms": 0.02097, + "tx/block": 33760.0, + "B/op": 33202425.0, + "allocs/op": 210058.0 + } + }, + { + "sample": 6, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 113739331.0, + "avg_cpu_cores": 1.899, + "collection_p50-ms": 6.908, + "collection_p95-ms": 8.387, + "completion_parse_p50-ms": 3.847, + "completion_parse_p95-ms": 5.097, + "completion_sigverify_p50-ms": 89.13, + "completion_sigverify_p95-ms": 91.48, + "components/block": 691.0, + "cpu-ms/block": 216.0, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 106.6, + "full_to_ready_p95-ms": 111.0, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 112042899.0, + "allocs/op": 227183.0 + } + }, + { + "sample": 6, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 110309323.0, + "avg_cpu_cores": 2.0, + "collection_p50-ms": 9.223, + "collection_p95-ms": 9.799, + "completion_parse_p50-ms": 3.51, + "completion_parse_p95-ms": 4.88, + "completion_sigverify_p50-ms": 80.54, + "completion_sigverify_p95-ms": 90.01, + "components/block": 691.0, + "cpu-ms/block": 220.6, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 2078.0, + "full_to_ready_p50-ms": 99.7, + "full_to_ready_p95-ms": 109.9, + "mean_width": 7.929, + "preparation_wait_p50-ms": 0.003385, + "preparation_wait_p95-ms": 0.006176, + "tx/block": 33760.0, + "B/op": 114532560.0, + "allocs/op": 228120.0 + } + }, + { + "sample": 6, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "iterations": 5, + "metrics": { + "ns/op": 302321983.0, + "avg_cpu_cores": 0.6997, + "collection_p50-ms": 201.0, + "collection_p95-ms": 201.5, + "completion_parse_p50-ms": 3.91, + "completion_parse_p95-ms": 4.54, + "completion_sigverify_p50-ms": 83.33, + "completion_sigverify_p95-ms": 85.29, + "components/block": 691.0, + "cpu-ms/block": 211.5, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 101.1, + "full_to_ready_p95-ms": 105.9, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 112070532.0, + "allocs/op": 227185.0 + } + }, + { + "sample": 6, + "variant": "candidate", + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "iterations": 5, + "metrics": { + "ns/op": 212242945.0, + "avg_cpu_cores": 1.089, + "collection_p50-ms": 200.8, + "collection_p95-ms": 201.3, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.01212, + "completion_sigverify_p50-ms": 0.09627, + "completion_sigverify_p95-ms": 0.3047, + "components/block": 691.0, + "cpu-ms/block": 231.2, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33750.0, + "full_to_ready_p50-ms": 11.24, + "full_to_ready_p95-ms": 11.41, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.101, + "preparation_wait_p95-ms": 0.1062, + "tx/block": 33760.0, + "B/op": 114276446.0, + "allocs/op": 241076.0 + } + } + ], + "summary": [ + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8", + "medians": { + "baseline": { + "ns/op": 122594527.0, + "avg_cpu_cores": 1.959, + "collection_p50-ms": 6.465, + "collection_p95-ms": 7.1345, + "completion_parse_p50-ms": 4.574, + "completion_parse_p95-ms": 5.255, + "completion_sigverify_p50-ms": 84.08, + "completion_sigverify_p95-ms": 86.08500000000001, + "components/block": 691.0, + "cpu-ms/block": 239.60000000000002, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 115.0, + "full_to_ready_p95-ms": 123.5, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260018538.0, + "allocs/op": 235460.5 + }, + "candidate": { + "ns/op": 109941733.0, + "avg_cpu_cores": 1.8975, + "collection_p50-ms": 6.663, + "collection_p95-ms": 7.3915, + "completion_parse_p50-ms": 3.6959999999999997, + "completion_parse_p95-ms": 4.961, + "completion_sigverify_p50-ms": 85.30000000000001, + "completion_sigverify_p95-ms": 88.43, + "components/block": 691.0, + "cpu-ms/block": 207.95, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 103.0, + "full_to_ready_p95-ms": 107.2, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 111946584.5, + "allocs/op": 227176.5 + } + }, + "paired_ratios": { + "ns/op": [ + 0.9157606016480484, + 0.8983480572855805, + 0.8958732493445192, + 0.8175978972511556, + 0.9385651395673379, + 0.902798816616987 + ], + "cpu-ms/block": [ + 0.8804898648648648, + 0.8708174502329521, + 0.8779661016949152, + 0.8073180225768782, + 0.9084158415841583, + 0.8755573571139036 + ], + "B/op": [ + 0.4304801173811123, + 0.43037553602809353, + 0.43090328157222957, + 0.43032986118695243, + 0.43044963541099457, + 0.43094939136916016 + ], + "full_to_ready_p50-ms": [ + 0.9276139410187667, + 0.9125, + 0.9140555058191584, + 0.8181089743589743, + 0.920875420875421, + 0.9033898305084745 + ] + } + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8", + "medians": { + "baseline": { + "ns/op": 118471106.0, + "avg_cpu_cores": 2.0755, + "collection_p50-ms": 8.404499999999999, + "collection_p95-ms": 9.3885, + "completion_parse_p50-ms": 4.0495, + "completion_parse_p95-ms": 4.7925, + "completion_sigverify_p50-ms": 80.41999999999999, + "completion_sigverify_p95-ms": 83.13499999999999, + "components/block": 691.0, + "cpu-ms/block": 246.14999999999998, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 1940.5, + "full_to_ready_p50-ms": 109.94999999999999, + "full_to_ready_p95-ms": 112.75, + "mean_width": 7.933999999999999, + "preparation_wait_p50-ms": 0.0044215, + "preparation_wait_p95-ms": 0.0055785, + "tx/block": 33760.0, + "B/op": 265467053.0, + "allocs/op": 236415.0 + }, + "candidate": { + "ns/op": 108861585.5, + "avg_cpu_cores": 1.9975, + "collection_p50-ms": 9.1395, + "collection_p95-ms": 9.703, + "completion_parse_p50-ms": 3.7205, + "completion_parse_p95-ms": 4.669499999999999, + "completion_sigverify_p50-ms": 80.625, + "completion_sigverify_p95-ms": 87.02000000000001, + "components/block": 691.0, + "cpu-ms/block": 218.2, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 2028.5, + "full_to_ready_p50-ms": 98.225, + "full_to_ready_p95-ms": 107.30000000000001, + "mean_width": 7.931, + "preparation_wait_p50-ms": 0.003487, + "preparation_wait_p95-ms": 0.0061805, + "tx/block": 33760.0, + "B/op": 114427432.5, + "allocs/op": 228099.5 + } + }, + "paired_ratios": { + "ns/op": [ + 0.8821061499566317, + 0.9632092875139537, + 0.8920642090832533, + 0.8961627067766412, + 0.9663841422814191, + 0.934050253549925 + ], + "cpu-ms/block": [ + 0.8594185583432896, + 0.9459234608985025, + 0.8543451652386781, + 0.8632686084142396, + 0.9160274525635851, + 0.9011437908496731 + ], + "B/op": [ + 0.4305651299904384, + 0.43098028878633016, + 0.4311480628525499, + 0.43153875434357863, + 0.4308293102489762, + 0.431394635138336 + ], + "full_to_ready_p50-ms": [ + 0.8464566929133859, + 0.948087431693989, + 0.8810835629017447, + 0.8814102564102564, + 0.942883046237534, + 0.9055404178019982 + ] + } + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8", + "medians": { + "baseline": { + "ns/op": 319117819.0, + "avg_cpu_cores": 0.7849999999999999, + "collection_p50-ms": 201.3, + "collection_p95-ms": 201.85000000000002, + "completion_parse_p50-ms": 4.577500000000001, + "completion_parse_p95-ms": 5.101, + "completion_sigverify_p50-ms": 85.505, + "completion_sigverify_p95-ms": 92.265, + "components/block": 691.0, + "cpu-ms/block": 250.5, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 117.25, + "full_to_ready_p95-ms": 123.05000000000001, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 260100833.5, + "allocs/op": 235465.0 + }, + "candidate": { + "ns/op": 306414200.0, + "avg_cpu_cores": 0.7164, + "collection_p50-ms": 201.3, + "collection_p95-ms": 201.6, + "completion_parse_p50-ms": 4.0954999999999995, + "completion_parse_p95-ms": 4.7075, + "completion_sigverify_p50-ms": 85.18, + "completion_sigverify_p95-ms": 91.97, + "components/block": 691.0, + "cpu-ms/block": 219.55, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 103.85, + "full_to_ready_p95-ms": 111.5, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 112084236.0, + "allocs/op": 227185.0 + } + }, + "paired_ratios": { + "ns/op": [ + 0.9398181596682805, + 0.9517653835331482, + 0.979241740522063, + 0.9845051486926455, + 0.9427394030426424, + 0.9506779310864465 + ], + "cpu-ms/block": [ + 0.820443062572872, + 0.8478175383405426, + 0.9232026143790849, + 0.9296875, + 0.8456299659477866, + 0.8573165788406972 + ], + "B/op": [ + 0.4309787563629935, + 0.43124985915617925, + 0.4306612852303245, + 0.4305710166725875, + 0.4310845754469543, + 0.43087425651763905 + ], + "full_to_ready_p50-ms": [ + 0.8635593220338984, + 0.8723404255319149, + 0.9175965665236052, + 0.9131944444444444, + 0.8530101641907739, + 0.864102564102564 + ] + } + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8", + "medians": { + "baseline": { + "ns/op": 228250477.5, + "avg_cpu_cores": 1.194, + "collection_p50-ms": 201.0, + "collection_p95-ms": 201.5, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.008445, + "completion_sigverify_p50-ms": 0.10830000000000001, + "completion_sigverify_p95-ms": 0.23864999999999997, + "components/block": 691.0, + "cpu-ms/block": 273.20000000000005, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33755.0, + "full_to_ready_p50-ms": 25.71, + "full_to_ready_p95-ms": 31.060000000000002, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.1265, + "preparation_wait_p95-ms": 0.1313, + "tx/block": 33760.0, + "B/op": 307487420.0, + "allocs/op": 250062.5 + }, + "candidate": { + "ns/op": 212793283.0, + "avg_cpu_cores": 1.1125, + "collection_p50-ms": 200.95, + "collection_p95-ms": 201.45, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.012975, + "completion_sigverify_p50-ms": 0.10655, + "completion_sigverify_p95-ms": 0.3325, + "components/block": 691.0, + "cpu-ms/block": 236.8, + "data_shreds/block": 44160.0, + "early_verified_tx/block": 33745.5, + "full_to_ready_p50-ms": 11.435, + "full_to_ready_p95-ms": 12.655, + "mean_width": 7.001, + "preparation_wait_p50-ms": 0.10545, + "preparation_wait_p95-ms": 0.1123, + "tx/block": 33760.0, + "B/op": 114248747.5, + "allocs/op": 241061.5 + } + }, + "paired_ratios": { + "ns/op": [ + 0.9400183145773918, + 0.9304854255599625, + 0.9370596252848155, + 0.9251756487759943, + 0.9278233327936518, + 0.9291633370175537 + ], + "cpu-ms/block": [ + 1.0507407407407408, + 0.852264291017075, + 0.8925680647534951, + 0.8578698655034532, + 0.8652585579024034, + 0.8364688856729378 + ], + "B/op": [ + 0.371315674805637, + 0.371751339469171, + 0.3715517807242803, + 0.37134226522721586, + 0.37090241933553086, + 0.3716546689720987 + ], + "full_to_ready_p50-ms": [ + 0.5033636723387416, + 0.42591876208897483, + 0.456394211967149, + 0.39505300353356887, + 0.4096512856639662, + 0.44043887147335425 + ] + } + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8", + "medians": { + "baseline": { + "ns/op": 86284007.0, + "avg_cpu_cores": 1.9915, + "collection_p50-ms": 1.131, + "collection_p95-ms": 1.3054999999999999, + "completion_parse_p50-ms": 4.2045, + "completion_parse_p95-ms": 4.5120000000000005, + "completion_sigverify_p50-ms": 75.965, + "completion_sigverify_p95-ms": 76.995, + "components/block": 128.0, + "cpu-ms/block": 171.45, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 85.295, + "full_to_ready_p95-ms": 87.33500000000001, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59262017.5, + "allocs/op": 208746.0 + }, + "candidate": { + "ns/op": 84634300.5, + "avg_cpu_cores": 1.9945, + "collection_p50-ms": 1.161, + "collection_p95-ms": 1.2785, + "completion_parse_p50-ms": 3.3979999999999997, + "completion_parse_p95-ms": 4.7515, + "completion_sigverify_p50-ms": 77.17500000000001, + "completion_sigverify_p95-ms": 78.605, + "components/block": 128.0, + "cpu-ms/block": 168.55, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 83.355, + "full_to_ready_p95-ms": 85.155, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32247037.0, + "allocs/op": 207231.0 + } + }, + "paired_ratios": { + "ns/op": [ + 0.9746922616368424, + 0.9951789090799403, + 0.9926956952263228, + 0.9777527841466942, + 1.0031666961884662, + 0.9638762929062183 + ], + "cpu-ms/block": [ + 0.9776207302709069, + 0.9942163100057837, + 0.992982456140351, + 0.9777777777777777, + 1.011634671320535, + 0.9620471535365153 + ], + "B/op": [ + 0.5426052293108848, + 0.543362814763171, + 0.544037264384435, + 0.5439978034258355, + 0.5456395572515589, + 0.5462334368408132 + ], + "full_to_ready_p50-ms": [ + 0.9666627260905544, + 0.9811827956989247, + 0.9894155004116194, + 0.9733003999059046, + 1.0008119707690524, + 0.9588691924458348 + ] + } + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8", + "medians": { + "baseline": { + "ns/op": 87248672.0, + "avg_cpu_cores": 2.043, + "collection_p50-ms": 1.608, + "collection_p95-ms": 1.7635, + "completion_parse_p50-ms": 3.8935, + "completion_parse_p95-ms": 4.551, + "completion_sigverify_p50-ms": 76.625, + "completion_sigverify_p95-ms": 80.185, + "components/block": 128.0, + "cpu-ms/block": 177.95, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 134.5, + "full_to_ready_p50-ms": 86.005, + "full_to_ready_p95-ms": 89.575, + "mean_width": 7.9965, + "preparation_wait_p50-ms": 0.000796, + "preparation_wait_p95-ms": 0.022195999999999997, + "tx/block": 33760.0, + "B/op": 61994718.5, + "allocs/op": 208907.0 + }, + "candidate": { + "ns/op": 82776496.5, + "avg_cpu_cores": 2.0505, + "collection_p50-ms": 1.5775000000000001, + "collection_p95-ms": 1.7285, + "completion_parse_p50-ms": 3.5694999999999997, + "completion_parse_p95-ms": 4.5815, + "completion_sigverify_p50-ms": 74.05, + "completion_sigverify_p95-ms": 77.295, + "components/block": 128.0, + "cpu-ms/block": 169.2, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 107.6, + "full_to_ready_p50-ms": 80.97999999999999, + "full_to_ready_p95-ms": 83.73, + "mean_width": 7.996, + "preparation_wait_p50-ms": 0.000912, + "preparation_wait_p95-ms": 0.4726, + "tx/block": 33760.0, + "B/op": 34792762.0, + "allocs/op": 207390.0 + } + }, + "paired_ratios": { + "ns/op": [ + 1.0017513290145441, + 0.9453831442147634, + 0.8998773191728862, + 0.9890803420162109, + 0.9263731670193178, + 0.9405173998621812 + ], + "cpu-ms/block": [ + 0.9917888563049853, + 0.9562996594778661, + 0.9007352941176471, + 0.9877336448598131, + 0.9385749385749386, + 0.9421257651641626 + ], + "B/op": [ + 0.5594570429623663, + 0.5588242475689451, + 0.5616061781129013, + 0.5607965311540477, + 0.5608448552296642, + 0.5639668909248479 + ], + "full_to_ready_p50-ms": [ + 0.9914930341511528, + 0.9362554495110167, + 0.895277656318321, + 0.9850801027271615, + 0.8954137587238286, + 0.9342437456965802 + ] + } + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8", + "medians": { + "baseline": { + "ns/op": 286103124.0, + "avg_cpu_cores": 0.60885, + "collection_p50-ms": 200.85000000000002, + "collection_p95-ms": 201.05, + "completion_parse_p50-ms": 3.5839999999999996, + "completion_parse_p95-ms": 4.243, + "completion_sigverify_p50-ms": 76.47, + "completion_sigverify_p95-ms": 78.07499999999999, + "components/block": 128.0, + "cpu-ms/block": 174.2, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 85.42, + "full_to_ready_p95-ms": 86.235, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 59261713.5, + "allocs/op": 208744.5 + }, + "candidate": { + "ns/op": 284508953.5, + "avg_cpu_cores": 0.59975, + "collection_p50-ms": 200.8, + "collection_p95-ms": 201.05, + "completion_parse_p50-ms": 3.642, + "completion_parse_p95-ms": 4.34, + "completion_sigverify_p50-ms": 76.015, + "completion_sigverify_p95-ms": 78.86500000000001, + "components/block": 128.0, + "cpu-ms/block": 170.75, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 0.0, + "full_to_ready_p50-ms": 83.565, + "full_to_ready_p95-ms": 86.255, + "mean_width": 8.0, + "preparation_wait_p50-ms": 0.0, + "preparation_wait_p95-ms": 0.0, + "tx/block": 33760.0, + "B/op": 32246849.5, + "allocs/op": 207230.5 + } + }, + "paired_ratios": { + "ns/op": [ + 0.9937660087088263, + 0.9599000052041363, + 0.9945451045909277, + 0.9957213421590944, + 0.9962654638553642, + 1.0154857889278281 + ], + "cpu-ms/block": [ + 0.9760932944606414, + 0.8787245164662832, + 0.9780600461893766, + 0.9823059360730594, + 0.9896848137535816, + 1.0534790109258194 + ], + "B/op": [ + 0.5422762671117071, + 0.5459361007437157, + 0.5439949351797095, + 0.5447114222584726, + 0.5440807130463787, + 0.5435762541915306 + ], + "full_to_ready_p50-ms": [ + 0.9793605344786448, + 0.8553244334057747, + 0.9892713982551286, + 0.9740168539325843, + 0.9922716627634659, + 1.0731906933239799 + ] + } + }, + { + "name": "BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8", + "medians": { + "baseline": { + "ns/op": 207240378.0, + "avg_cpu_cores": 0.8925000000000001, + "collection_p50-ms": 200.7, + "collection_p95-ms": 201.1, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.018265, + "completion_sigverify_p95-ms": 0.02266, + "components/block": 128.0, + "cpu-ms/block": 184.95, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 5.6995000000000005, + "full_to_ready_p95-ms": 10.009, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.02256, + "preparation_wait_p95-ms": 0.02418, + "tx/block": 33760.0, + "B/op": 68208508.5, + "allocs/op": 211687.0 + }, + "candidate": { + "ns/op": 203113946.0, + "avg_cpu_cores": 0.86705, + "collection_p50-ms": 200.8, + "collection_p95-ms": 201.0, + "completion_parse_p50-ms": 0.0, + "completion_parse_p95-ms": 0.0, + "completion_sigverify_p50-ms": 0.016695, + "completion_sigverify_p95-ms": 0.023855, + "components/block": 128.0, + "cpu-ms/block": 176.10000000000002, + "data_shreds/block": 8128.0, + "early_verified_tx/block": 33760.0, + "full_to_ready_p50-ms": 2.2874999999999996, + "full_to_ready_p95-ms": 2.4505, + "mean_width": 7.912, + "preparation_wait_p50-ms": 0.017895, + "preparation_wait_p95-ms": 0.019264999999999997, + "tx/block": 33760.0, + "B/op": 33092654.0, + "allocs/op": 210058.0 + } + }, + "paired_ratios": { + "ns/op": [ + 0.979626446397073, + 0.9800711976642443, + 0.9805513776165456, + 0.9784646516152438, + 0.9773330692247688, + 0.9823734076227761 + ], + "cpu-ms/block": [ + 0.9459167117360735, + 0.9769357495881384, + 0.9367567567567568, + 0.987481945113144, + 0.9461045891141943, + 0.9503816793893131 + ], + "B/op": [ + 0.48566852069517175, + 0.48334060770190196, + 0.4852564256254716, + 0.4840454233959285, + 0.48688530140559333, + 0.48667443471323013 + ], + "full_to_ready_p50-ms": [ + 0.3992779783393502, + 0.4153676875114616, + 0.4205969049373618, + 0.3519091847265222, + 0.3030944194657498, + 0.40450588837685614 + ] + } + } + ] +} diff --git a/docs/transaction_sigverify_streaming.md b/docs/transaction_sigverify_streaming.md index 84d499b3..edd3629b 100644 --- a/docs/transaction_sigverify_streaming.md +++ b/docs/transaction_sigverify_streaming.md @@ -17,10 +17,12 @@ and repair catch-up without a mode transition or a 200 ms batching delay. 3. Signature groups contain available transactions up to the configured lane target. Transactions with multiple signatures remain indivisible. A rolling per-request window refills when any group finishes, without a wave barrier. -4. Once the full slot is assembled, completion rechecks exact batch bytes, - processes all Alpenglow markers and FEC roots, and constructs the final ordered - block. It submits any transactions not already covered and joins signature - work for the retained batches before marking the block verified. +4. Once the full slot is assembled, completion compares shred slices directly + against the cached component bytes, including padding. Cache hits avoid a + second component buffer; misses allocate a fresh buffer at its exact size. + Completion processes all Alpenglow markers and FEC roots and constructs the + final ordered block. It submits any transactions not already covered and joins + signature work for the retained batches before marking the block verified. 5. The existing replay pipeline receives the verified block. This change does not execute transactions before full-slot admission, alter execution batching, or move parent-dependent validation ahead of its required state. @@ -97,3 +99,5 @@ throughput alone is insufficient evidence of end-to-end replay improvement. Measured Zen 5 results, raw logs, and validation details are in the [September 12 benchmark report](results/sigverify-streaming/2026-09-12-zen5/README.md). +The subsequent [direct cache-comparison report](results/sigverify-direct-cache/2026-09-12-zen5/README.md) +isolates the removal of redundant component-buffer construction at completion. diff --git a/pkg/turbine/entries.go b/pkg/turbine/entries.go index 333eb23a..b4657ce1 100644 --- a/pkg/turbine/entries.go +++ b/pkg/turbine/entries.go @@ -135,21 +135,23 @@ func decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds []*Shred, timings *en var entryBatches []*prefetchedShredBatch var parentInfo *AlpenglowParentInfo var blockFooter *BlockFooter - var batchBytes []byte var batchStart uint32 + var batchStartPos, batchSize int var haveBatch bool - for _, shred := range shreds { + for shredPos, shred := range shreds { if shred == nil || shred.Type != ShredTypeData { continue } if !haveBatch { batchStart = shred.Index + batchStartPos = shredPos haveBatch = true } - batchBytes = append(batchBytes, shred.Data...) + batchSize += len(shred.Data) if !shred.DataComplete() { continue } + batchShreds := shreds[batchStartPos : shredPos+1] var batch *prefetchedShredBatch if timings != nil { if cached := timings.prefetched[batchStart]; cached != nil && cached.start == batchStart && cached.end == shred.Index { @@ -172,12 +174,22 @@ func decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds []*Shred, timings *en } // Bounds alone cannot prove identity after repair or replacement. // Read decoded fields only after the preparation channel closes. - if bytes.Equal(batchBytes, cached.raw) { + // Compare the original slices directly: a cache hit needs no + // second component buffer or copies of already decoded bytes. + if len(cached.raw) == batchSize && dataShredBatchMatches(batchShreds, cached.raw) { batch = cached } } } if batch == nil { + // A miss owns a fresh, exactly sized backing array. Transactions + // retain instruction-data slices into it after this call returns. + batchBytes := make([]byte, 0, batchSize) + for _, part := range batchShreds { + if part != nil && part.Type == ShredTypeData { + batchBytes = append(batchBytes, part.Data...) + } + } batch = decodeClosedShredBatch(batchBytes, batchStart, shred.Index) if timings != nil { timings.transactionParse += batch.parseDuration @@ -200,18 +212,16 @@ func decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds []*Shred, timings *en if batch.footer != nil { blockFooter = batch.footer } - batchBytes = nil + batchSize = 0 haveBatch = false continue } entryBatches = append(entryBatches, batch) - // Decoded transactions retain slices into the batch buffer for instruction data. - // Keep the backing array alive instead of reusing and overwriting it. - batchBytes = nil + batchSize = 0 haveBatch = false } - if len(batchBytes) != 0 { - return nil, nil, nil, fmt.Errorf("slot ended with %d undecoded entry bytes", len(batchBytes)) + if batchSize != 0 { + return nil, nil, nil, fmt.Errorf("slot ended with %d undecoded entry bytes", batchSize) } var entries []Entry for _, batch := range entryBatches { @@ -231,6 +241,23 @@ func decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds []*Shred, timings *en return entries, parentInfo, blockFooter, nil } +// dataShredBatchMatches is equivalent to comparing raw with the concatenated +// data bytes, including all padding. Neither equal prefixes nor changed lengths +// can reuse a cached signature verdict. It does not retain or allocate buffers. +func dataShredBatchMatches(shreds []*Shred, raw []byte) bool { + offset := 0 + for _, shred := range shreds { + if shred == nil || shred.Type != ShredTypeData { + continue + } + if len(shred.Data) > len(raw)-offset || !bytes.Equal(shred.Data, raw[offset:offset+len(shred.Data)]) { + return false + } + offset += len(shred.Data) + } + return offset == len(raw) +} + // decodeClosedShredBatch owns raw through the returned decoded transactions. // It decodes the same padded component envelope for early and full assembly; // marker merging and UpdateParent selection still require the full slot. diff --git a/pkg/turbine/entries_direct_compare_test.go b/pkg/turbine/entries_direct_compare_test.go new file mode 100644 index 00000000..4fb55637 --- /dev/null +++ b/pkg/turbine/entries_direct_compare_test.go @@ -0,0 +1,100 @@ +package turbine + +import ( + "bytes" + "context" + "testing" + + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func TestDataShredBatchMatchesChecksLengthsAndEverySlice(t *testing.T) { + raw := []byte("0123456789abcdefghijklmnopqrstuvwxyz") + shreds := []*Shred{ + {Type: ShredTypeData, Data: raw[:10]}, + nil, + {Type: ShredTypeCode, Data: []byte("coding bytes are not component data")}, + {Type: ShredTypeData}, + {Type: ShredTypeData, Data: raw[10:20]}, + {Type: ShredTypeData, Data: raw[20:]}, + } + require.True(t, dataShredBatchMatches(shreds, bytes.Clone(raw))) + for i := range raw { + changed := bytes.Clone(raw) + changed[i] ^= 1 + require.False(t, dataShredBatchMatches(shreds, changed), "changed byte %d", i) + } + for i := 0; i < len(raw); i++ { + require.False(t, dataShredBatchMatches(shreds, raw[:i]), "truncated at %d", i) + } + require.False(t, dataShredBatchMatches(shreds, append(bytes.Clone(raw), 0))) + require.True(t, dataShredBatchMatches(nil, nil)) + require.False(t, dataShredBatchMatches(nil, raw)) +} + +func TestEntryDecodeDirectComparisonCannotReuseChangedSignatureVerdict(t *testing.T) { + v := newTransactionVerifier(2, 16, nil) + defer v.closeAndWait() + txs := verifierSignedTransactions(t, 1) + raw := prefetchTestPayload(t, txs) + cache := decodedPrefetchForTest(paddedComponentShreds(t, raw, 0, 0xa5)) + cached := cache[0] + var err error + cached.verification, err = v.submitTransactions(context.Background(), entryBatchTransactions(cached.entries)) + require.NoError(t, err) + _, err = cached.verification.wait() + require.NoError(t, err) + + bad := *txs[0] + bad.Signatures = append([]solana.Signature(nil), bad.Signatures...) + bad.Signatures[0][0] ^= 1 + changed := prefetchTestPayload(t, []*solana.Transaction{&bad}) + require.Len(t, changed, len(raw)) + timings := entryDecodeTimings{prefetched: cache} + entries, _, _, err := decodeEntriesAndAlpenglowMarkersFromDataShreds(paddedComponentShreds(t, changed, 0, 0xa5), &timings) + require.NoError(t, err) + require.Len(t, timings.retained, 1) + require.NotSame(t, cached, timings.retained[0]) + require.Nil(t, timings.retained[0].verification) + blk := BlockFromEntries(100, 99, entries) + require.Equal(t, bad.Signatures[0], blk.Transactions[0].Signatures[0]) + require.ErrorContains(t, verifyDecodedEntryBatches(context.Background(), blk, timings.retained, v), "failed signature verification") + require.False(t, blk.TransactionSignaturesVerified()) +} + +func TestEntryDecodeDirectComparisonRejectsChangedCachedLength(t *testing.T) { + raw := prefetchTestPayload(t, verifierSignedTransactions(t, 1)) + for _, delta := range []int{-1, 1} { + shreds := paddedComponentShreds(t, raw, 0, 0xa5) + cache := decodedPrefetchForTest(shreds) + cached := cache[0] + if delta < 0 { + cached.raw = cached.raw[:len(cached.raw)-1] + } else { + cached.raw = append(cached.raw, 0) + } + timings := entryDecodeTimings{prefetched: cache} + entries, _, _, err := decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds, &timings) + require.NoError(t, err) + require.Len(t, entries, 1) + require.NotSame(t, cached, timings.retained[0]) + } +} + +func FuzzDataShredBatchMatchesConcatenatedBytes(f *testing.F) { + f.Add([]byte("a component spanning several shreds"), []byte("a component spanning several shreds"), uint8(3)) + f.Add([]byte("truncated"), []byte("truncate"), uint8(1)) + f.Add([]byte{}, []byte{}, uint8(0)) + f.Fuzz(func(t *testing.T, data, candidate []byte, split uint8) { + if len(data) > 64*1024 || len(candidate) > 64*1024 { + t.Skip() + } + shreds := []*Shred{nil, {Type: ShredTypeCode, Data: []byte{1, 2, 3}}} + width := int(split) + 1 + for start := 0; start < len(data); start += width { + shreds = append(shreds, &Shred{Type: ShredTypeData, Data: data[start:min(start+width, len(data))]}) + } + require.Equal(t, bytes.Equal(data, candidate), dataShredBatchMatches(shreds, candidate)) + }) +} From 9a30c693f4d5a77f7fb82f7de21bcf74c9a8b6dd Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sat, 12 Sep 2026 22:33:00 -0500 Subject: [PATCH 05/22] Reduce completion ordering and root work; bundle ready verification groups --- .../2026-09-12-zen5/README.md | 195 +++ .../baseline-root-adapter.go.txt | 7 + .../2026-09-12-zen5/raw-results.tar.gz | Bin 0 -> 53842 bytes .../2026-09-12-zen5/summarize.py | 59 + .../2026-09-12-zen5/summary.json | 1430 +++++++++++++++++ docs/transaction_sigverify_streaming.md | 23 +- pkg/turbine/assembler.go | 81 +- pkg/turbine/completion_order_root_test.go | 228 +++ pkg/turbine/entries.go | 5 + pkg/turbine/entry_prefetch_benchmark_test.go | 22 +- pkg/turbine/fec_root_cache.go | 56 + pkg/turbine/receiver.go | 7 +- pkg/turbine/sigcache.go | 17 +- pkg/turbine/transaction_job_groups_test.go | 118 ++ pkg/turbine/transaction_verifier.go | 71 +- pkg/turbine/transaction_verifier_test.go | 42 +- 16 files changed, 2285 insertions(+), 76 deletions(-) create mode 100644 docs/results/completion-followup/2026-09-12-zen5/README.md create mode 100644 docs/results/completion-followup/2026-09-12-zen5/baseline-root-adapter.go.txt create mode 100644 docs/results/completion-followup/2026-09-12-zen5/raw-results.tar.gz create mode 100644 docs/results/completion-followup/2026-09-12-zen5/summarize.py create mode 100644 docs/results/completion-followup/2026-09-12-zen5/summary.json create mode 100644 pkg/turbine/completion_order_root_test.go create mode 100644 pkg/turbine/fec_root_cache.go create mode 100644 pkg/turbine/transaction_job_groups_test.go diff --git a/docs/results/completion-followup/2026-09-12-zen5/README.md b/docs/results/completion-followup/2026-09-12-zen5/README.md new file mode 100644 index 00000000..1fb87113 --- /dev/null +++ b/docs/results/completion-followup/2026-09-12-zen5/README.md @@ -0,0 +1,195 @@ +# Shred ordering, authenticated roots, and larger verification jobs + +For 33,760 maximum-size transactions arriving over 200 ms, the final-shred-to-ready +tail fell from **11.24 to 6.44 ms**, a **42.7% reduction**. Catch-up assembly with +overlap enabled improved from **105.36 to 99.75 ms**. These measurements compare +against `e063695f5b22a68990007c99c7dc7b64eef5568e`, which already contains streaming +verification and direct shred-to-cache byte comparison. This is not an +`alpenglow-dev` or whole-validator replay comparison. + +## Changes + +- Complete contiguous slots walk shred indexes directly. The decoder accepts + that order without sorting again; sparse or extra-tail input retains a sorted + fallback that includes every shred. +- The receiver passes the root it authenticated into assembly. Each FEC state + retains at most one selected source snapshot. Completion reuses that root only + if the source, parsed root inputs, and every payload byte still match. It keeps + the previous lowest-data-index / lowest-coding-position selection and error + behavior. Root selection avoids sorting, and relative data index zero avoids + a map scan. Repair, reset, and missing-hint paths retain root computation. +- Large ready signature requests dispatch four vector groups per job, normally + 32 one-signature transactions. Narya still processes eight-wide groups. With + two workers and target eight, requests below 128 ready transactions retain one + group per job. There is no timer or wait for more transactions. A request owns + at most two outstanding jobs under the tested configuration. Cancellation + joins admitted work and can stop between vector groups in a larger job. + +## Method and scope + +- Ryzen 7 9700X, eight physical cores / sixteen hardware threads; Go 1.26.4, + Narya `r51` at `c265ee9667131a556cd332c252b79de0860c7640`. +- `GOMAXPROCS=8`, affinity CPUs 0–7 (distinct physical cores), nice 10. Mithril + and Lightbringer continued running; the validator binary and PID were unchanged. +- Both generated fixtures contain 33,760 distinct valid one-signature + transactions, exactly 228 or 1,232 wire bytes each. Complete component bursts + span 200 ms at the synthetic tip. Catch-up offers all shreds immediately. + Components use a 60 KiB transaction-byte budget, a benchmark choice rather + than a protocol requirement, with real entry overhead and FEC padding. +- The final comparison has six alternating baseline/candidate pairs, each with + five timed blocks per condition. Tables show medians of the six reported + values. Residual columns are medians of per-run p50s, not pooled percentiles. +- The assembly benchmark now supplies already-authenticated roots as the receiver + does. Both binaries use the same benchmark source; a test-only baseline adapter + ignores the roots and calls the old admission method. Root snapshot creation + is inside the timed candidate path. Authentication and fixture construction + remain outside timing on both sides. +- Timed work includes assembly, entry decoding, Merkle/block identity checks, + transaction signature verification, and publication. Packet parsing, shred + authentication, network loss/recovery, disk I/O, transaction execution, PoH, + and commit are excluded. This is not a recorded arrival trace. + +## Final assembly results + +Both sides below have overlap enabled and use two signature workers. CPU-ms +sums process CPU across cores, rather than single-thread wall time. MB means +decimal megabytes of total Go allocation volume (`B/op`), not peak heap. + +| Wire bytes/tx | Final-shred-to-ready | First-arrival-to-ready | CPU-ms/block | Allocated MB/block | +|---|---:|---:|---:|---:| +| 228 | 2.35 → **1.40 ms** | 203.13 → **202.30 ms** | 175.25 → 170.25 | 33.09 → 33.44 | +| 1,232 | 11.24 → **6.44 ms** | 212.58 → **207.64 ms** | 235.40 → 233.55 | 114.14 → 115.56 | + +Every maximum-size pair improved residual latency, by 40.8–44.3%. The whole +arrival-to-ready reduction is 2.3%, since the approximately 201 ms collection +period remains. Root snapshots add about 1.4 MB of allocation volume per large +block net of the removed ordering allocations; this change primarily reduces +the completion tail, rather than total tip CPU work. + +| Catch-up wire bytes/tx | Overlap | Total assembly baseline → final | +|---|---|---:| +| 228 | Off | 83.98 → **80.58 ms** | +| 228 | On | 81.77 → **79.35 ms** | +| 1,232 | Off | 108.87 → **102.78 ms** | +| 1,232 | On | 105.36 → **99.75 ms** | + +These catch-up cases supply authenticated roots, as fresh verified ingress can. +Spool hydration has no retained root hints. A separate three-pair, five-block +comparison exercised that fallback using the same final production code: + +| Catch-up wire bytes/tx | Overlap | No-hint baseline → final | +|---|---|---:| +| 228 | Off | 83.71 → **82.23 ms** | +| 228 | On | 81.80 → **80.67 ms** | +| 1,232 | Off | 108.08 → **104.34 ms** | +| 1,232 | On | 105.57 → **101.32 ms** | + +This models root-hint availability, not actual spool read performance. + +## Choosing the dispatch size + +Four rotated screening rounds compared ordering alone and nominal job sizes of +8, 32, and 64 signatures. Each condition had five timed blocks per round. These +intermediate variants predate the final direct FEC index-zero lookup; their +assembly times must not be substituted for the final comparison above. +The job-size table holds the new dispatcher code constant to isolate policy; +the final assembly comparison instead uses the prior production implementation. + +| Signature-only catch-up | 8 per job | 32 per job | 64 per job | +|---|---:|---:|---:| +| 228-byte transactions, wall time | 77.53 ms | **75.04 ms** | 73.78 ms | +| 1,232-byte transactions, wall time | 86.66 ms | **83.57 ms** | 83.20 ms | +| 228-byte transactions, CPU-ms | 157.60 | **151.35** | 148.30 | +| 1,232-byte transactions, CPU-ms | 181.60 | **174.45** | 173.55 | + +32-signature jobs save about 3–4% catch-up wall time in this pool test. Sparse +four-transaction arrivals still dispatch immediately; their observed p95 +availability-to-verdict remained approximately 1 ms, dominated by synthetic +feed scheduling. The 64 policy is somewhat faster in catch-up, but offers little +additional benefit for maximum-size transactions and doubles the work one job +can put ahead of another request. We selected 32 as the smaller useful policy; +the measurements do not establish that 64 is harmful. + +The real lean transfer load/execute probe also ran before and during signature +load on the same eight cores. It used captured block 2622581's 33,760 228-byte +transactions, two workers, and target eight: + +| Nominal signatures/job | Catch-up execution slowdown | Tip execution slowdown | +|---|---:|---:| +| 8 | +6.71% | +2.28% | +| **32** | **+6.64%** | **+3.35%** | +| 64 | +5.56% | +3.96% | + +These are medians of three paired slowdown ratios, not ratios of aggregate +times. For 32-signature jobs, tip pairs ranged from +2.38% to +4.40%; for eight, +from +0.81% to +4.16%. There is no clear large contention penalty from the wider +jobs in this test, but the small differences should not be overinterpreted. +The two workloads run in separate processes, excluding shared Go scheduler/heap +effects, dependency planning, commit, and full replay. + +The initial contention round overlapped an artifact download/compression and +was excluded in full. An additional complete round replaced it. All original +logs are retained; the table uses rounds 2, 3, and 4 consistently for every policy. + +## Remaining completion work + +A separate 30-block diagnostic profile of the final maximum-size candidate +averaged 6.42 ms after full assembly: + +| Completion stage | Mean ms/block | +|---|---:| +| Entry decoding and cache-byte validation | 4.30 | +| Construct ordered shred slice | 0.82 | +| Collect/check FEC roots | 0.67 | +| Construct block and footer metadata | 0.46 | + +The final signature join's median was 0.106 ms. The stage values are distinct +coarse timers; bookkeeping and the join account for the remainder. The earlier +current-baseline profile measured approximately 3.1 ms in the two ordering +steps and 3.5 ms in root collection. Exact byte validation now dominates the +remaining work. + +## Validation and reproduction + +Native Turbine, replay, and node tests, the full native Turbine race suite, and +native vet passed. The full local Turbine race suite and vet passed on the final +source. Local txverify/sigverify/node tests passed. The local replay suite hit +its existing `promotion_test.go:349` zero-duration assertion, also reproduced +in isolated repetition, then passed on full rerun. That test and production +replay code are unchanged from the baseline; no timing assertion was relaxed. + +New regressions cover sparse/extra-tail ordering, public unsorted decode parity, +every root-snapshot payload byte and parsed input, replacement/reset/duplicate +admission, recovered-data/coding precedence, multisignature groups, failure +index attribution, bounded yielding, and cancellation ownership. The final +root-selector fuzz run passed 64,721 executions against the original sorted +algorithm, including unsupported types; an earlier screening run passed 128,732. +Every timed assembly asserts transaction coverage, exactly one verification per +retained signature, expected block identity/footer metadata, and no internal +native-verifier fallback. + +`raw-results.tar.gz` contains the raw screening, final, fallback, contention, +profile, and validation logs. `summary.json` is regenerated from it by +`python3 summarize.py`. Source/binary hashes and exact commands are included. +The three screening patches in the archive's `sources/` directory recreate +their intermediate source from `e063695f`; +for the 32/64 screening variants, change `defaultTransactionJobGroups` from one +to four/eight in the roots patch result. + +For the final candidate, build this checkout. For the baseline, check out +`e063695f`, copy the candidate's `entry_prefetch_benchmark_test.go` into it, and +copy `baseline-root-adapter.go.txt` to +`pkg/turbine/benchmark_root_adapter_test.go`. Then build and alternate binaries: + +```sh +go test -c -o /tmp/turbine-completion.test ./pkg/turbine +GOMAXPROCS=8 MITHRIL_SIGVERIFY_FLOW_BACKEND=r51 \ +MITHRIL_SIGVERIFY_FLOW_COUNT=33760 \ +taskset --cpu-list 0-7 /tmp/turbine-completion.test \ + -test.run='^$' -test.benchtime=5x -test.count=1 \ + -test.bench='^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*' +``` + +Select the target machine's distinct physical core IDs. The archived runners +record this server's paths and settings. The live validator was not deployed +or restarted by these experiments. diff --git a/docs/results/completion-followup/2026-09-12-zen5/baseline-root-adapter.go.txt b/docs/results/completion-followup/2026-09-12-zen5/baseline-root-adapter.go.txt new file mode 100644 index 00000000..9d645b92 --- /dev/null +++ b/docs/results/completion-followup/2026-09-12-zen5/baseline-root-adapter.go.txt @@ -0,0 +1,7 @@ +package turbine + +import "github.com/gagliardetto/solana-go" + +func (a *SlotAssembler) addShredFromWithRoot(s *Shred, repair bool, root *solana.Hash) (*slotCompletionWork, error) { + return a.addShredFrom(s, repair) +} diff --git a/docs/results/completion-followup/2026-09-12-zen5/raw-results.tar.gz b/docs/results/completion-followup/2026-09-12-zen5/raw-results.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..ee85538024754f5cd7c7079d06641a6a17454e9a GIT binary patch literal 53842 zcmYJZcRW`A|37|QW;WUDvR7udF+~3 z*b{-8vWzGGcb)I}(U(7#xmkGa)o;g|Yr@yL4YPeXD*wr4&B%R5eft_5F!i-5=;DAZ zz{~IKQ{cs_|5(WBL5wXMpxM`Q`~%COmCnSeYl7j~rL}OHBSyvr#-ZK4y@mFb5O}Zo zR2CKbrRmw}0=MnoBs9EJ+SGrsH3vMs&UXBk|NQ|ArAV>Rpo4`IWe|%p9KA%7@zMO>dKBLMLiwO*EX*%(cOQLG{ik@N%M()sh4z(XQXX$n~ zT)c~sL#^0i2L^_Wn)AEF$ZvZ8dxo%Ntf%_g^m{fmARzE==g&i zO|KkeU;3?RIo4vPMZ*B8A1r^6f4-ulxR&Lb#clpY%CTe?r}_L&-KESaVw3u|m(%Ri zdsltrC0<|W$4-CR-Hv*KBgf6w*qgDBU;1>^`&S#6bWxVkO!G-*o0RZVa5v>=qiQ?_ z|1EF+Hl}=IG8nxlk!Ik%A;Huaq#+pN&lKZKk|BavF*wxNJ8oqOh*p`n!q>fYckvHe?#(=nSwZ9t_J~T4aKg#+EO-0|BK8)LH zeIXPeOiAcY?4x`&xbaK&nxeV{{!>}SH#7%)9(0zc=dmovKgU=6Fo`swa(~%-b?QZLYO!Qc zjdX`XwXbk9j@v>Jne?(DaiAe|LL_I7yw0V`LJ{+UOl$wKhbf65mulr(%4*;5lhI_Z z<;2(7t+dzT#aV*-HmobdKWFkgW^$#Ky%4(+R^B9ox13kc+YS)1(e5*ryB4 z(26$Z!`vS?TXG`=arw%UF?3tS$;^SeT!r&*g-v{o=v@=b3exbsxv(*;Yg$k46y%KF zkwnMNbHJ$+ZD_$f?xO{iG~0lr^e$$WP;M6)m+KP!ClA$v{yLJ~%rzv%Gg-Wg9N1Qt z-a}Ru?qc4QyBKNGc{%p$ekf*-yga(==jMc1E{Zg7uL*p3S?!tdsVT+Z+4z)lhr9kz zR+Z@yVZFbEUZ$ZbV&)25B&&}RpAc$2j5CE@n_PorcDn^LEvZhga_RrIz}z~wpEr^| zh(PiC5O@zBdU5RvFb1V%&KnH}(49I^hs+zr7q%jMh{JLTPOo-yYR`RT^JK!>wJDye z=ik;Ve|VvxNO$mp{%6c^mOs-f)3wpaz($V`=JSi^<}^m+*j{?lxWC~jFTP!3g|yAm z(lmZ1Qs*rbZHirD)>iWMtXDqIZsl&pwjN#-Eo$fdkTfTskyd8F+G!OSp5}U_uB*vL6yZyd{fUH&X75s$<0U5)Y4Ce!+7qnP&-%tu?!^ zhucVSPZf&_ln65CHOTl>$DnOwqg19OD)hGao090YR#f9`FFgU0ft{<^j!jC03VbRQ z2#!QgSsASrp|nJ<9*Pmx&m$kTqt3$Grvyv6z~BZGRv-pL2Jm!wmNMylT>8b}n$Mi4 z28*bZr%#a__i-Pa%8A>UCGuq92%|mZU8N<|L+nEutUxT61rlgI4zSO?su^NG7*@e9 z?(_PLD}aA>1SN@aD7Q8#J4$B~QI_RCYPDbYjtWqV&@^=aB*l~DYANZf1Q*7I6P#z> ztNb+GXRn11bSPxA8s%wKwy(TcT_pH6!sebRsUQ{$Tqaq>8Dh1QmOxr#Rw-{vb&9lb z1D|K*(bHA~^X%)A1Q4NaqZ8##$oTD;NCzB9j&pW&ATY~4etjPG7xK}N6(NLP=XE;g zP6X%%TFMJ`d{fG~hadLhndkX1b40cx6iG({0dZ{eb#+KKW3L4noZSXE?QFY_1Y1yXm+q;Q1|^n!_n`4 zvu)}zOQn*@wV&VQI#e?~lYLC(yc%ReO|AMQ=hQ9zmW5I_F=t*v+H+>2owAbRHlX@` zn@CNtcDn2O>v9?1*xa0}9&QbUM$X|4Nb*Cd)m3qZ!>E& zrckP)BTw-qtXJA;=BYgV@|EV3Vg+>E_S-URBI}C&bnXYXAPpZU z!$mE~#Pq929H$X(;V2f9SM7=fXu;j<@~bcAsai>njmQtyeMT=aMEL`&=r!N;W%*sL zA2O!A#w=H0K?eI7%2S8%dkFNfx2N&ut5Ch`$AjGbg2tG5 zS+di1vMMy`X#~Z}^qmsPQbKsyf#bLpX-S3^dafY1Rv=X0d_sp-^ zj@1%clVR-$+BgO!myIZIU1#TVFyBGHjLQ+>ylrG>(DLNo%~Fe9{gl1;H41dlO>N`1 zx%J7M%85jD!46bV-JZ5F`HcbY*Iav_!w6({ky+}9CrWU8{K5;6y@zo+oTFXMVt>|$ z<&=Sq)Fl(FVAQ+HxW=-;!#R_~bFeFN^{u><{mxHxYJE9D&iMVtF7A`hH!N%G4(BAI znA+aG)LfUAo^offBx~3lMU$=WgWCs`eQS0)9XeQyIRTYYaQ*Q+CHW70qo3TPQW_5D zjJXpv!$ho!nPQ-R(W6Yu}ocNDVl`HP)GwqiGv= zZVC~^>dR~wyPA{Xar4BDmV#ULFVYrXoO|5XdILxX3xRV2mkMou9Mri)%_)dw#GDQF zG;-hGnY`O3h9IyL#eGhr3fP`0CY5~wF?+J(nLN>U)|?Z&o8ljt)gX}Yt5-zICztc> zN|uOa_beU5;#j5#QSTM!);jNIh^C_Bs{>4oj(pSwp z?@+Q-C(kJC|1{B|xi5Fvr&K;DnH2sf*DtZPV40;>()@*hvq-q=oxi+T^#1L;x;4sJ zfqPhV-g^+uUOTK`J%0ea^4>O@dNx@w4?3iB>n)(1Obs%z$Qy$|;LZ8rTpkq2LlL&$ zJvd=+eBJU2B&>|N{Y+5Bt9mR~wdQ0H}mYGqNUc{_Ygond2i`#mAi%9mGt zT^v^;7uz=zF!2L4{iZGY+N<6=uZT>giaAW%+C>c7H$CngPgxUi;<1*)U5>XDs6vmx z6_(5I&{iR1E+dBQGxf7#MG@3LR)Ohout$2m!h<05k&+6>m!^HAR+(O5zeOH<@m(Na zbiA?0;F1i}qgoi?)aMVyJrlq?DxgFCO|#BCGh+0X^cCQ>i{@~x^t;M=DDo{jEMnbA zU->Y`tJWwhpI}x7kM>m7cRpwj({wR;raK28x}Of=0b6g`&MGTwru zqSIS_&b~1L@cg9@bk2&AY)ueNFO)e$1)H3EbWV>wpaQSzb}=0@@R>xWgC-D7sXd&t zOj-Fp%F!AH!F*qMd@CU&6jeo@=VBE!A_h5@ zONL4Fwki`DzZ>R1j4cbKe0+GNTK>zu*LLmi@e4`@)80L<()fM-{6`$iN(ZgX->B>M z)&}^7Y3D=|{FahXyZ|0_bK3O-zKZ0)b4QbZ$-x{%#wiok-2TId)$j3D(uawmm(lA& z+61jPA;HQHNS9VrXHJ&BMFP?=zBbp0x3#o8C=3%9U!yCT!2xvnWqF!l|-8Cm{;JiZ|_U^XciMS4XVsAtQRCM1dmRl`*HP$IB zFEy%TeWVxRw#j^p*yyU=6dco|0|}GvI;_RNZ-OJNZe3WOQW=Ys{#+2g%gx4-rB{J% zxySc-nKmqe@>OhhQ4W6^_swQ9we>RMH z3yNQ+_-hfps$d|#0u|+S)_+)to334PCO1?EqPN18`K^?7=EvE? z=&zN|n~KF(LL1*Bg-6pr8R^gQ?V` zgwD($IfIWSP)3`-)4VZWr8D_VGGVEM+lKWaUegEke$z&DsNg7DnnT;3g)c@EJH@^cD~Q92oEj;0pilpA`fKiDs8uBocePR{ z4(If@uKL7Z{_YoIFjXoQP!)&Ur7RNK?N5zN&E5rk$BaLa)RFx1hh1b}_tXf%J)%6| z{XxS=`q2XFQWl)?_87 zodf2lr9ZCvF~X|%DPW=Z7Q@6KR&>=uPdUA$iWtU|!?yc$)UDg^4o!%E;rLmmh5ukh zq=LKOY2)@XQa|olw76#T!T2^>sHQH z_6rsGPzd~Anb-l|^)Yn$Fgn@II_OlozEZ;n%7Cp1_FGV@vv<3Ztv%av-2Q5i7?ZrGso~1wwf2LJNNb{8k5$w8vl8qslB>>2%|8=sE)BqM8a_&*q z2Chua1Cq~5!m*GKtQ+E z)K#x@8+Fv6J3fpgRX!9TVvd88S3VOz9Y)GnT*1-2h-*l~Mku|2^E~l!BSdFf=GN;} zh23}6I?9FVR^N0GWJ`!Qd;W@7cS%9pD^*n$3@1*{V;wY6XT-hZ+)2NRblLCvwI(EK zm5cg{Woz#?0C`5k{qRb!M3)JnMUPIqD&_%W-a#vK{(^QKvG5M+{@&;6Oy%?op17xc zH3i%igs?`x!rSv7XkKzg&H#dwm>VjL+8>!N>Hc!P3z^TSUy#6*<=Lq3V5{gZu@MC} zj=_JGolkGfaz05@Sf!Qg^y=brr9}PsoHTSH!D+Z`f>x)MBh(%fVHzfwY-J-|ZV&4T z$5T~`M>raiQYRYz%x>yr$JEct?+hG#7X`?m|Jm9H)-Wn~9jHIVeU^f2$l>j?N0k5VL;LS(6$I&TL&wBciguBGKX2~mew6>X z0`TwEGhFOqbfvXi)|*{T!=pm@h5qgXu7m1nlWd;MF?$#1olLc>xIKA2^73Y`LOm-% z#IE)hQZMq0`ded4w*{0tKiAQfbVjbtve%&zIEQsp8S>V;_$`Ms)OF5>BR@u?3NkZ~ zb2a^PjQY;Cp6e)G*}?={YjfqWEux%GC4Zi;HSQw!mQb2&PZCvjFv5f3;6cW-gp$)N z=@nQO{UaqcF3nsmm(4Y+>hQsQ!pyMuP65fltyv#f=UO-_K0WEh@Fd-$v4~a16wAC( z;U7+aq2^c5V|s|5u&cjyx}_Y|lRoEoM9nK9eN?6N1Ig+PYserEVlKu4f1YW@^889X zFp`wE>OCXoVI&^nSfw(`=zla_Q$_ak&Ld)fG38ECEWaJc8qz{gXDxC+LlfJp^Xox3 zGoOGf*);`_$16ndOtq(#zCrT2qd8A`4VgFJ9;)WOC3KZP3;$wd&$EQ;qL+|DM*r8o zI(ed6=+x`7gKw!_tFBYObhgjn_0R1vbIbo3iCdROqR#?KjrcSv`|Ll9h#4F6!Xoek z%xdglpE#9wb|U@~`6xXhVHGobN;M>D^`d&}iGn{<*5$}3o3^L!6ns|GS$B#$BhaF} z1eI4Yg%&9?yO?x9m1;E2Jg{W3wF1oLhMp@J>;tcjW(!rCUwh=;>&=9G7!EN;xg{^^ zWMS%U`>eEy=VAL>f@F6?=r~u3vFMKAaCS#S$xuJt^v~f2mLdk-{+UDy#nLZ3Vd9eq z=bsn|1?ivIB&FxS#w|DV<26Vmym~FV0ZiTVEUBrB3NK{{{Zo@L6Y)2;WLs&v6isA! zk{>njbhC{T+R1a~?6upelWPg)Nmz6g;}Z}af7`;c<-b`l9@W0=Y)lx~vsWzJv+uC( z|I3_EUKxq!wa1;JY)8@-kF|O)MpQCeCZobB;~lOd!a>~=OdoSsBSQ<3>Gd4WD(L4e zE(Xr*3YQ^4^leVxRae2H;#cjOk#znqehv4@&MIv$hj1=pWkfbmO$4x>a|(``Pk%cIi1h(prN$6VGgLF$XV$?AK;=N?B_^j!n3tQvX+tY})V4*Yf0T^8dUC`^-S z1;h@U!9mf##-(a~$?*k=*%Ws%+)|8MZK$%I^imr_`L5UVm0M3l8_iT)Ez!%0O6?m>D_Hcl2uee+(ROl3s`Kde$1)ERgLj(E z2=>M9BiWx8igkbq;SB=i=9y0KQ2kKtZj1r^PS3-D-+QwbGBYN`XRk3c~uw=Zd3g@PH7iw&X=s)G`Veb1?;7b5+T9n2cz$XzSCR94^z6Hv+S2K9N%&# zMNltg@cHpC6GhQC%5(gC(jsBJMJ8&8kc#&{gG%4^HiTfC5=1RR3=vX6z8?sw6-C2> zoMPWY$;+m5{-R;D9|)k^UdI2HMy*of38`hFyy{^S0oMY(5ZT*VA@1|JZ`a!K)}S!Q z=dSS{)q(@OIfq3lzN%qiC;t7Kk45PljQcj)J;gd}?@@D>(T^qj=tSswY?c@E1yXZr ze%$;kVko5AWyExFmy(h|Q2Iu@_cevEpfpm1<2)}k5BNwbX!c)1W zG>v#y$17MVEt+Wg>l4{XPRSTs_)X0}!x<%wv2hc4d7kzRDp9(~W3TfD2I!6wmBJyO ziYqr%dv{Q|GVMwpG=-i znHO?QPq&(*II4fC#w>b6ETiZ`d!uz#^b1FkbJL>TlVRle>tSTtthnCCbJqFm*>S-( zoC7POx$P{on;s+wyc?g)Q|iabH+@YGktq%3B5A3e4p11Q1 zW9U{#)?Os!qn=M`pvjZe(ea~ERJes#1O^2yq+UtzRWmiThP?`n;#{k-R&Mk8iwS?%b;s6^p@P5%1I44h?yZ?o#jd4MBR+x* zCQYs^_teq)2@N}3=T{xM_(N!{jRcjc1?6S$>rqi{t|DHwh*)!)_Ynw|TKtqHp`%G! zLTCyw3MLx-?4*u7R>^ajdk?cVH+%`6`WoWX6etFe>G%NI_CG(LckYi!2o`ha_7BA4 zGvKLip>a`zjblt^VG(&w6Oj!EnGJ_hb>ZQFMyZKI0L4C?eSf1PG*ewk(S`LAxlx z$R}jx2OQ1?O#NK*eE4cL);aq*kUv8;evXcA?pPApV8YLPKrTiuc#m)~b@ zs{uR`byC%H3C`8t+PGovN0~#v;+2?^8Q*0r$Q-^JjqX=~n588-V+CHnGceVE78fLX zMPBV5*LG_dDf>zX-#qzlBeP21QXP2?6Se=lDPJfhR?AMs+3;ap%38ObN?6N{C8i$S zUGppW*T<_Z|8zsucEp_VmQNwl*=kokF=Ea`S2BeYqaS)>?o^CUk{(x{{9rhE_SEtZ z?W4xX-#*adYfjEyRaVL?-HkG}s;-_}DfMh&pGH`?n}jKKHoYP5(dqiZ-!)Sf8w6Jx zm~N_BjQd0O#Hk!H*IbDN?jc{anG;d#oss`Fhr&DRM8crEY3IE2+(zl9Padd9z89#z z+nQQJ_|5p{h|-^X_2!vfB?xP!OU|Ns?e_6#?Sd|Yic1vtaLrSMR*%{+IRPiW|7J@g zg^bJ|n@*l?PPJo^-7Ek}!}rzal| zR=(pl8h0`3FjZF#_|LB;0t?Ci_Quyh>Vp9LM8QkR-WJVj?<>iF$J#-&)LC-@>?)P7 z^uMGZ`PH>3z$Zed-{NmUYhLtlvK1{&Epf7dqQ`yip^!Fp=&zkv+rE;UF7xFK^@}g} zVY8BG3^1M??Hs_%8KpT*GOJa4?ILoDpT@frj;d!kEi@iqKW{5^4fM>r@>*1diisdWMsz|Vz}dV3zA<~e`1pH z-YH6B975C9wjgb7nMUy8Pl2tNq66x%E}pl^9mP&lh>g@^rDX|&HB&Sb*6aBp7hX|S zjb;yXiobM?h=#=#tg2Why{pLS)lC@nvZo$W?tF$kT`DggSRIjx{PYGSMUb$oX2;Uxo*bQB>l%34)QCXxq-F8Jui z=jW0hlbGY<&9eIKWoBwSo%Gdw7liy@^am%9YW;pC;uuAjs#pJ9GSMF_)DF4h-@gKn z&~=K~V=kyViVx?0$XW3EzO5xZik8U@NU4`S5jf=3*!ltl=R;FU>gJKvZLa@(z(Tx> zrV`%JFR0gb@E(V?quMH0Rq@JYmgKV8%opSKm>zCjk3chNMay_iTNK?q!PGUN(#j*P z^-jFdzUE+{C?6a*2;OxXZIU>rwmJ8}`&LgYIk*59UgfR%x`Ps>NneBu1xiHj^teCk zQ?nC2LE@3j3KWabpCF~m$XX<2WXsHh`G~*B8fGIr&-@13922ez(rS-Z77k?k`NluC zol=9t?XaaJA?Lf8JjGwImdy6`nRxaEU4*Bku<-Gf7Wyk+Rk2!$+@NTFi{|Drs&#ub z`YbV%r#gQ!+>_%1%#zfTMd30jYw_h^{h4hKJ;~kHZQc%42z~~mnof5FQda*(O(MfJemWklgs;QSfmJmgFBtMgGmgF% zb_PG!n_?Ah(>+bVC*Ll?3Dfy+!zcS1!fE@b5IFdF|K>65r;nF5yr{nY5MCH^gg zPTEt!GdYT7OY6vLSw%`jU!8JGE+KVi9Vh1}!TcmVI=?SitAuF=o4`yqtAzMJA+xm8 zKJT34L|>Qw>?3l%R=MQ5vJosOg&`s+s_zt~;^Hi6Abf*bQup5gh5Jc(d6-57?Qj5G2Qod=zw4|S^UEYH6s=brW zUU}{*bsjfi11*BL!#RXJZ+lzz)pL;w>FrdLiR1aN|t1dwfvU zWi3f$CQ64NaQt<+;!Tu%63i;%EL!>4&FhAc)fL0Et@Ce<+#icTGz~8}RfdfyfNP`0 zP2JLQ;fuWQLX83Ahl($MmXpDEDceP2A&!KbU`-j@t7+!`O`!=M>mBo;N zd(>HIgMO4N6NpRRq~j!i=_IV`yMPM6IjSZZSRCc-rEf6|mp*(HkOvV9)SKt}I80?- zyL^y1PGnJDETk|0ogX4uHhP(0Sx8>utrj$>a7Tn7?=8W6*BEc|v?MQ8RL=-E6n?q5 zHGmUPwE0DvrsQ4nj?3&*(|QtfWf34sC*xYTe*EJ1n))vGUCV zMRxM&G}x*c%qyLHq-CCa1X!+9y_^5}oLLcQP|(HUM0`A&VS4?Q=z7=x5eU|Akyu%nthI?C-&I zsm)WPvrOeybi7C#GYCvf0tdf4J5S6qsIoqLJdwLxA>G4m`7&4T%FLR7QTzRFIU@Pv z(#x-u)-b);v>5%NqU5ag$d;S_hVHiR7=4tR&K&p>+&x5TvT~y&Yk)|>X7lI33SV(K zt8TzS5D#nPqGQm}`H@?!cSv#1q-GeioXO#IK#e57!R{&hQ;FW~{;y)zxbkaFUt0|t zgyxo4H)&UniV3^6`tv4(af;S7Pwe5<3^}@^=#PiA+-s9QM~7Lo>HmZ?%yeYs0(^Wz z;%GdWDg#pIuj6_3h`$u~sGO>-jFh9kKj&VFzvuS#do-PWASyR>vS+2KZN;rIz27ai zx6*{%_e_zYpXvMeKh}>zM>d3S2jLVyk7jU9z3!Y$eq!H{Mq%S2oAs~xVrZ=apuPuQ zoOiAnzv(^i_X9}mw=~wZ3($A~j?ahOak6LU3ArBu%Ou))V=`_M6Ont?e9abEyqh!q zh>ARbhx#VxoNpjXW zko8F+Y)d%oQh&?^D)c^G@SaoX(c(foZgt;=@+%`DE7g_Vl}Uxkgsi5|9{=FOJ=~-? zytfYztoTUhC~>N}!wo+=^M(F?C*JU{xCL{^niyC?MIP~Nx=2#8<%NQs$!^kGV|Js)}Dbc3~Fk|RZzrx2-zk=JMx)vXk zt^|&+V7GL`(R_^3Pd~b!{du7(pZW4j9@-}G;N#l!TZmW@+VK`Yrp3k)%leV)@loN| zIPv5aKlV1N1m-Tj61@H5?6WixYslllC|tp0^xG7MvQ(xn+bo|OUmx7~k!fnlNpkkb z>;Bt=zb+$g_H@sqRLo~5%Pjg98f8&uCWf!8-Z?Q{PyCX%_6tiVE_iFa=vGfU;>G2? zUC#!Edi+hx;cTy;Hj1N-N`V|qd{xxl!R7gV>(_k5HW@`W4`lG1NR1r@me$>To9)R(X%M2_}zE?fgDlU{5#h76!}#90w>Le4;p_4<(4_v zk#umIz5;WBPJ=7GZ3#Ju8Ei&Boxqc}wdu9N#ed6r>gBE@jKZw2x4 zyY!mtNWs-B2Csy)cf#0n7%*f9SH*vDJ>n!XOnhvtJh>u3bz9dkr!Rc^md)S+)3UU9 z=?ed~TVY{0i5Fb=z%$tw4^6^c)=?Vt%EN$D`2aL)0!&}UzY~K%yJs9iERv-^@7p{a zwo6K*exy7zE^Y*-G3toOge-&53J-DgrYYF~Rrt4)v7cXw2OY_vP+JQF zTS*xnF}bH_<;3xC&#W81{V)rtxtO_L7+rf&C(vxK-1&ELVd-ZRo{kg2>E)U}%H&`s zC3W@T&B25eHZ{!fU$1a)Pr*aI=W8)cf)PjS{H0uV7A;erE=Ccrcr=siXk~ljuN@ix zAZMke`=`G}m07`Ff23vLWl0m!+Aw`?A2kx5YOCk9&Ptih#%z8iTZOADqK8Z0@m$7R zq-qeUpx&u|B@fpl^powFDU?8i6yH_A+pJeH$$na%?PlkV4-7XOH&_!nI2h7)>|-UE zl)2_UsxP!>wt1dL4)U(8{5cikXkEFr`7+NtqillvU2KjSk6_Oj$(NqH#8ZqsoBL7{ zhDmwpT&nVW-ggj!DdDKsQaFBzW3lv61$j|&3MDJCpzYw{=&qQsee-u4nvKv@G(t zwAR8k`~G{;EQwjOllO)y?6y4&m4ZE^=whU(QpT?v(WMIsm?=0!I|yh*8k2EItdzF$ zaKAh4#9t}s5$Q*?nkqCMM+l@acYH4_Uydo$G9%zn{t|CwbF|L=HprLoJ)4aLlZrE) z8(x};@-==An<`7JXWgj~&um0tf{$age1I=S-;~)om?lt}!GUlfE|Gfyd;rQ67-hE$ zA1T0UZ_Ks5wxva)?zl6UxR0HqH)Dw}FLcD`z-WkN9iVP{Idbs{c~vB*V4E!gRb0?H zT3_|)mY}ZNF=)iyHMoguULEx@0mzG-G}I9omnTZ_`db9ssuhFoR#W2-w{P){=s3|u zo->6ct}tFDu~P?GV=8uW9!@jh!Hd+GMNZ5%UYeq@xe8 z#=P$-A&Cucd}!nmL8r+%J2@nRSNu&_xI!M{9FQ{$oqI+EUH5}paBvHTicfo>QKs7! z9}=F3RR7+3Jvm$%<@NyD-H3A_hFtEkL+;(bUgN1$3S7Pf&23i?&&TDIkjQHVhapHM zU=!lJM+9ZR#}6x#eGGA`#%LvRDqVWIW0tGEPw%~vg+6UUVOy6zap|}eWUSM01A?r4 zpk%3}mC?$lkct*L6z%5RD`$gS@7oAngdpG3^|pmkfyrHSc4+Fbu>XC{ma!IUUsQ4u z${PhUQfKI8{WxNnihB(PvtVc(FrJwi=9eZGNYjHkj@Pf@W*aTnvSoooFtMPb+;7Xm z5{nL@n9gouBUbtwN}D)*eudVDGe~WGQ(JO&|<@~dRLXAJni@+ zz)!P~cgEO7aSqZ%YN;#lrnuRWt26^p1Zraw5 zVV%csJ7wM@ZO*cWqG9AAXc&Y67i{zpOkVZ^D{Px7v!ZYU8tXf4&e5(XAo}Vanl;Q@ zX7Z_0ESTBl-dPh!>;$j5?=T8g{Q_4l=HTXBS{R8huvu1AJHME^i>~-EuN#gmKW#SX z1g;Gc3DQbXZ0n|#r=+29k%X!rLgk1?o~_d3TAW2CuMshj6D;+%W!IEv*BD`mrDgF; zTdY;oO}SSuXcs_{!Rs25K)b|piV`|bgxU-VDqaF90aLw@Q!wtM=N4kE@Hy`$qD8`$ zpoN`$cS6KpiCPtdk&!^09(KIq2}>&~%2=R*q8>3aa;YLOZTo-owB|kpD(@tZe;`d+ zGLdz+pb3h99_mvO52>^uBH7oFN_&=DSo;r|A9Wg{2P7Ls({-F?TF@{j#9IqdR>I=6>t4&1TCQshaBryw@ARE+|PieXR^Zy+% z3UdO6aNs!({XIV~L%VSnMrVPK(t!XOa{`ujVbr}7ATSTA-X)q{p!JS$=@;!v3$BnOSsutIg*@kPqYZSr*><`+@RCrE`7@!<(XaK8vwYVH9Rj8zLh z0J7{sBB*FKPWRtA7_XCwB0Y0?<}RvfCAA*Sq#e~|gue|+A5t6uS8Nrb`ImCt3vs;n z+h~NMJ2(jZuFfvBmj2-j{IPg-!wOKz`r+qQ!^q7qGPlF{>p}Wux`FzN$4O}Oj@??` z3y1nS3Ce!LC+<+%Yr52LN1(y^!Xs+Y{@ZpldllXTnN^nC5dg{W0Sy4iI0yBEuDs6N)i6mqlr8gU6?Av*ay45F6azO$~yoSR3 zCNd*%;z|{`p|zD3{-;lF#QbU)8e);JQ@-gXg2&k@9Fb-Ez6SX0ve81)##5|T{g;Ox z`f~QE`$}$fNgXf|3V`wqp&lH#gMcV8RuKImM!oLY%sx*^9=N;k%lRV7sRA1YQL9Si zCSq-$z;}aQyS;u~?P)L_%dXTY{nIN+{w;3EnhWwIJdLgT-oWq0j0oG&(4IS#6du%q z0!G1@;ssdn1mFe0TmXe5FnxG=xy`1c`(_GIZQp)>ng3saK8}@3V3-6kmOxzcZaAIO zLM|S}bI?l;c~cQ<>NGsWdq5K9#tL=xAym{`4Gc;tAz?&V#C2V#&gx$+BayUU4?%|( z$z|lofdkaE`}aX}HKfvkN8^=0aq8$8$`adTq<(IT_K?+9*4o9 ze|mOoC4aU8lZDqG@NODrhX}u^DQGTQfML)E0mhEpGzmb2zh1ebD`D2kcJW zR?GP&%I2wiGIaMVkA|xs7{`Zs(iD9&V9dR381GKdkSOJts~b^Wg_g@F4!gx2^v;M~ z$|;Jla;U;7ztNASxxUK^RovR>7lpIk1{g1Z8N2{%FuD=O(DSOy!RR+%0Y;d(z(_Z4 zOd5-MTF-~6-5c!;Py4TWap1CgS3rc0L1)O8v)K$lWjn4O+XmR`xL#rYOar6o63l&# zE-=1v#~9XA^yBbn)c`}0I{!lbC!q48eL-u2V_%*#W`k9e!ZSG!fy|fn;h%4QuCm^*t#8IgnU}Ul2SGCO5 z?uo95Y8i$Y9BAOTIyj4S*Z`z-@5vyOQn1z9Soa!;#~$p!XiGl=m0sSXmymghC{&b* zctHJ59E$8cL>spAgh_mbRFb~nweJINC07FDmpN=~6*MaAufXJ+gtD=GP-Ayx?mG>% ze@LT3K33;;fRpyZgL?U2LQIePWcW^9=oeUaYJda7qU!1|UMyff0aT}etE~~BPQbw- zh&Hc1rqcBiJlKtkx-;ni6UNkj(}^>UOOvk!AKSCiE+?_E_C}h4?jW=A`*UD@Szb*c zrIi3fRq;IY_%Aq+hS6z{fQKJwj&u!&ZDUv)1Zmw$IhS}DSB9A)v3`=C?oCHdB za_f`yEOMuCd!q{tw>r4JOS-^*Xmz-zZ;q!;;D-pa_;$Gz@(GCdiP9Nq!AWEXC6jm$ zVq1T&f*4XfPV*d)r07SO$8CMFE_R()iRFE&7GHKfLdB05>Iwo$A;)Nv<024q*9?+a z&%ZxW50Yfh)`c$u+HokkG&k4FRqN31Yy0TBpLJjMZ(qOfOw2ivB=$$xnC&m*vS-X> zQhBrw0&{R^#jXFEpYRC8XO?MSj(mej@bMV*B&t1Pfs=0VqlqPysTM%PzuigDQFYo( zAMi*%^)`)0By_9@ z?TDMuewYG?ZIM7{9j{DNUVwrD#bS_grxcnGAcg2Z z8_jHi$2M47bxeSx%IL;vv#w_2Jr=sKcV3r|zZaS`(fkr!o|P1yal6vVg=sQYtP8C9 z9sfTRojwNPrZkzErl7gC=x`S-jpNYsb^ygP3y3SvfdRD%L+hOz4nC z*AVjTVSv{EEl=@Tw@enuu5PsbT>s;afE*}uw#s6`qPxll&;#}L-kvlX?*}W!4pYk& zKc}aAR=EBe}xBV`vLdc2yPI(?Gz+9yHgrvOC&?p>`|JSJ4;Xd9B$B z5I6!t6EmNQ{y(bTIxed2iyj^jkZx%mx*HTwWDx0)mhNr=15g;cySqaaX{3~q?iN7- zX%Oil9AKF9&h!1f@p=CmX1I6e?z7k4=bU}_S{JQd6Nm(DWxt5$03g461QT6#wdj)*h-wq0%3Qpfx$*%@55= zVGwnd6X9`ePVlj-lwyZ<*9)`GlD?J+B$}dfRfdk|<0u2jW;4hJ!(Q zh+f?l>>HK|?6wM1tqBG0X>*_@bh*;NEw#EI`Da<(;JOxNN71LujBtr4; zUGprm)9SxqaZIueEpomEoFBY;;nA`M)*$N!4WqvrRU(Ot?6Sb#TetY>?WX{K5|<~{ ziY=FJ4;*0Yo!ceXRupeqoBm}dU|@rDvo~m1Uo=S*t2#bh&61HF&=dU4?GFjRN-gTL2zpjL8A;6y_G znBC|rTrgpDQskc8X2P+?9x!~Pv$kS88j8h$Hsh#$Y0rH_9&5Aut(vZ>1uu#eoXf1&LxO*(VA%E`|KM}7{?ti9qo<)6UURz(Z}@1>lz}n0rN0(IrQ3^{!0(|`LTi%Is#-f z{~bNx+?*wOh}yDBN4)$8^@5|9MG%RIQBmWa zA6sDnxpma_>|F<95}?J8hv7gLdAT)ucvSRmgTm{#M^HKpezU*Ri5}${;W(7r);96; z3s9=Jc#pge)dhW zj({FT@cxH%RDN3Kgj#!G+_0vOxsl@kPGq<3xRW8FG=E3NsrskJ&v%NdRHItzi5isH zpT$Iy>xApeHT-;qKjGrC+nnykqh)M8bdh{S!#?j=YCc!gc~oq zajw4FGURJf)5U=fsMzVa^O?jl>`m`c*FNDk0{-hb;MJS!(XO}Qq1W7(SM~p(u$$b` za2E__`5IZxW0OL>JUOy0tTN#c=2yxNZ~B_e@a5Lg;pgsCR9GuwZ~1y~b$I3Gq@ep4 z6){@mc~}dRL!*+x=gX5luM2nYvl_3re-t>i+;N-l+)fKv`_c~Vx{i=>?O53po1?FA z)a6?5_VRhu0rSoAbtfzX1Yz!DRZ>=#FzHq{&nkJVug?NMANdW}9ZCIz24PO1wIumx zi|DUX2+YCY$3X;oZ!2#Rek}sd_TZS)?GE%~_ywjHv9mBlO!kd?YJn$qI=^awZof|MXPTdO9Z&rq!FhIf* z6h!V`p2Bv1ud{-U%PZLEAYyYA)7$Tvbh+#`Vz9T4K+jUppXhn^%$?7X>9uthpIuaHZ zCNMib3d&m?S;DbOKV4f}M+PjmXK61jEz=j;j=-oM#=+luzd5~B3FNI??NkY!8($oy zz-rxiVCH)V-@vbP0$$ymEN>LSua})>I3CL)SHQ(6oE&lNdFpwJs5`U-=w*8Htl<4oCRkd^{`?hM8kFHZm3@g97poS}~mnxZm4-qjJ1$L-PY&_d-|05XdE>o^rIoMOUP)NRAF>uB>xO>?2odiq&)F@%%bTs2A}Tvi-k1?^a#|Gy=A5<^n#|1%{m+Z%NDwzYE%fk?~6XL;U>>Bkn|R%y%}N z6DM||r1JK(=P}CAx?+sDH2ilV4u}y&7&KZPv-bv$U2Th#PAT6ct$hPzM3WdVEb?8iyuRKZ<@HF< zveKP=sw+aQv|j4hWLy5!-yj&rp%Put`SO1_q_?4dRelMe zUH_x|W+B&_MebNWA2;;R;YL?+@Z**AxXkCk?q8RerVdRo*PmB#he!tD5nc3O8?%dh zv2W{nEx%v|dS*W!WLZ8I*7~wH`&k`{`ldw`YLql*ILTpWB>TKI>B}YR;^5<_NR_(! zXtsA(nTvdm2c z9lJ|g%((WPTf6r7v-#^$(%xoD9`ae!1~Rt!z`3EwZ!#|Vnz01D`08>y2s{aXXY&Wy zUe}L$(LYpUi380_V?bMvFftvZu9v2 zxiJs>$!n;_OGpj`wZcpu>M*_xn0ZNsnMr9Fr;UG2ecyEI|5XwSR-3z$T(e_B(#R^_ zC;v;~VZZxFprXV=o%xmPZ^6eTn>bT$S)wYL6J(kkwfEoT@}yVzrlfu(|2;l)!+t)x z`ld5O(%O4E?uVVHvozz^-i_%;tPaqCsMSxEyWabL?!0#V2+*c8A;x-O`*4MG>JRbh zQ)+0$+Xk9{BTgejcz1dK812Z~6SA++9-iX?6MLNU7%)_Vk@$brK0?bmqYQ&4^ay%s zPR(I+Jlo~fhFhJc&mX8{Xcx5eY&WgKQm?+2B^C*|{>6DyXz;RKy+S`%fSBhg`k_(0 zn#WzV3eCq1q8Xb>^#?5D>Uv4r?`9vUUvKgs(6p+=n=%k}VYRT3Ec!nCcfU!NGTQD& z<(|}x%lMP~Ke>%53-fU63YylZA=(&PE8Sf0$kq%h$jN$D`f9akUG=uYPF=h%Pn z;W=2sjWuqO8$xrN6kpkTUgFw3R#W-*`lW_Rx+eu)1BaE6$C8q45YsKSQR7!3-L^T| zQOXA?H5zN_%5jU7>KCt?d2?Uhf$$=vLQ={pAnV;Pgj>u%Wk>4#J`NE2wECAc!h)G+ zxRcRY_BN*JamBzoEL9+4dm!f3%tE>PLQYvK0TV&+SgL?7MLgR_xAVx9-ihzB+%ty9 zP)%J{a9ntk=1duR0{uYG+CaCkloCr(G56EDQiJu^|=`m;um`mPyH$i*?#JPy!MVA)|e|1n3fWI=g4y~*7a zf`fxr6%9MvXH$Pnrr(7r#oogtg|?13Wk&(KxPuHRc`pU?(d;^)yfZL)Xz6bpgJowq zW?!5BFkS=iO>Y4)ccImeW9a!Rti6TIGBbQajN3?%_nQJ5%KH=q$m-`!{#XUP`o{-C z1f!q!jEO0^NIui7khW11gtyBD4?Si`uGnSJaZXwMPP5d`=Y!ws%gS;2IiJIDutKz$ z<=3y$&ucMH&RHuimbB&==1U>?z2#UrhfwJh+%w4;`rhDhG=mO#cMh+-jTkW{(|ta9 zP9ItV+B6ZBu+YQ`2p93A*=z-z*gb1`NW6WZ>%&aY#W`9%tmSKzd;PJz|~gXE==k_RN=`EyetK3p$kDe4XC>oMcl~(fhqQ>3*H3kV z2TEZwY_8R(Z}{Zb7E2(HyWv09K=M5#w(+c? z*|A{eTeo+AS;x9T17AGRylwlP@DKf2Q)#Ax^nI!}UJbIJ`a$NPBIi-A)=r69AO0n*`p)E!CNLKUhU8`J|5AMlCP$Jms$tgb)jKz# zmKxVWq5}#!`)r_|^v%aL!a~>^hBP;u>A`8vi&VJiIL%x=<#F0uW{*_3WFhTkRO%t` z^K_O30UTH~JKGtlW&G0!G>vvB+RknW)}EDn3q=%%1H;d+;=WV~yNi$JJkz4}Es~%^ zv5i8%s9<8;QsB11LtlS+-9b`t*W7|7(eyH14o;<&3spOn4|Q=`NJjhLLsAGTW2S#R zZ!cuJyv^X_`g@U|LLdc>EGdk&w5B*K>zzu%ohvEI6jVI+=HzU+eKoxq5fr`61~h|% zf^9oDRmbmtP%MxxhO$Hkz1R6*KBXNi`P`g0%7=gWP~cjHqoxm&;w;!+Bpn`Pn>xH- z6j@_)HpHY(&(i$46tZqcCCGSpduaA^va%D=V!4)1nzn%UP7>iG%?Aw-w+1+GD5ddDO@o$WpI1&(t5t&5Tq!pf%f#CC+1)-Q_DQ)dKDeC193P~wGI$<2_|j(>9$ zm@-8}WlsO=J#IRI6PAdg1OC4Ia15{C0yK7Z)k{NLpcl(IVG{u=jx%;@aVK!nJPhoK z_88!KAxj2|*u~@nN#b<*F|ZkV2}5Ra6GFLIyk>e^+Nc zQc(I!6HLP#8O%fwUR~k-C)Z!JeF4VjUved_k^3V8Z#7mcPx2>hw|Sa0t{38An=9g|U4{Guuy66^qXAH<}z(}ruVyPu2$uZWm^+kcK?RB*h!k;ef>hJq4th`N@MqMdyh_)sa_NvR- z3{f!C#q!YnVN>@97sCw7S!ZVPo|s7%@kpGvi#>FJ$H;^nqy2GHb?JgxaCP#S=}s2e zYbfC9Z$K9@aN;d*08aDKo7-G>Vp{GUN4;pYeIBMsQByd?&6#zb^fraquW)D^tN)-g zd$W^HXGx+h(Vu;B`Vu?;a_Ep!UZY^5GU+Y^CsK4TN?@GZbnvqFzLm;i7@ja)vP&91 zZIV4s3_W1xpt$jI2YFVn$bFTk+b(ic9%Hd65Gvi^HlT)}Ac4Rl^b_*}mYV zvzV%#f^X|X6xs^A$>oIl=p zdTbC~_YnC%)FB$=6N`mN`5A69uudHw8re4i&gl+gAizlIh^)CVk3 zfBfsx!9s-+#N?3?si~N8Lp}h6xItW+f>tJKe>n~5> z`7_Z$BtOLt>7Q8Fh3H_Zq1gj8!MyY*%{zc&jNoRHdy5U=zp15Ef2rm?@@rk z00k=v0hDH=8xogoQ^HoO=<^_#nn_drp0?r&-zS2WZ>h76O$C=k*0sh>Dc ziw~>aq%5g%yauCuj7pldV^?XKSWl;}Jcch_c~RvBJ-;^>D=BDxkCVr6N1Zfj3qr;H zi|v`Ss<#kD@l0;+nRjra=#{%FiBc6Nufs2Q_>=pPEX8eJ;elEA9+=MRPyLbEn2ibN zhD{gCxLb;xF>e)q63zq^w#cD)Vy|T6Bc&-4+gWZ$f6u)qpPzUN;U{U7qbQ~w@C_z@ z>XLOU+|3_Cq+BLPu2XMAp8S0cx>v7MIlqa_6phMp#QAjT|CQ@ysD0*}*8HdX;sQM` z-oeRQ)?TgV^GFE4?MM{6Nqg?Sbr$+KXlMt%j;nRfm>f!FsEP6Y^heS{M`{Tc80RF9 zFIKR}c-~ZKQe^yFN@bK(b@~3=PU|akOwwyeY5MEwBJ}gf3m>q7_LHP zBe6{1Rl#Tqe0F7RlLWfqPqD=3=BT9f7M5LKO`?(>tQO~ulslH#n^VW2M=@QL|4ZSAckZB(ZFP&! z-=B;ynRcu>SNi0HPoGP~=a=5tbNYet z*#6)eMYkz0gDy_^nD2-${?HUA6EaE_zJmuRTUP8o0vOvW;oG^df7Q(sV z+q?o9`o;bt6{`Tn5JmwZ7XJa^P39kXe2djM4y0*@VI|!p$AE6~L%^dmYQm1PZwd{_ z=lb>~Zi{bhL$DjW_-GR|*(BEpnuiGeu>p!ssn0CF$>8Sk9RK>VUzDj~_P56+MUpY@Ucd15 z?fFK)W}x;se#7U7NGZvoJ%F@-*!|QSPyeq(I!c9kICK;0?v76v*0!MoeOcSVpTq-< zJrNf}uoGrI_@i&|=SUC}h5o7wn!k-161|R+{?T1TYubOi zgEBzjY(GN-*$5Av=*Fx?xc84*`nywcpOw4+x>vyE(~20x3MnnX4pcan!jq72NVZ_;9yj6RCQt2DxqAzP9V;;-V~a|KA+(QbqW zcw!ubu`70F!nQ+Nr@8G$Du+{JXH;pdPH&xV9;wkX$1bc{CpWSEnd=BsC5?n@8hS8> z0fOz&{Idb3HcbP_Zx#PZgNFqNolB|8Qk+C(;snRGYmXw>cx0v8L1C|Zhjpc{{&cumn<`;ms4=2b@PrJ23FDyY(5x0-TZj^b4q_l<^0E`Y#fUQ z3oP0D8t7<%glC#Yga^Rs$f9%h!LP{an-98ZBdJ8U_&c7Qd(QCY$ltD$4S8b1i8uw9dcHJoZ`Ciq2x zx%eZ)fZPAseSrz%2KUI0=)(N9o)g$Rnc)b!q11?!;YGc^!W=6QRek;)lyI*H$dM$M zs_RfaS|Y91iU2wZyjo;H*V%9jUeCXYZMPJr#`HQ*OKWv!mPro^kN`GGsb~7XYo*6o zwQXh5Dqe!@2oNm!cCQz{$+86xH;=_vtq52V=0Qj4<}zdK6j;6NaZ2$|iE|p$ zd?SIg{gTM~7915RD4!dArm1Lp36*?=j@qkH z8=QG}@j15lRJ7vkc! zm~)jz+aJ(pJLHg=7efl^QjDJHs}jQk6Vt5MJ~jcaWeV4`&c)sn1mc^0SVGzW%< z!GQCYROv*2mpn3rvMDeHV80>|u7@y;UVg#{Sb!mvl zJGBsxj{>s;&Yn3BMm$X*$!eaBjiTfJxWNo_OcewWm!QQ_;QWa=4<2nv97!lfFn=4lL^??KVvbn^fONcgi*2g8MtVWR1P+sBKU-TkcjCo zw$Dp-#>Ci_#KY(lWF8oP&ZOaKkjG1|cG6|V<*gGJ)YPJjhTy(~N#L+x%QGrWr~Z=< zjBY(WSo+(@Tz!NzRwovR)zC`Z9=rESVc zL{`hy{~m8}mDpDYhhNS;xXs;!tPzr2oLt&Gg?F`z+;D8AS|KTCzZz!gSQSXjg0q+G ziQ8-|;B7taRPu^3c|U_=GcesulX2xZ8XT4DRPyQ=Gk5uto54M}tJIgr@Ky3iOuzxG zen<1!FL)bk#f{V5qU+#8Ie(SH4kkVpLGS7j%i<}U zB_aICe927DZx0>_P-Gr|c;mN89$)%&pY7}IP_gyfq)>-hVZQV90_0iwc56r8%sbCOWRqU*E=L@7-d>{p|6r z!wPzMeWT3*g1<&P;gjF;LkJY}zl}5q*3`Nf{BmGP!f<@JW8?Ks;$e{u;==D&2-C$% z;bj&c$Ks`s|H9PAk_Q0@)1`TvYrI;NWf{qfTLO;Fg$h2kVl*S)3v6C2UgM&j$`&Qv z;=4Q&<{zE4ck7P5I{Zap4to9(Y_zJp^@%w`4rKR}Z|&yXet(Zc5U=+mf3>tgvji8D*?lGs(%#$IV*FKa z%4x5wwh(-;U;1|h%U)_Xs$CX|TaJJV@Glk#j45)P*}YO7H-U`|xa@Y!7cWNKXn2}?>S9%r=IW08I<}6#$TpftpsP*7q+6MO5{^$ zwh+6Ov7@-@IpIjYpIqX|IJ?Tm(>VPCv!yN_`L4cZ^Sfic@uazBX7gk*-nWD{)K1K5neT2ShrNB4=*{pcQJSisq=j)6e7ObY3Y3NNs!hK1hQDVzR61*J#D;*uhyUu$bBUP5m#UrhP(U(C&`L=&1a0;*kT}#Q_ zf`%=73FVI?fOy@?vqCa$9ubThbP}Z_jZv5bFI<111%FbY?$Bzx3*x0Qr`~8<1_{r8 ze<{t;RLm)8cCn!#!qy;Gr?t72!*{(~>R>vTsz8T|RiHDl98z#KxlSO4E7Gji43df~ zrF*KPdee0@9ZX%Hh(Bs!Rgiw9qbHv6CN~a$^*!$N{)^9wYu`id8FS;$d@_GlG2*?m zAeo)ZQe~eGsh47ECoNq^ku_GW9@MT@B_@bwhqY&B*&-UKuw@ zjedRE)ga`76PW#0S@jdkG==pyYBwdzqA&0dgjkpp zhmQBVJ*T|xcWZ@BAD_`WtjeIv7+QGao8dLuhs0y>_M2BCgEbq22ZW1Sid= zthD<9i$#fVBFGj1L*ZwTaSqkE^m0HJ4d*V)vI%=~x&`ye+k{cJac&b`Lw_fuqw1&Pi<{FO1fOEc{E^VnQ6NB4;Mix7B3YeJ zrgXX zsJZ38h|v_|m6pt+$R94^sWv`7x9P?o{V4pL_b@z8fm?n!E;d*}WDqZ!UKD2?`<~A? zu}QVTzbYHz9eInFKi7iVzT96)o>Mw0L+ZW*QC~j$YB^d(iW*1>ffI3{!3RF^Vzk+n zL~}K0oX{|DOSF}xj#`STAHIc{qOK9CwKCr?^F9Ny%cP;Djv3Vz{_7(7)b;S2B=-@F zlWd%hE81$nWjo$+9^Ne&r8ZESjUF^)yDkzI1l3#45`E~Z9BUer7Q7SfHS}pd4d!-@ zeTpwx@qMpXE|u{Y8JvQ_eov{3!`rRdwqpU!{ZQX`@>CNag+KYT5!FLR-35c&-s7oe zrtHoSta^;#s=8ghHi6)ma_W9I7)db9`myVv(X!4+C!g4~1uOB{gt3v}H|dpNlY=VV zD;y;snYoH#+_{PzUOfw@GV@oF!q&K);!iOepBS<0CFk$7r@4@fVOd%Vi2c<7az&^C zWL#RQUwYj~LLaEzR{L-x!eOVpdX&ip_zJQ>1?9c%_mF#z_haG)08vLi3Sl@0!YfAW zfSOkZIx2xEhkGAxtA7DwBT5dXy__sB&v})5%W5-zcC32j)KQod=<4xeDDEVJvX?R! zu&xob2;L3>g!&~1jt=e#C7!lQy-IrutuTvG@25Zm6JG)eOU8yth2IchEc z_Iw?WS`zNyX{C|ThS068vwek^zOEA6A|g@uN9xS*Hn#B#opopNaa*c~ctr+diGmjx z0(Qu76klZ9jgWmf`N9^Q;OPYUNPg>=W`if<;LW24!#vk-9o4l)>lLI(hn*Qg-lq{eI+jse~NYv(~Nmf$A6d68=$-grfrsPaDks-EG@# znVh(t2q)K;x*Ip-3U<*|zO?zS6ceo(7+Ff0+dCdmWoL78o3m*S?ass%>3_Ms8g&Tx zVf*7jw-!jG>T7cb_sC~xdQ%DTMF(hA1roc ziuk+jzUb}xLBoK*P+2JRlogLep{*z94x0)OpIrf=h6#r)i(C5lVJ+=?Q*JIltq&-d z9DC)lhsm3{12WW|YOIERo~^Qe4I2Lh%gYMRfR190a<0kINzgjCKb+41r z$#`GcMlSHp_ZIIzT5|iXc~Ad|%fUVwxW;)y4MvUQ#F(NpyBm39Ud7%ghS2zBZmM7P zUK#dL|BPX^hTy5Wh+FdFY06T~I$4S1n!&&j0kFdbgWG#M(5660e4rPHQ);qcaX2wS}O`)`#FrMV)g&AkXcA|;c)dj zwbVi9{;AB*SdU|KJgsH*0B-55>a+NhPUT5FdLo1Sl}-wEQH40UhVkOF4LWZU3)NN$ zUywi6$NQK^CE4x9nN-a7$(M);LG;8fhgPdzGuBaXN;aufEx<08yJdQ5OII=y$;YHs z=B#KP@q$}v7U0HKs)v4-45f1wl#(By&EzLLmxwuHVhrPxNWT!)L%vED<7#J*b=F6i zPrcG>I7ZnECaRWMh+28t4{6Rgpr4zNZ;jCP`DtxU3F-ToZ?KHuC%o@#a?Vm8vkCQK z%asxR>JcdS_FZea1mmZJzy&8s1JAYY60+v6F_D#Mq~{ zVxr6#dv;v4`5YT1hhDh#ERNf@oiYXfW?RVh z5JycWC4ON3rQ99_Jf&~UN2Wu7{4mS92UAg9*b&MCCEB*vqr@#SgkPUHlei%%+VWwK-kUTUNtPU-;RIF}3i z9HTC=@q5kG0rfyzwVA_6Ax-uP>~f4Qvi#Y<>LTkWMx6QH)$$*v;$7{0Dc?M4a;6r; zF`bwbOl9`>;-tC|9B1dZCFO|Yr^6rC5sc04@sCw{(s(D1CQz`_c+;=PAVay6L0vkv zd|J5MrQab0Q_db^K|#Xn0n!k)SnOuUJw^4(3#R4qnL5 zy^F9U6u8-;uW)YtZoti#%a$O*z>Xp5>HUUGVMnL6L}JNFob1eFbI@a&gp z(d0K?3{(Upo#~WLALw{oaGeIFPq80A^Rdg{rT!lKKDr`)(`>}=Lj|cKEJ0nv#D&j{ zU2QQx?kk^F<~^FqLl(A;+{#hM{7?P;_gIT=5z(0H@Es}ZFl4G~^Co8<)(@#A(0dyT z78}yWx!loOAK6c?cXx5`2y0Fj4cJH)O-K=5ug#{X=63EOk(uEauZ}>?zg@^WjFtMq z?-_6D{mk&;*O@okS-cF~yB&S!+1<^w`L}p#xCV@u6IdPjX?xqBjGaY38T097vyojS zb##5QJs_GU8=CRQuA9M8kT^VCH=VgxQ?Y|Fo)dW1Vt5ccK^z zw%*R4I&3N_;=hcPo+KuyG59c%%qIA%a-u$-M&MQCnP+*5t$LLs<>uL8MT(w!#YfTz z)7y6??Bz8)SPATz{pl4&ZF(YgJ)b9<|H{y6SMd$>d!2vivP$mepr4ro&L%|09A{In}3!FV-aLP;+0L zLt)FYVDN8b!>Bx`e^*!XPRBS&mw2mx26IQeZy+g+5ZxTTYh!-Rsr0g2A@zvFc|^tX zM+5rbDS}W8LH`oM$|JoL@8&NaWQVls3!Ked3JHLr#`$C6TFq>^{n1AY?L4~Mpjl#e|~v1 zIvV>34K&w$sZ2Q&`TOe1Y8=M_iS|b}meH!2RzuV)3l3p|931RyQk32Hud}np&VLrS zXCS^)vu9BWuQ#!s&Z;z=o$1C7?Z-!dz@{K3wz2PSzpp;GEM2WMTpi)0EcLz(!MqmX zT)3vvTV|&E`fGL6+U0zOtS&0`d_^mT(&Pu+YH+7!@tlHX;rm+Fr>5FPuY9QVp`m{L zr{DcQLwpG!k866pzy1Pb69K)e17BMY9XfZ(J8r1H96epEhg{Jmr`_7Y#Hmc|?IFG< za@-Z7y6V+^OA(?hQx}SRyxc_t+{Z`l6t#nP!tYRYkp$)O-%hN^xd>fj&A(k{MJ`JA zEc{8UhY#=7=&s}Oy4!@7>Lmr-!Q9mOrXiit}0Yl z9o47c#@kx0y%S!bMkMpC?(-Kd^zS}_+@1%|6!@S0mNN9n4_8GmzpvB^Z~YYJ6w^AN z2&(#)l(@PtFJV1|A}8=+aAzI3PZv{x!F0h(#9S{hPhCYHYwHpLE;M}vT;JIZ4}@kS zS4LLhPt-)UF}IZc^*}Ga@ERf=WZF6>aF7U`g(x!I$<9kB49oS|WiM&TpP(bu;%m>r zEfLFcR14!y_~x!VWvV%=*v~Ee>tF>*n9W>U&bqaZxTmMTC@*yu8$~Yig$c5_ znoNp|f*@mU09icjWwL%Uw+`=q;0AhY`T!R&N(QsBipV121GqpKxc>{NqeRvayl5Iq zh{~}@weT9e{TRV`ZB-5Uwh&yufasR#U&(8z*>_BptMn`;O1JPP>2Lo&oYQeLPkh5$ zwilz4O4oIEd#4!?2__FU0`F805XL=w0sCO>Kz#wzT|a2q013YUFW>)0su`#;4#V@) z(JM@=@U063HAVv^LI}7^1(1NY0_^-Vac7rZbqTl+=fF7?mm83s?>tW$uVy z&IPO*ReAxFT*M4IE`nw~-dRl57W)NEQK|Aq+kO#PCsb`dA$WwF`eF$ar6Jp8imuCa z!kG181~o8sn;>=sPONd~C*TymMBM2Hq1yCkH2xvqA<~%^x z0EpDESGR?z_zBw9^OKuVu&(zvNvF9ZjliaW8K`Vwugj9AX&?SU+36?fsd8KZJ^zh_ zSYq3Mhp>qsz>O;wfV2a)66?VLM8?sr(Kkt%Co5P#$YPxKLBGi`_;e4mlZHnh48*}x zA`r1wL^8&4UBr6;K86A;%FM5!sn|`W`CmA8XWDWBb1BuVI)KMow}3#R<{5!e51a_#-@w`{H&8Ik0U*qXk)i=c+wlK`hI%}lcp(YE z!*jo|07$?v+QkuAF;nGT_iJc@4InAX^#Elg3qVr&;UnOLb%;(p_F6Sv+0?IBB2R$h zDLfPN9|mgn)bTrJIOE5)}@Pv8e;#zn}`M*O$YD?QqIaKs0A;Y!l4Rq%Iw1Y)vvS@Ta+EH zCR^4)0`}Wa;aFRzui*K^;$R1hyMq54ckjWIZ;eWrq7G~vl7c+!{{N~-4`YUfxD6ZU zw#x;q@%;@2qm5`UCJMO$2u%(G zRx73dr3vurMZudcVWX&{R3I<}Y`9(e0GrBdoQgnE4!_pLuLG0{_?y z_{h?uDz|`rz%;r@{38ZBQ46x7De?y~w`U9PV#PbQLHd)`Rrg!07Vj}dVgio%5b9hH z2rb_wtU|O7Dg|kzPO75=Ub4XHe?Fc@K|c(fz&mO{7eC6%4Kw+_koyaUUl;(T(iu#v zIch}*Guep2>fZk6+dHvq&FG?+C+H%dr{@6WeqHoo+~<Uk7B^ao#{+gRB_v45lkx!7Z`IL;)z_~!HILgWiZS}tlTnmH; zu+`slm_{TR@B6PRh>fI}6SyjgKB)~>jdptd1TOdpBQlRsIm2SmHS;EEK0TiiREh7y z*TQ_U4OgjmfJ#MX&I$bCIA$>ZS>6Al+3Nvt+67ifIQ|xlV2~>n7?=PXvSY**y6AuN znkXiT_rV0v`vsog27B>X!3zYo8rgCQYe<$B0#)nprctmAEXeuDTuT;+|AKLT0xuPN z_F2r}rNRlE)`1(+kIid=?4A* z{DmFX;K>Q#Qk3fgHb^!BEdxn<4oGvr;w-!TCkTJ;aD&j=0OQ;LkArV0G(K@$CS_u* z+=BnV=)Hu?Y^B_2XC5Q4V_$YtR|AZ&2&e^7(e~I#3Z-uWDT3F~@kGF3uX|lI_8#zH zLn$5W-|tv+EB`8G-e_-c0(%$iQ}9yQZGb-4Ju9LAnTw5}OGN4AO2)R`CDtHpZkW#n zP5_`!CH# zi2nsPsV+ccN!Sz2{yYbi!xJE8775p*B&5DT%s^l6qTqE$_b0v%^moau{;$y1l5{*d zJ~%W0!h5%${Q=qX4(NZTa#pP@PMy1#2|=As&$=%{nr^1YddZT%Y&_e+YJ2{HKZ@9p_sH-#liO2|N8v!67LeC3^KI zrOIM3>gfWDWx&mi@8p4u<;Si21YH}ibDz2S`S0SnbHD7gxUU~|Z|`35?neP+c_5 z@3<$ivQx5d2Nu6&yEbF?!?YQlmi>UfCI{DnNYmxy&R#(0_!hAO>TKT41~4|)eR=B1 zYl(C$d$dnV?u{&US-RWf5Q_F_^R}I*gdi5ZuBQJ6E)!ECUw@mqOn*{E95%O_!3FI9-U*jWDiX#`iZV#=<)m?DDYlkV)6qus6>R zP>yFzwk&<#wNyI{0xXa1?Su1;tZ*cxd~>ihHTJyi5Wb4EJu0We;*67sRPi6ysr?dr z@~Li2JEvnFCU{iibM$H0Yi?!Ua>b-`)Iv@dbz%#(nM|T9ah?P*C*(|1yy-`hqf`F{ z<`py^I@c}H4Y7C^RTXmUNR>Tqw%p_;U)Tc&h*)xknrNg36>IN5D&D-hv~RL*?i$mC z*W!VX)Mmy15<{oqEhx!p24e&F;7)C6joyvIbPxe*x}5 z5x|+tS#TY9>wl!q+Qw zEl933we>4<$rV;NAy-P)R6e=F8W$i~iAKdK7Vs-7gJNk`(>#-6O?NO!u_pUigkn)a zlqf*4NJ1}|O0f{bX%veQF`GAA z!IX?O(Z@nE76))FK(VOErC5rj%UB@AiA;(m{{<9_l9FQGlw#cq8y6MnHZFGO#4SNE zhJ37?8|rwyEI<3c*k#$~M79A(Xum2R@L$4zQTR8OwsHMWUc&#X;eSp^aQ^&H6pJ$dQz`$gHvf|~ zF{jmkQF5{RFRI2e@;~A1`Y%$^Me{$=jQTHXcK#>8m09_p2-S<_f6|%tUvN(S7d@T- zDKHKv@;|W>kFN*+r`CaAgZ+=-691R{Uorci3DOzYe<|g^mBarU?0+Tvm+)T@{%v4e zB>!Pr!vAXEf35aEQR;t6`EP0VzfmM-iv16(1?_((ZZos~i_W$G5xr3T7aMJvF7w$i z%XE=%^i;aQH+pbBc`t~_%(DLxnr;6>3{TttY#3(3{#WMv-5mbkjQvlR_`l@;iphT_ z*J~vI@v{EI8sUF+_P-MTOZYEn|1UcVUH?VP#q56+v6=QiCD=^+pUL)}W&dMZEM)(qIMe<|v+aMFPTT*2 zjNWDamrdY*b#wFIBKm(^@_!}&SJ?kkNEFF`n3eqBis64w^WP%y&rA3(;lC*Sn>@^s z{HI9?|0{w2h0T8p+5g~A3IC=1x2*WTF!6Yb{g1N+?SCfCcxL<`Lb>)owOIV0$qzmw z{?8-^pAr8TJ~bo$FMM#$_&=LNJj?#ak<7IJ36>WBXA*~(_P^VK|8H#mTY&$gvW)*O z`M<*QAI9VOKV0U2Ss(ltlLQyA|CRY4O874f|E8K;1pj6JrW}5$qoHqXj^$!!~KU$*kC4GARZmSL0zV5Z)sPEfl-5aN^okz{LDE?=F?vwZ* zs5UF{|5`pzVDk^0X4^gMbp1iQVe<}eJs7qxo6gUV;7)@RS9z#~O|9HI@K=qN)Qr@d zsxDNsC7Z2!Ln*p-eBFbN?D;wMdv3$qI&NQiCYd)8dr0pA1S5KY!~p>Bo8yOD_W{R$ z@*FsP13=*iChu>n+Zg(Xy?{bIjLzWJ(RufCyL0ArfC2O!w_!61n+tGcP54husTU8b z4yiHmgVSv}#vo8*^2GyZ&>gs!TaWtPUe9YfD(qpWF}!mAze9%tf}}4V?1s6ATbJ%2 z`1c3+ z*mR3(g4pr2wBt?QSYW)JAN|Yj;Ar3uyxm&2-}HdI9-YHKez+)@1_KcMgHe}a^+QDN z!W#>|DX;(oulc6k8Bp@8ckO>T>(=~px98P<9KuCqGeKk6Q#%1OkevVxCIB~d{Kf(k zXnGAe4dFV2%yIv+=XK72&v@ZluEOlXBX{5?mcucF0;OWo&W4fbjKpui{Jnn1y#$%4 zb?G$*m+gU9JNoRxDeP|rt9@Y zuT8e`a2ALJv%n&lh4{fqxv{`3f|Tfw>=HN48ecy!ig_$Dq{O(PbNGZ|0b^CP$}@!WBdDfttb*dazr&bYaft*~8Kr#^JDf@_Bdxyuf=; z_VKdh=r_Xu*A3&h6sCU;{qNLwOjzmvy^;M7YpM9PkAAmvxt{&!n9+&-r<|1jA8Yx1 zjp{#H(BG~)+iw5NRO|T=*3b!R=L9u#obCOg-`@wC2+ZwScOS?f{4v*qay8I4P`h0p zE~!Y)#Q6++x5v{C*RcQg%)cZU-_CL+gHK4wW}sS}5!gO~D?adE06@0PA`?m?O(zs&pbbO{i9k%qj$HpiPreW$vQ++wR|}8$5$t-5~cEp z-|zqT%v zt?eAIdfSuh57LaFQX1A%!55)9&DwxTMfnZ4Af`8++!Bja_Gait{{b740TalSXW&|M zf&~>?5T)~g!ZQeL&-kSI3vS6}+aDaxryTq@0}X7DB8n191tVOl=?yA{>^mg?CFshM z6ajhi9~_}c`yb_{{C5}ae{gN%}?=?bmv4 zf6z92^IKqF99#NeypyO$VjsjbF!PNkh*hvyc=_g-$x%5PzX$biVq8qK<4%t!#_L9~ zeM`2eF>}O=SToy;z}DvMv^$0q+D#u_%JJyjjxO~2OsSKz(dlb%Z5q)DIyIJs=y_N& z!?g!zP>K^Uk|9^ZFs%?&XY*$15QatONs~ zT7^+?YTWS(VrsWWsnvKDOpZ*Jw%+rd#JHhqa!wSFR|DDASaeQ>JRMRdAeXf$xf94* z2bR7!5vg$NZP@P@ZqG0PyS1M?&34~w3?5&4S2^dLf+qCAD_1S+VB9rz%RQms~$^HkRxNWSy?SWCDdB`Z*c#GEwafQ;_Y)?b$ zhHN%uT`DH+f zM6|eM@OYyM7mUL7mOTCCxEt)^ft?lD-UMzbzjvQ8Tf+90^;5h;XAJ-!V?eX~mivSg#*w(fYQ z<42Le?L6%|p;Z&4&CccZPat_ZeXrN`+d$B-9S~aqdv=Uu`e{VwoyNJ_IfEtH-ukvw z_>bFH*9HF=Q!xqugp~Q;*9ZTvP3T_38$)t&EwbMo_8VTD-Q1r<1FKO)RxB}w5V{dIiE5NewtJV*Q<4*WM74L5Ohdbu|_bao=Ud7DTZ2~s@GT1TggE1?E z>z?O?OX)RDq{7&g@4$SAje+yEQn1~BIx)MA?^u)^L|h!V&1?IW*vNeo^>N_XKUK%; z_YcfJRc8=1Js5rYR;fCL8&~5KIB=Tr!N0{v59waj33l%1PODpWTF|}MxBWW@ht7Pe zyB$BNSlro-rnhTep;EE8j{vv_rgy7-c-j37yWxZnn#)tYY&q@Ls3Yh996Fu$rSo-k zA!zJ`y&bhX=2%3->;lCLb|H1t6|6xx_wk!&&pviSlJ78{G`s^WU2Ny-mltMYllBZ) zexu@riMLvwHP5}WZ*>%03>w*uux_O(3j(M2W_?c{?r0JH*Ci;Wo~_hbApWunHsVF}yek$cIKN2t;M4 zV)VlELJ#zD4)0xsZay49Zzom%1N=ES885*&qGrdp4*|r&NIUJS^Ur9-^)j(;_Kq`S zxtz@P8lOJrUJ~>s=M=1)J)yJqJ#Q|_l?qxOhBM09*BE3Z=NX?+FvaPvM`$B~1Y$_T zz{R~R%#kJg&B7xYjZn%ce`rihA|#+%X--ue@tKv!-Y4E`$k*o z%-Wi-+m=XdA2b67s~YNN1AJ~S$EnA*r5>k2xC-2h5tD68Ow7fAs)YR*To?6XY0UN* z%5Cd0#o4ytb80gonOkEpt8TYoB~I-+Z69Q=(>K_e=8TCGTayJ^! z?&^#7Q#FngtNEP* zvk(c#Cgx)2L*=Xs2o9dz`&_C1Xb!xcUvJmKM$d?tcI1 zrPdr?_57W$l~7?@IrtjL!1pR?>cPsr-a&MK9_=1HI5F98->QbwGXChhqq)Xxg&9)~ zY)h4c*fQDIK7~Gd!+}|^wpp#d_h}C#Bj1DX&mP}@RDtFB+%zY>TQ$7~%TzP?qX($7 zVy6WQ&^riK61BkhZ6{pqfCkmCVWW0-cWdX~muCA6Hf?8eAHG#9G2=|-pt3C6OeL{q zcSZjf1v)J+3OL99LkOGH|Isr4&ze5BE&p3;0NVaU_*rJ+xBYS0*>vtVQz(F7q9}!a zCUd`;oM<{d^&9kD)4+gT8jmF7h?vY?Mi4f+-(t5X%Pl!${CjHATw4A!MkG&ZI90j{ zBdp`?BAsVSr;K4Tqiv#Jp>alXJ3?_yrX;t6-UV`u_vvXM*iL7@V~kMCQ=G;ax0D=t zw5DN8Y{M82meP>JHi@8NoKKvQb`SA*Q^N{QH^i`eENC|7@lyIT2{XcWmQhU|k_i&d zZp>DuzAjVY)GV_)(S%aTh~v5a%j;9SUu`)2gD=w)=gP5j9&}GrvjAdJqj<8(RC+;# zaB6x%bJpOM1GA9j_D(;$smc2=rYNP|RN}zs9WZ7{NXhUhvmcNFM0lCmZz}m-8Yyqf;H;7Tudw-T_a|f4Gmq|9=8#7!Kb>_C zD`^p&l`XIH>9A|GoS)kLZs&i$R$kpdeg;hZRO^Gvy>Yu5@C-XaQ;>#1nt1x#i~E1P zdi~qOBY0N9#TP#xKYRW2^V6fBpT2$k`sXKqo<4d0+q=^T_aFZH_~j#L)aQ71vxmRE zd3g+tEXu2V3(Srn99ogv#H3cEaD0V~39{F3cVI?G$M;_!SE{yI>vHIy2U5>Zb?>a* zIke)n`NtRzKEr}LR@Sk--m4gXy|Qa^zImOU%IC_is-`9|(58c@X>w zB*&!CXx^xDV3(m{rdm0$6Rw1jrqNlB4On>=d&-g$doFw+6@3wuG9mRKolfm2d#GC$l?_r<nG!vB-X9B4N>l4YKC)Wdusc8TVn_G0NV!kNdU@A;( zXi?Q4!ip8r{b8cA3R_5S-hH~Tg@Wg@b$^uWLb^XxlVsE~Tlt4-8Vg)d`-e2)1=N2g z-vH4C^ndU|8lyY~AQD{T0vbS66Li$u3>Bb2xhg=DenH4=9iUA`D07v7vucdkW5)|= z0TI?n&!q;W6l1{%iL`(i8Q2_a0oRKfOzlCTQ&5A1vpy3wh%D4FX*pOOYT)yr22GcN z8f1DA)B>o15nd2AP^}8028u*s)PSWZh8kE=)G&hjEYu)JZ=Hu47-LI74HWBK)L=@q z-UKx$Tnsf3%ILhPL2v<^A4d&nW2gaRPIy|uC_(vb)F89D2C^R1z_~1r8nkB1;TqVY zsDa6%T!TPGxdv98YoMecYQU->YLGG?H7Fzsat(;A2{kYP?VF$mnaT{L9yv5GYG9fR znLrJ65!A4tLqQxhV4}Iij8FR$VBqg&V1~fjo`D(o&HV`klcbP80VcR8dN6q&m*Gz! zYIH$=0y#SVdHw{772+FMEdtM6+`#mR7s>G_81Xc~3f_ZnXN zQ#4!N&`MI&qP)rANV_IUUBiQQ&{Vg$N3WE!XeT~N_d%Oo#8t0j9%Rnx~Z9`8$_czhvU&y z3NctvIJTpaV$6-D%E1=J22u^=x$rb`>##PiM2#j#O-@`-cnyIYEf^gUuZ~^pA>CwN zQv!^65*SuNT1utu(8q55)C3Us{P;+cHw7%Z2P=cPTX$u>xi3?O{PVO zy_QK&h+A5!7H+n515R|-SxCH5Q`QwM3T3nozyXv~bAV?Bfaht%;kR&=f>Q~Hh42Qj zBmksMC0Nsgm!w{p7p!z8x^47-Hl4(|A6%XB>r5rFlQ z{;!SxPb4RQVt7E4h^9HTn7}bw&YzqJYdh{`x8pfO-}7xgDTDT$^LA%oDrq@a(D)LX z1nF(uPj36tHQBTRHLVR2Gex!f-oR~lyr$z02j_sZ+l?R%4ID_G5x0M9dtOjdvZa^PQJJjwqPB6tb^Yvun~1-Qi)@AKPDFU*+-%`PvUj%$kv1`)60>^{~wwp_MCz?rMbP+JpL{ZQ6>Z$=(L25w)Sn6@V<7rzKH{783J8%QCsMV0D=`lDT zk^+wlEVd9#*Be}O?h*kAx4{|5LIYnfTkw{sDYWagl-{;OeGA#p(NVZ9E4d_Xc8WYp zMa$HkZg`y670eC91pkW|E7{wqJG%a5bVl+2Gs!@W&s!^slw?`H#jX2_~ zfBrUk9J_7%dY{ zp4jrluHwWZ1aFJ$a7JhycqN!zgAotbRbk<2DuDh^ArLyshLb|jStCu&l9Uv!hr+HZwyUaq} zih^BcBCm@S?Mg#5hkkfD#*m!YN*~jgmsOlA0*n9=jOuem3mSY4UTL!amVY>&S6+#X zvIJ0B0;p&Spz)cUDgzWdqmxyD#z%Hs1SmSYW$B*VTDqqw3IXI8@P!FKf;}?6{3#-@Izdu&Ig~(<)Jeu@N}r90FgMi)#;} z(F&AQ`?fW5);Tn6K@V6PU|^h4F3|80ZO77xp1Eh2Z5QbKV9?z+jx`;3@2tDMWh=Pu zxXzD;f4V>7jCUQk83=hs;CKh!7=pgzL$h-$N*T1(?%VRIK`mM5$Dq^NGdPC3&d!g1 zf**SajF4sZyU_R7sLZ!pi;uJ8k2>Dn`nCn{wH2htH43cJ379CkyJgUb6?Y6D7Bo*3 zr!{jnQvo?(;O#mS@@oa?WAdT>{ZOQ|;+E6%`f&HkCmauK@ye-2YR+`NfhWT&#6{vA7 z>Yi}ZgiSS9EF{G07dn-HxcjA9`Oy+q4!(Z-wlx-sf|JzlL~9&b3?{7*<1Q?{jBbZE ziGa88Ee!^&o?)R#nmeMhlF5O`dil{K36*?gELqrkF)AtQOxiAj^YdZ+x+j4^Yfe<_ zz`21AKd7F03k)QqSDRre85J;f7OG$xw23_&nkoKa@*n&?4Nsu~>7_aVV6l6}F9Kn2))2rUi4a^R-dK06;cfI1An zXY*FowziIfOgv))m@zk*YN>cHHW}#?4NsXFEk+d4p1qZILNUi z`MS0Rv)sy)R09FEYbceWa#wL-)v|rwmaSSg7FmneEej>l{K{oUOdOM#@j3?AXV|;F z&L{IkRGrMSgA*0W0wQ{^KRU4tyf zhmMAe*A30l<7tI{4L>>19k64=R_ih}QIGk}(X35>_SSf%tz-m3LnRn-?`!b)9{#px z(DhgN=iA;_^YFJy^iZg z2@;W|tOU0Hiy@8v>~3E_zW?aY%7F!XYY>{*GT08=(!T5NxAEc{-HSQFf*CAp&jR46 z=!7~RP!xx-=z)ch(u91}ipi<=907wJa_RG-tC)!V){5SN!1x4cC}64vh@B?J_ml`i zxWcom=vD5S7r$45ky=xT?Q5_`-@X(@ZV@m5(uf_qh~3#UC(ItzsA&<@7r>XrV9;v} zoxQTpB#_|f3>U+MyNNLd{p;kQfh6lTLm;%4y{j$|G4myHF~jH4;Ubj34yya55MeoyaOktIOZhZusf^Y$;tlT(Tj|_Jr4FRQFAlp zWBc(hJ+HS@LwLH4g{#`2J8YaAlpAl(rd!Yd!WFZlMi~rzZm2u9$gncqIZ%L5&y|5O z$Bia%=5nUk99qrKtSN?U(P#jZU~?xa|&x~nC<20f%5b}d@BBfabD(sDfK`99R1H;2vDUSsMG_MdZ1Dd zw1d~J2V$ijsMG_MdZ1DdRO*3BJy59!y0dy9R_cLDJ3==z{bE)1?ka+mpo0q`H zS8N?JX!h{wZ>Nu6-hc4?v3b-y`1H5?FCGa7zdqqgkV?ad$re=syK9qZBxN*vD~5x9 zvYgDJWnj!dh7MWoG{D6>fn#LAH0(}utG&Ys3EhlZwecu+w*vpnN_8Zu$Jo=Ea(cuW zD3qK`CB)&rB@368(R|$TeBl3pSS0C0p!2csUNJMQ9N2kQf|y148GHY|AR+@0ecL2c zG1ReF3$z2~*SCmD7{()Z;~-e*)OCw7H6QjA;Hra@#3rP$S0Qhjph1(Hw`Ep5h`g^< zQ{=O$N;&gBa^|VudENv1u+jHo)Oq>SoUp#)CxZB_ukw?^|E(bRTLJ!0AWSFgKT28Ff4YtFzs9_lZdO|%GkDfL zOecX`F*ebh-Hq~yIb7XxnaC6#G2*ufLwA~A4F8rqV>Zud_?3|13anFMN{opV87CoY zW0UCxL0qP}T_U5$@pmlu7xpw66BXCheh5>)okXE+p+0ajhAiG+kfCp8G}+-{@NFyN zVNm`j)}3KW~R=%BQ807IL7-Xx!Te1!1`)`Hm;f@Ot#-KXTfk} zA=`A49KUbE*$R#yw4FVVSz~{L0Ziy}wtn$qGF4$OC%*Bb+A}~B%zt#;6Nwg=E27L2Ohk@g5 zfk+ZN#vzK!KFF!4B!L{~k&crO<{$m}&j%)yJeV!TMlJ{NJ#;!a?RLJe1nV-97inH2 zZ}!_Qvau8u+}Ws6Zy3&E_I^Q|noXEi*&Q8-Q3JRIuWfg~+qGoSwllqVdv+nZCSin` z2toF;d)DeUhW??=xpp{aL5nU+@i_vO`~=%!xQoHiH@o9s`+!s$12fSeF-&E|G`pju z7}mi+*TRR*l+C&C0mZ#Oyl?X?+O!?jqhUSx^1^L*Uj6*2YTSU#cbjt17_VU5t6nM9 zm!E9;Kgio)QZ=kK|2v{n@;?!*tp9c^&+cQg7KX{TmBS_1P8;eQpm{|dlAM}kbkKc~2a|2w7sTd`Yx zsM#BN){hi@qw6zteP>;p7BE$JnCfl6kS@@fQU*HH+CXPQ9q1I)2RaKW1Rbm6awb%Q zMLm737R-y%X^-@SBZtSGaM5$Mgkuk!q~~L#Fm$FhhVwduPiqaQrddFD81o{|QzANX zEuxd5Mznd^ay5ye=UZHvIN`W7Yq`@Di>XfAvo(w}oTK8MMES=iHHI0Q!l=564N#fR zK((8ev1__nGKE&8#B|15%o&-sGW3!YX}|LHlg6Vl)KJ=$9IGILTvJtNZVkl51S)gr zASNHp)k2IxprEGA=G`l@ZWCkY=+j2(tDsa&A`@4bZ_s4ZUZys!*X>>=yUcbvFXla`KSQU_lM$L2Y5Bs;kFe@6 zh(AKk@s*y43JH{cqqR;```sWeApJ0ADvd@nxsgI}@}1-P6pckLUhp&V6NvChxYR#T zSnT;8JQzKBGP%Cy&~1Kg%`9%TuK)O;=kgPMKDn3erbUDSziZ7?fhN=N8Eeu1ASoyF z|4CZvf7bHpUO3KovGm-(0YPb)z^T2r!O7_MBO_>xx%f`kFlT;=4E}NO-x~h?_j=(U zBRr}9VSp1#_+N|u2e$jb@qkhFO=uGWk@n0ToClepqY&PPVP@Sk4;t(X>V&Equemkv z3xvDcWv=M-~b06$U6h* z>cGLo*%@>P?&X0a`R-(2V%(Qtl0aXWP=#i8CdQrVsm}CNXKU}sNbg&VqjH92iONg- zZ}GozrCJ*1FM|IumlRFv|0OQ@zgx}!ks3kD-`crnq+jIkI#DeTn~dPV2yzv+FxhhAZ%+j@BSfj zhMg-|hI4pobS0STOIwKN!aGD89`nwg!~Dgfv8AGG*qm~_fBeD2YhI{wymGvG$6Exj z@SK2!7X&Pv1z3wtJpSBtTVPd!624}*TlK!izz(d*mD_2zjFH>xb%8Zf4CExUzZafw zToy)5b^NaN5^Tolf??wYRtDJh}h# z^8@D?#(2{)HZrey42;#0&F=ZYuyjFHVBgeSg!MRrGu&xD3fQe*y=&juK{bT@kspHL z>x`FRShMbcGJW7g7Pjx~F?WuQed6G|<(C_cMgQ!l&ShdI2jP!xv^`3()Prp|ZrS6O zW^E4QKNef4*scY8()T{QCZl1qZHj5vM_F3~t=fU*h7U0|Ne~A?Qo%#)xYlL-uw^`> zLRS1VdfscEhJ7ANIl61{QSAD^7%U&Ze*N2P=x*fBHj+}pwbsty%OR8UtoP?F7yI5{Lz9xqWP|eUZ5Jkfd2O>p zL95u6VJEs~pt#*Qy?}!Szk*E;e_VFE7sH;VC8jk{l@H{p&y{-Re|*$?@b{CZnQ7etq%wm!r)t^@HF4;1|aa|CYZWy?TB2=d0GU`Z4|d%SHW{zaF^5 z7sn5tcj)y6ebM~%{ye$Vb3HldQeS!0`&0ftd?mY-K6xu&>CUTP+THt}_wdWG>mB#& z;{CfL{_Eh~{dd=IpZ11F-hJ#zzx#&0fA;>^d-2yY|m-pW9`0+)v&mR2#`0C>L!PBd^EmzRqK(a1x zi&yRU7q5PK@v8qjIp%Nwyx^}p7k_*?a*rM!J%4lEbsuhat?$2U4&T3e`{Lqd=f%_K z@Be=N=1j&b9v2efRn36+3(M*Q4VO!H@L)`+leK-0!`6*LwNaU+n1q zgY)O_e?Rj7e)jyn|Ln_)_a8rLpYq4=hQD9Ee&71*SKfH|{I8b}>n|@Z{0B!L?~|h! z51X4^YWnM6?e~8_B5mP)A)l}AzkhN3@GbvUuuoSn>wjOl{LeSTzXyNUsrUmYmwK)z z=Ui&S`i(Ec^Isk`{5KD}=huInzd6DK`L=ugsQW_w{;umk?R)(t|?6X`OW8Ff|K#xjxB%Q+?~6J=x{C8#bka8 zcoB@bAFRdV{(p8+_F9*)=u5H#=lTDESTyPXLvdOEcP*b%kF!oa&P~u1s!>7agzm-R zADDz;XHD>r=e1uSh0mQDf_LgR-QK|KkLc;v|NM`$hpNcA zhpW>0pa0pq*K->e?wRL=&u`t^x)(;0Z^y9AnR|BGcKc0lFzD`sgyVMHy|eDNY26xj z8qSXE{CEW1eHi)Y^>-b&**rQ2I{O4@`a9_9HB4v6hi2wt)l`ae+kNA(b6K?k<<5^m zzcr)k-*t9=G}6MuD8}57=&Iux4q(^$x^?fnux^xFix0Hpk9ywSy7$c*z1+m|7*v_R7jT$qHJqOx+3@c1!oZCnfrq`5ykUH{OepBo4c~O>2SqzN z16aG*n4?c@?0et2!@;>p0b(5Znr7pIxy5_E7oT8Moh8vAHqrD3u?eR)h)q1IIsz{c zEUrCxSH>Vey}&zzx-$rTuWQE-s!o74ogeL-YY)u7yQv%V)$XQ7RpH#zA&fZReb+d* zZgIx3`^(^wJ8*+<4{aGICyGU_h3#H={fkS_EL_zAlI%V<^@2>OID9eP*#Jd&FsvD3 ze^z7Nw7drJqIThHwc4(EHu(9-=nmR9y3vb{Y90nn+u>pfS zKD<7(4wl^ln`!&X#L3zrcYc|$J|T47JG6BZgHatj<)H6}qdD!o^gdgddFHuIufL;dYvamG+`DHc4dy0103C zUf8p}8qO+Xh;is@UARrtAaPpY2|_@TOY>~Lp(LU_SQrsrjy4E=Ke4fuQ>M z5qu#{$k>mg@oIi!h*`BaNn`QG+CG08_aDO6Ez>Vc}4&yHBI3`7JPCRx& zJrLlzUOA29K#>xMw5id&FX?V$dIz5)GQb6q3;PJGoCF1if{Y=fqVK}qlmrJIVkG^% z=<|mOQ11z01Oh@Un~kpQxAfdRd1BV2fFADmAayF0pb4KkqYtVur(|a!>>j6P5r>Da z+H(jL!4*KTyvIGqlaE1c;6)J_UY@u&&d}f9ut)8abEoh~s~5uGfB8jKBOVO6e)Niy zLuy44)Dkk+t#hOr(HPF?jdP=#7ApCPzDm#O*Ils4HeuZbJF!k(^@cHVnJtbV*(yRD zjXuS7g$=}+O}x3MBI6t+uQO={j)V#-75;lj;tqG_-%g=TFp7YQ5?k8^Vima-I_b0N#9rPjS`- z&cea=@m$70Po#y__vUuA#P>vUQ7K<5zg!m;8Nm5`Zm#npYv(&S`gdYk>4a)X;*FlO zCH@URc`J4B6GS#$YPS7nZ8DoT#k5T822tci=x=F!61GujK1HZt=`}MMi{^R1EGE8- zKQo)Cps&FuR64G*2Gis-&v>*%0Kd9JK>v#X{KQU7o1&+rLci1Kf7FTzv-2wb%U&g% z)pf7`NFVb4+#B%rq<_>^6f*^39ry5sXdRWsMBG7Z9D~5JCco>4DSZl2Y3G4lnRC;2 zfBa>a>VTu+NHraC=B~&i<(HXxJF^TeFpFx@0a+4(m+|7Pdk?EIUZf3x#%{%80%H~oLnQJ9~}1^BT4Zy(3KH~oJ{ zyWRQk|1SQ&!$bCR&_62v;ol;8#rg*<4XOgS{HtD8hf{*73i7>HwZn4i6q&H};&yGz zzJ7IzT^p-1#AIv6TrX)@cPeCu`|O1o?(e6AJ?Fnz;?!=@D*5n_e5Fp3%8U$b4b&|b z>yKiG@d?hX;p;fy7P9z4R{3q4+YT|iz`-h1(|r7B#` zTd7Ef{vlYZztt?=;GrHYTlbOq*7lYejmP#9uARb^m*{mgH*);6W^1p<_?&&Zp2vcg z+A+v-D)NO&nzg|@@l^99Vug_ENY!Q~Qed#&U5V(-0ZtCF@o10IFOi@p+_0uh zQbo433&N^hzHEwPM|0bvDej0W$eVo&d-cl3D)uPTI_k2=Ls1PP9z)WIP8=R}(TOfP z(M2aVq7z^5|38RM9PSRg=tLKt_@w9r6VB1#Fx@*K$~oG9mhKIjf?;a5A>Cws7Ll`r z)-J}bi2De~ZF@*|{!hF^@Eu5Rnz8#p3~0~oq?Y*)>hEQ7KRAJ?1~|)_Vr}t*4fn_V zJ^fAFw(srTa#@kW0P{9Aua?W73OLmR>q@-U?|Nm?^F670c6nuaf0VSPh+-@W8O%t& z-AnPPaP^`-_vKR?99}XjiA|&eEHB3MHM{6>_9JU5GBT)xemL&->q>P!hZJ@(L5c~m zq9nbLHOfoE%QQ1T+PNL`i6s~-kx{-_FHB*XwmaS3X}r+doH7L z;?K28b+AerOsh`Lspi)Nc9#MC_PCAI#vQFC6Z{nb`Q-)}nAYnVY> znUxBnC@90Py%AACghpObKYXyiT?P-{Xif`6YMdcCL1a-eNSdl$!qO*kHI{LT z!+{1U3Pek=F=!C*WU_DzHb3ir5T^Kz)^AY368Q!v>ym(of|?s**D1B6j#ifajhNI8 zM-mB>)7;J{e3p~BxG{n6vi>S@2ID#cO_C*DDR$U%P?;IC5#@BLxIQF4xmx1(13aQG z3%NkEENHWo1xGTp1TjGX!$W3;FTrlvjUv_BAym_}jby(N&0K;Fb6hQ)vg?h_39 z6bHqo#(Rj?Y-7j%^`#q<&)7|xV~v8H^(VKL^FP3cjWOY0$64Yy5`KQSj(nb|_+CeK zSM&Fe+oRTr@s_3^r&?2CbTAIbnbH~AsL|vSc_t)Bk9b>Wul~L!jzU8bIw0jIQ6vLiwmolMY$!OUe)+|GvEkMUE}h7b{*~rAze#iqd<)sqCMRpmPQc>X z29sK)W<;~tr6@KYyzFG=y(|>Wx)DCMqa_)8q|%ANqs|Mp>2P69h{55Kk`VUKIIuP6 zv8!Jo*Zq!((UXknoz}_II8T6GTTEVUxoSH9{sqI}88{!8$DZg<_jSYiF?FN8 zv_Ep>>N@h9s;w*h7cCI!K~LJK`c`Q5*l=ixka1k2+0-$pW2M(qFy=t7i4dB+bl08- zk7duV?#o1ppF%Vq?Q%d1|2UgssZKZ9xUB6AoB{^Le$HId5&QS7CI89Zs84r80jNdv zCGxP$1jg%fiL`;5;0KKIgG+(gl7=a&UNqlRmni-e?e1uRsNBxc;2>ystm!(20gsfZ zzx`PMUuISQj@K=dv*pC9s9Y2leKzfYorbpG4kK2T6pqs6hQh-D#PpEM2?FMw>sDKi zM>y3mBM6mw-Up;(nf`FdYdQ3>$$O{2(_&hHW|y2V#BT}vooI#z_zzS)S-`T8>K{us z+`xG=`8ea07)7zFTVF>&rRA3_j$IolEQQ)z;0O8$ShB5B6Zv z(*FmCE`%EqUTq$npT)H<+hU%7u)_12W^c+jN=ihS_!ud&oZM*aWO#5e_}XPBDMNRW zzp)BJ4@Pc_MuCQau;g_Kj~<)$g4_(;jh6|L%X;aTfO$E zwP|=F>Rt%b7W{~jcwhLy#e&V&2YcgP96u`GqXFH@K#z@M+d*2TNGc$?m-&gibcDmb zba(&}gnyVl(C~g{md|THUN6t6zw;B+@D3Yii**)lLaLaN{p1*CKaOQbvyoxAgs^5e zq%U1H`r!~Q-<)=g9b{RJCtfmVGmPMAJ{$<5VhfIavW6_cOol~`zXgw+^ayhD8OPoL z!!?qtVI#poGyI|$EpJ9{m#>!jn1dy>I%mh{vOxStv@rBLsS(9=vC3E`O?zK{buFj) zny)5G|6fhMWES_P;ByfpPExUK_BKNDKw#=~^$=m_TZiJ7WvHXCU&r|-%Gs|=)F4=F z@K%`4GR}}bS;d)~0Q5AZ)T~Gp*s6v9M;SOj>ssEU#ogCZk~G-WdE}#1v}@mn_Kw}~ ztsSHl*H9nl06DmP1&Ez|NoWJ1?QjmTnb_%wAJ zN=Ra!IxIX_lqAqN^{7G~(0C@7wig&A+1VW zyECVqdPT|;HF<~eWg7liHE?o66Nt_9@rsOVcY<|xD*qBXS05`5OE|SQLV+N=!c%6& z2ob_($iV?-bq7$;4xc$aNRP?p!jZ!kpehonwcha@!VOy&&|Th+mhbFJBPj3vD!4X; zyjLS&lv9o2ak773!}Ke!3%*uzQPK|wyIikm$a01>qf5ho49Bk<@xsOEa>i>3!Fo}y z*|~+AV@L)ToWZHxyekRJim0|a@rjoL?edZ|5!j8ydaDXsL+N9t11P6c+EXPbmq@5^ z5amjXTjAlelR7lRJ}ulC6s7VJ zVcF`KRG9fH??>z?-ZHfIH}DDMS9z5~r5P2H&9|U>`*Ccocn?SNbVTVxOf-<2S82hYgmlbkfmEx1SlngBpBou?nEXvpo>|rypX_AZ z6D1zuQrE<{y3Qv=dS%2@r|V4;A+lpXkaa5U=fOo_T%Tj10<%PJ@W#|IRF!hoWmK*$x|deVYQR)fJ`+(F)wX$`vBL_H*^uCRr&lG_C;CfvU zdEf{H52AS<*N6oAqn|e33mB6W8(` zfaIUyQM$JqzK7!e{;LxCr@~3Uc><`ylJYUX9^F(V(kkeI3%Mgu!WMc}15Lu)(O0bm z?My?SE6C(N+%vN1;m4S80~D+#@f^F9eMJ@AIqq$PBME@-SRE$2;Yw}pnvuvEnyFbu zX}SOamB?q%(ZgDD9#)KfRcATA5TL)l%jdDu#OZU!;;N3;H)I^q8$XVwc7D@yBgA-; zkc1cI*J(bTG-F)otBj}Aja~jghlysPiq=MR+&$61pL0A{Ur0{8$Ngo&a%=^~3i#9I zo+509G@_7;8?$1>mN@kn@=ySgHb#Uw%P(nf=AvN1!XT!78jD7Dy?@9r@qtyYAB-t? zj~t6MatxJJSg7{B#LWUB?k7(+^AC7k^%JG#>V*Cd^JTnH6&WEKBi>lhC}E-DfF}|M z*bQoBN`6&LdVUGGXX5)=gIBiG;(ddr{%S)NO4Uwiyi(hsQm%WHOu3b()3$d$jZg(n zKv@msW2AbLpg8QVx`?E9S6M9V z+(%&53^DsCd4;wV{aKM}!j`x#*cpfT5Lhi{07(V~hSL{s1eP#ky?2u|Bz#W^?VDAp zKI-Auv(($1^Q>6W5>CtM@cF`j2j(U~ggD&x6WgxQ^mdrpZB3qu+02XAko&dwYm(n= zh0p5TS5ObhW{qlm9BK9D_!YISGv-bu5_g{qlm9G5n Om45 512 { + t.Skip() + } + state := &fecState{data: make(map[uint32]*Shred), coding: make(map[uint16]*Shred)} + for i, choice := range choices { + s := &Shred{Variant: merkleDataVariant, Type: ShredType(choice % 3), Payload: bytes.Repeat([]byte{choice}, dataPayloadSize)} + switch choice % 5 { + case 0: + s = nil + case 1: + s.Recovered = true + case 2: + s.Variant = legacyDataVariant + case 3: + s.Payload = s.Payload[:20] + } + if i%2 == 0 { + state.data[uint32(choice)] = s + } else { + state.coding[uint16(choice)] = s + } + } + want, wantOK, wantErr := referenceFECRoot(state) + got, gotOK, gotErr := state.merkleRoot() + require.Equal(t, want, got) + require.Equal(t, wantOK, gotOK) + if wantErr == nil { + require.NoError(t, gotErr) + } else { + require.EqualError(t, gotErr, wantErr.Error()) + } + }) +} + +func TestFECRootCacheKeepsDeterministicDataPrecedence(t *testing.T) { + packets := append(localnetMerkleShreds(t, "d"), localnetMerkleShreds(t, "c")...) + f := &fecState{data: make(map[uint32]*Shred), coding: make(map[uint16]*Shred)} + var shreds []*Shred + for _, packet := range packets { + s, err := ParseShred(packet) + require.NoError(t, err) + if s.FECSetIndex != 0 { + continue + } + shreds = append(shreds, s) + } + require.NotEmpty(t, shreds) + sort.Slice(shreds, func(i, j int) bool { + if shreds[i].Type != shreds[j].Type { + return shreds[i].Type == ShredTypeCode + } + return shreds[i].Index > shreds[j].Index + }) + for _, s := range shreds { + root, err := s.MerkleRoot() + require.NoError(t, err) + if s.Type == ShredTypeData { + f.data[s.Index-s.FECSetIndex] = s + } else { + f.coding[s.Position] = s + } + f.rememberAuthenticatedRoot(s, root) + want, wantOK, wantErr := referenceFECRoot(f) + got, gotOK, gotErr := f.merkleRoot() + require.Equal(t, wantErr, gotErr) + require.Equal(t, wantOK, gotOK) + require.Equal(t, want, got) + } + for _, s := range f.data { + s.Recovered = true + } + want, wantOK, wantErr := referenceFECRoot(f) + got, gotOK, gotErr := f.merkleRoot() + require.Equal(t, wantErr, gotErr) + require.Equal(t, wantOK, gotOK) + require.Equal(t, want, got) +} diff --git a/pkg/turbine/entries.go b/pkg/turbine/entries.go index b4657ce1..f4f505b2 100644 --- a/pkg/turbine/entries.go +++ b/pkg/turbine/entries.go @@ -131,7 +131,12 @@ func decodeEntriesAndAlpenglowMarkersFromDataShreds(shreds []*Shred, timings *en sort.Slice(shreds, func(i, j int) bool { return shreds[i].Index < shreds[j].Index }) + return decodeEntriesFromOrderedDataShreds(shreds, timings) +} +// decodeEntriesFromOrderedDataShreds requires increasing shred indexes. The +// assembler supplies that order directly; public decoding still sorts input. +func decodeEntriesFromOrderedDataShreds(shreds []*Shred, timings *entryDecodeTimings) ([]Entry, *AlpenglowParentInfo, *BlockFooter, error) { var entryBatches []*prefetchedShredBatch var parentInfo *AlpenglowParentInfo var blockFooter *BlockFooter diff --git a/pkg/turbine/entry_prefetch_benchmark_test.go b/pkg/turbine/entry_prefetch_benchmark_test.go index 4a0f14e7..b8279928 100644 --- a/pkg/turbine/entry_prefetch_benchmark_test.go +++ b/pkg/turbine/entry_prefetch_benchmark_test.go @@ -60,11 +60,12 @@ func BenchmarkEntryPrefetchAssembly(b *testing.B) { } type assemblyFlowFixture struct { - components [][]*Shred - blockID solana.Hash - parentID solana.Hash - bankhash solana.Hash - shreds int + components [][]*Shred + authenticatedRoots [][]solana.Hash + blockID solana.Hash + parentID solana.Hash + bankhash solana.Hash + shreds int } func makeAssemblyFlowFixture(tb testing.TB, source *block.Block) assemblyFlowFixture { @@ -90,6 +91,7 @@ func makeAssemblyFlowFixture(tb testing.TB, source *block.Block) assemblyFlowFix chained, nextData, nextCode = generated.chainedMerkleRoot, dataEnd, codeEnd roots = append(roots, generated.fecSetRoots...) var data []*Shred + var authenticatedRoots []solana.Hash for _, packet := range generated.packets { shred, err := ParseShred(packet) if err != nil { @@ -101,9 +103,15 @@ func makeAssemblyFlowFixture(tb testing.TB, source *block.Block) assemblyFlowFix if err := shred.VerifySignature(public); err != nil { tb.Fatal(err) } + root, err := shred.MerkleRoot() + if err != nil { + tb.Fatal(err) + } + authenticatedRoots = append(authenticatedRoots, root) data = append(data, shred) } fixture.components = append(fixture.components, data) + fixture.authenticatedRoots = append(fixture.authenticatedRoots, authenticatedRoots) fixture.shreds += len(data) } appendComponent(NewBlockHeader(99, fixture.parentID), false) @@ -154,8 +162,8 @@ func runAssemblyFlowBenchmark(b *testing.B, source *block.Block, fixture assembl if span > 0 { time.Sleep(time.Until(started.Add(flowArrivalOffset(componentIndex, len(fixture.components), span)))) } - for _, shred := range component { - candidate, err := a.addShredFrom(shred, false) + for shredIndex, shred := range component { + candidate, err := a.addShredFromWithRoot(shred, false, &fixture.authenticatedRoots[componentIndex][shredIndex]) if err != nil { b.Fatal(err) } diff --git a/pkg/turbine/fec_root_cache.go b/pkg/turbine/fec_root_cache.go new file mode 100644 index 00000000..cb4395b0 --- /dev/null +++ b/pkg/turbine/fec_root_cache.go @@ -0,0 +1,56 @@ +package turbine + +import ( + "bytes" + + "github.com/gagliardetto/solana-go" +) + +// One snapshot per FEC generation binds an authenticated root to its source. +// It is written only during assembly, before the state is frozen. Completion +// never trusts pointer identity alone or changes the deterministic root choice. +type authenticatedFECRoot struct { + source *Shred + input shredRootInput + payload []byte + root solana.Hash +} + +// These are every parsed field read by MerkleRoot. The exact payload comparison +// covers headers, data, proof, and trailers, even for noncanonical test input. +type shredRootInput struct { + variant byte + kind ShredType + index, fecSetIndex uint32 + dataCount, position uint16 +} + +func rootInput(s *Shred) shredRootInput { + return shredRootInput{s.Variant, s.Type, s.Index, s.FECSetIndex, s.NumDataShreds, s.Position} +} + +func hasMerkleRootProof(s *Shred) bool { + return s != nil && isMerkleVariant(s.Variant) && (s.Type == ShredTypeData || s.Type == ShredTypeCode) +} + +func (c *authenticatedFECRoot) matches(s *Shred) bool { + return s == c.source && rootInput(s) == c.input && bytes.Equal(s.Payload, c.payload) +} + +func (f *fecState) rememberAuthenticatedRoot(s *Shred, root solana.Hash) { + if s.Recovered || !isMerkleVariant(s.Variant) { + return + } + if cached := f.rootCache; cached != nil { + // Data proofs precede coding proofs, and the lowest index wins. An + // unauthenticated earlier arrival can still force fallback at completion. + old := cached.input + if old.kind == ShredTypeData && (s.Type != ShredTypeData || s.Index >= old.index) { + return + } + if old.kind == ShredTypeCode && s.Type == ShredTypeCode && s.Position >= old.position { + return + } + } + f.rootCache = &authenticatedFECRoot{source: s, input: rootInput(s), payload: bytes.Clone(s.Payload), root: root} +} diff --git a/pkg/turbine/receiver.go b/pkg/turbine/receiver.go index 69b67190..f5588bf6 100644 --- a/pkg/turbine/receiver.go +++ b/pkg/turbine/receiver.go @@ -703,6 +703,7 @@ func (r *UDPReceiver) processPacket(ctx context.Context, conn *net.UDPConn, pack r.codingShreds.Add(1) } var leader solana.PublicKey + var authenticatedRoot *solana.Hash if r.leaderForSlot != nil { var ok bool leader, ok = r.leaderForSlot(shred.Slot) @@ -714,7 +715,8 @@ func (r *UDPReceiver) processPacket(ctx context.Context, conn *net.UDPConn, pack } return true } - if err := r.sigCache.verifyShred(shred, leader); err != nil { + root, err := r.sigCache.verifyShredRoot(shred, leader) + if err != nil { r.signatureErrors.Add(1) select { case r.errs <- err: @@ -722,6 +724,7 @@ func (r *UDPReceiver) processPacket(ctx context.Context, conn *net.UDPConn, pack } return true } + authenticatedRoot = &root } matchedRepair := false if onRepairSocket && r.repairClient != nil { @@ -773,7 +776,7 @@ func (r *UDPReceiver) processPacket(ctx context.Context, conn *net.UDPConn, pack return true } } - work, err := r.assembler.addShredFrom(shred, matchedRepair) + work, err := r.assembler.addShredFromWithRoot(shred, matchedRepair, authenticatedRoot) r.slotResetMu.RUnlock() if err != nil { if errors.Is(err, ErrDuplicateShred) { diff --git a/pkg/turbine/sigcache.go b/pkg/turbine/sigcache.go index 7f3b1313..75b040b3 100644 --- a/pkg/turbine/sigcache.go +++ b/pkg/turbine/sigcache.go @@ -43,9 +43,16 @@ const shredSigCacheGenCap = 4096 // verifyShred authenticates a shred exactly like Shred.VerifySignature, with // the per-root ed25519 result cached. func (c *shredSigCache) verifyShred(s *Shred, leader solana.PublicKey) error { + _, err := c.verifyShredRoot(s, leader) + return err +} + +// verifyShredRoot also returns the root authenticated for these exact bytes. +// Callers must keep the shred immutable through assembler admission. +func (c *shredSigCache) verifyShredRoot(s *Shred, leader solana.PublicKey) (solana.Hash, error) { root, err := s.MerkleRoot() if err != nil { - return err + return solana.Hash{}, err } key := shredSigCacheKey{leader: leader, root: root, sig: s.Signature} @@ -53,25 +60,25 @@ func (c *shredSigCache) verifyShred(s *Shred, leader solana.PublicKey) error { if _, ok := c.cur[key]; ok { c.mu.Unlock() c.hits.Add(1) - return nil + return root, nil } if _, ok := c.prev[key]; ok { // Promote: a set straddling a rotation keeps its entry hot. c.addLocked(key) c.mu.Unlock() c.hits.Add(1) - return nil + return root, nil } c.mu.Unlock() c.verifies.Add(1) if !narya.VerifyStrict(leader[:], root[:], s.Signature[:]) { - return fmt.Errorf("%w: slot %d shred %d", ErrInvalidSignature, s.Slot, s.Index) + return solana.Hash{}, fmt.Errorf("%w: slot %d shred %d", ErrInvalidSignature, s.Slot, s.Index) } c.mu.Lock() c.addLocked(key) c.mu.Unlock() - return nil + return root, nil } func (c *shredSigCache) addLocked(key shredSigCacheKey) { diff --git a/pkg/turbine/transaction_job_groups_test.go b/pkg/turbine/transaction_job_groups_test.go new file mode 100644 index 00000000..ab2edb55 --- /dev/null +++ b/pkg/turbine/transaction_job_groups_test.go @@ -0,0 +1,118 @@ +package turbine + +import ( + "context" + "fmt" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func TestWideVerificationJobsPreserveSignatureFailureIndex(t *testing.T) { + txs := verifierSignedTransactions(t, 320) + txs[33].Signatures[0][0] ^= 1 + txs[98].Signatures[0][0] ^= 1 + for _, groups := range []int{1, 4, 8} { + v := newTransactionVerifierWithJobGroups(2, 32, 8, groups, nil) + r, err := v.submitTransactions(context.Background(), txs) + require.NoError(t, err) + index, err := r.wait() + require.Error(t, err) + require.Equal(t, 33, index) + v.closeAndWait() + } +} + +func TestWideVerificationJobsYieldToReadySmallRequest(t *testing.T) { + for _, groups := range []int{4, 8} { + t.Run(fmt.Sprint(groups), func(t *testing.T) { + large, small := verifierTestBlock(800), verifierTestBlock(4) + started, release := make(chan struct{}), make(chan struct{}) + var releaseOnce sync.Once + var calls, beforeSmall atomic.Int32 + v := newTransactionVerifierWithJobGroups(1, 16, 8, groups, func(tx *solana.Transaction) error { + for _, s := range small.Transactions { + if tx == s { + beforeSmall.Store(calls.Load()) + return nil + } + } + calls.Add(1) + if tx == large.Transactions[0] { + close(started) + <-release + } + return nil + }) + defer v.closeAndWait() + defer releaseOnce.Do(func() { close(release) }) + big, err := v.submitTransactions(context.Background(), large.Transactions) + require.NoError(t, err) + waitSignal(t, started, "large job") + little, err := v.submitTransactions(context.Background(), small.Transactions) + require.NoError(t, err) + require.Eventually(t, func() bool { return len(v.jobs) == 1 }, 3*time.Second, time.Millisecond) + releaseOnce.Do(func() { close(release) }) + _, err = little.wait() + require.NoError(t, err) + require.Equal(t, int32(groups*8), beforeSmall.Load(), "one large job, not an entire catch-up request, precedes small work") + _, err = big.wait() + require.NoError(t, err) + require.Equal(t, int32(800), calls.Load()) + }) + } +} + +func TestWideVerificationJobsCancelBetweenVectorsAndJoin(t *testing.T) { + for _, groups := range []int{4, 8} { + t.Run(fmt.Sprint(groups), func(t *testing.T) { + started, release := make(chan struct{}), make(chan struct{}) + var releaseOnce sync.Once + var calls atomic.Int32 + v := newTransactionVerifierWithJobGroups(1, 16, 8, groups, func(*solana.Transaction) error { + if calls.Add(1) == 1 { + close(started) + <-release + } + return nil + }) + defer v.closeAndWait() + defer releaseOnce.Do(func() { close(release) }) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + r, err := v.submitTransactions(ctx, verifierTestBlock(800).Transactions) + require.NoError(t, err) + waitSignal(t, started, "first vector") + cancel() + select { + case <-r.done: + t.Fatal("released transactions still read by a worker") + default: + } + releaseOnce.Do(func() { close(release) }) + _, err = r.wait() + require.ErrorIs(t, err, context.Canceled) + require.Equal(t, int32(8), calls.Load(), "canceled large job finishes its admitted vector only") + }) + } +} + +func TestWideVerificationJobsDoNotWaitForFourTransactionBatch(t *testing.T) { + for _, groups := range []int{4, 8} { + v := newTransactionVerifierWithJobGroups(2, 32, 8, groups, func(*solana.Transaction) error { return nil }) + r, err := v.submitTransactions(context.Background(), verifierTestBlock(4).Transactions) + require.NoError(t, err) + select { + case <-r.done: + case <-time.After(3 * time.Second): + t.Fatal("partial work waited for another submission") + } + _, err = r.wait() + require.NoError(t, err) + v.closeAndWait() + } +} diff --git a/pkg/turbine/transaction_verifier.go b/pkg/turbine/transaction_verifier.go index f8a1d27d..6dfb5507 100644 --- a/pkg/turbine/transaction_verifier.go +++ b/pkg/turbine/transaction_verifier.go @@ -17,9 +17,14 @@ var ( errTransactionVerifierClosed = fmt.Errorf("transaction verifier closed") ) +// Four vector groups amortize dispatch for large ready requests while bounding +// the work that can precede another component on a worker. +const defaultTransactionJobGroups = 4 + // A job is formed before admission, from transactions which are already // available. Workers never wait for more transactions to fill a vector group. type transactionVerifyJob struct { + ctx context.Context txs []*solana.Transaction errs []error start int @@ -65,7 +70,8 @@ type transactionVerifier struct { verify func(*solana.Transaction) error workers int batchTarget int - // Each accepted request owns at most workers outstanding groups. The + jobGroups int + // Each accepted request owns at most workers outstanding jobs. The // admission semaphore also bounds asynchronous request goroutines; callers // apply backpressure before handing off another decoded component. requests chan struct{} @@ -81,19 +87,28 @@ func newTransactionVerifier(workers, queueDepth int, verify func(*solana.Transac return newTransactionVerifierWithBatchTarget(workers, queueDepth, sigverify.BatchTarget, verify) } -// queueDepth is a transaction budget, rounded up to whole groups. batchTarget +// queueDepth is a transaction budget, rounded up to whole jobs. batchTarget // counts signature lanes: multi-signature transactions stay indivisible and // may exceed the target. Four/eight targets can be compared without changing // the admission or cancellation policy. func newTransactionVerifierWithBatchTarget(workers, queueDepth, batchTarget int, verify func(*solana.Transaction) error) *transactionVerifier { + return newTransactionVerifierWithJobGroups(workers, queueDepth, batchTarget, defaultTransactionJobGroups, verify) +} + +// Job groups amortize dispatch over already available vector groups. They do +// not change vector width or wait for future transactions to arrive. +func newTransactionVerifierWithJobGroups(workers, queueDepth, batchTarget, jobGroups int, verify func(*solana.Transaction) error) *transactionVerifier { workers = max(1, workers) batchTarget = max(1, min(batchTarget, sigverify.BatchTarget)) - queueGroups := max(1, (queueDepth+batchTarget-1)/batchTarget) + jobGroups = max(1, min(jobGroups, 8)) + jobCapacity := batchTarget * jobGroups + queueGroups := max(1, (queueDepth+jobCapacity-1)/jobCapacity) v := &transactionVerifier{ jobs: make(chan *transactionVerifyJob, queueGroups), verify: verify, workers: workers, batchTarget: batchTarget, + jobGroups: jobGroups, requests: make(chan struct{}, 2*workers), stopped: make(chan struct{}), } @@ -114,17 +129,30 @@ func newTransactionVerifierWithBatchTarget(workers, queueDepth, batchTarget int, // request's bounded completion channel always has room for every pending job. func (v *transactionVerifier) verifyGroup(job *transactionVerifyJob, batch *txverify.BatchVerifier) { defer func() { job.done <- job }() - if v.verify != nil { - for i, tx := range job.txs { - if tx == nil { - job.errs[i] = errNilTransaction - } else { - job.errs[i] = verifyTransactionSafely(v.verify, tx) + for start := 0; start < len(job.txs); { + // An admitted job always finishes its first vector group, preserving + // ownership/join semantics. Cancellation can skip additional groups. + if err := job.ctx.Err(); start > 0 && err != nil { + for i := start; i < len(job.errs); i++ { + job.errs[i] = err } + return } - return + end := transactionVerifyGroupEnd(job.txs, start, v.batchTarget) + if v.verify != nil { + for i := start; i < end; i++ { + tx := job.txs[i] + if tx == nil { + job.errs[i] = errNilTransaction + } else { + job.errs[i] = verifyTransactionSafely(v.verify, tx) + } + } + } else { + verifyBatchSafely(batch, job.txs[start:end], job.errs[start:end]) + } + start = end } - verifyBatchSafely(batch, job.txs, job.errs) } // submitTransactions admits one immutable decoded component or complete block. @@ -168,20 +196,28 @@ func (v *transactionVerifier) submitTransactions(ctx context.Context, txs []*sol } // verifyTransactions keeps a rolling window instead of waiting for an entire -// worker wave. A slow group cannot idle workers whose earlier groups finished. -// One caller can queue at most workers groups, so a large catch-up block cannot +// worker wave. A slow job cannot idle workers whose earlier jobs finished. +// One caller can queue at most workers jobs, so a large catch-up block cannot // put all its transactions ahead of a newly available component. func (v *transactionVerifier) verifyTransactions(ctx context.Context, txs []*solana.Transaction) (int, error) { if len(txs) == 0 { return -1, ctx.Err() } window := min(v.workers, len(txs)) + jobGroups := v.jobGroups + // Keep short components responsive and enough independent jobs to supply + // every worker. This is a ready-work threshold, never a batching timer. + if len(txs) < 2*v.workers*v.batchTarget*jobGroups { + jobGroups = 1 + } + jobCapacity := v.batchTarget * jobGroups completed := make(chan *transactionVerifyJob, window) groups := make([]transactionVerifyJob, window) - errs := make([]error, window*v.batchTarget) + errs := make([]error, window*jobCapacity) free := make([]*transactionVerifyJob, window) for i := range groups { - groups[i].errs = errs[i*v.batchTarget : (i+1)*v.batchTarget] + groups[i].ctx = ctx + groups[i].errs = errs[i*jobCapacity : (i+1)*jobCapacity] groups[i].done = completed free[i] = &groups[i] } @@ -203,7 +239,10 @@ func (v *transactionVerifier) verifyTransactions(ctx context.Context, txs []*sol if !stopped && pending == nil && nextIndex < len(txs) && len(free) > 0 { pending = free[len(free)-1] free = free[:len(free)-1] - end := transactionVerifyGroupEnd(txs, nextIndex, v.batchTarget) + end := nextIndex + for group := 0; group < jobGroups && end < len(txs); group++ { + end = transactionVerifyGroupEnd(txs, end, v.batchTarget) + } pending.start = nextIndex pending.txs = txs[nextIndex:end] pending.errs = pending.errs[:end-nextIndex] diff --git a/pkg/turbine/transaction_verifier_test.go b/pkg/turbine/transaction_verifier_test.go index 602fa704..dbb73e85 100644 --- a/pkg/turbine/transaction_verifier_test.go +++ b/pkg/turbine/transaction_verifier_test.go @@ -245,7 +245,7 @@ func TestTransactionVerifierLargeRequestDoesNotQueuePastSmallRequest(t *testing. releaseOnce.Do(func() { close(release) }) _, err = smallRequest.wait() require.NoError(t, err) - require.Equal(t, int32(8), callsBeforeSmall.Load(), "large request may only stay one group ahead") + require.Equal(t, int32(v.batchTarget*v.jobGroups), callsBeforeSmall.Load(), "large request may only stay one job ahead") _, err = largeRequest.wait() require.NoError(t, err) require.Equal(t, int32(800), largeCalls.Load()) @@ -425,23 +425,29 @@ func TestTransactionVerifierKeepsMultisignatureTransactionsIntactAcrossTargets(t txs[i] = tx } for _, target := range []int{4, 8} { - t.Run(fmt.Sprintf("target=%d", target), func(t *testing.T) { - v := newTransactionVerifierWithBatchTarget(2, 16, target, nil) - defer v.closeAndWait() - r, err := v.submitTransactions(context.Background(), txs) - require.NoError(t, err) - _, err = r.wait() - require.NoError(t, err) + for _, groups := range []int{1, 4, 8} { + t.Run(fmt.Sprintf("target=%d/groups=%d", target, groups), func(t *testing.T) { + v := newTransactionVerifierWithJobGroups(2, 16, target, groups, nil) + defer v.closeAndWait() + var large []*solana.Transaction + for range 40 { + large = append(large, txs...) + } + r, err := v.submitTransactions(context.Background(), large) + require.NoError(t, err) + _, err = r.wait() + require.NoError(t, err) - // Corrupt a non-first signer after an oversized (nine-signature) - // transaction. Results must still map to the original tx index. - txs[6].Signatures[1][11] ^= 0x20 - r, err = v.submitTransactions(context.Background(), txs) - require.NoError(t, err) - index, err := r.wait() - require.ErrorContains(t, err, "invalid signature") - require.Equal(t, 6, index) - txs[6].Signatures[1][11] ^= 0x20 - }) + // Corrupt a non-first signer after an oversized (nine-signature) + // transaction. Results must still map to the original tx index. + txs[6].Signatures[1][11] ^= 0x20 + r, err = v.submitTransactions(context.Background(), large) + require.NoError(t, err) + index, err := r.wait() + require.ErrorContains(t, err, "invalid signature") + require.Equal(t, 6, index) + txs[6].Signatures[1][11] ^= 0x20 + }) + } } } From f35a8517ed52d8c09632eba42eb8147266848c52 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 02:07:16 -0500 Subject: [PATCH 06/22] Make streaming tests independent of FEC acceleration --- pkg/turbine/entry_prefetch_benchmark_test.go | 10 ++-- pkg/turbine/generate.go | 10 ++-- .../generated_component_boundary_test.go | 47 +++++++++++++++++++ 3 files changed, 59 insertions(+), 8 deletions(-) create mode 100644 pkg/turbine/generated_component_boundary_test.go diff --git a/pkg/turbine/entry_prefetch_benchmark_test.go b/pkg/turbine/entry_prefetch_benchmark_test.go index b8279928..9b494b73 100644 --- a/pkg/turbine/entry_prefetch_benchmark_test.go +++ b/pkg/turbine/entry_prefetch_benchmark_test.go @@ -84,15 +84,14 @@ func makeAssemblyFlowFixture(tb testing.TB, source *block.Block) assemblyFlowFix if err != nil { tb.Fatal(err) } - generated, dataEnd, codeEnd, err := generator.makeShredsFromData(leader, raw, last, chained, nextData, nextCode) + packets, finalRoot, dataEnd, codeEnd, err := generator.MakeShredsFromData(leader, raw, last, chained, nextData, nextCode) if err != nil { tb.Fatal(err) } - chained, nextData, nextCode = generated.chainedMerkleRoot, dataEnd, codeEnd - roots = append(roots, generated.fecSetRoots...) + chained, nextData, nextCode = finalRoot, dataEnd, codeEnd var data []*Shred var authenticatedRoots []solana.Hash - for _, packet := range generated.packets { + for _, packet := range packets { shred, err := ParseShred(packet) if err != nil { tb.Fatal(err) @@ -107,6 +106,9 @@ func makeAssemblyFlowFixture(tb testing.TB, source *block.Block) assemblyFlowFix if err != nil { tb.Fatal(err) } + if shred.Index == shred.FECSetIndex { + roots = append(roots, root) + } authenticatedRoots = append(authenticatedRoots, root) data = append(data, shred) } diff --git a/pkg/turbine/generate.go b/pkg/turbine/generate.go index 14626e21..b4c58548 100644 --- a/pkg/turbine/generate.go +++ b/pkg/turbine/generate.go @@ -96,10 +96,11 @@ func (g *ShredGenerator) MakeShredsFromData( codeIndex := nextCodeIndex chainedRoot := chainedMerkleRoot + // DATA_COMPLETE ends the serialized component, which may span FEC sets. for len(unsignedData) >= unsignedBatch { batch := unsignedData[:unsignedBatch] unsignedData = unsignedData[unsignedBatch:] - batchPackets, root, err := g.makeFECBatch(leader, batch, unsignedCap, proofSize, false, parentOffset, flags, false, chainedRoot, dataIndex, codeIndex) + batchPackets, root, err := g.makeFECBatch(leader, batch, unsignedCap, proofSize, false, parentOffset, flags, len(unsignedData) == 0 && len(signedData) == 0, false, chainedRoot, dataIndex, codeIndex) if err != nil { return nil, solana.Hash{}, dataIndex, codeIndex, err } @@ -110,7 +111,7 @@ func (g *ShredGenerator) MakeShredsFromData( } if len(unsignedData) > 0 || (len(packets) == 0 && !isLastInSlot) { - batchPackets, root, err := g.makeFECBatch(leader, unsignedData, unsignedCap, proofSize, false, parentOffset, flags, false, chainedRoot, dataIndex, codeIndex) + batchPackets, root, err := g.makeFECBatch(leader, unsignedData, unsignedCap, proofSize, false, parentOffset, flags, len(signedData) == 0, false, chainedRoot, dataIndex, codeIndex) if err != nil { return nil, solana.Hash{}, dataIndex, codeIndex, err } @@ -121,7 +122,7 @@ func (g *ShredGenerator) MakeShredsFromData( } if len(signedData) > 0 || (len(packets) == 0 && isLastInSlot) { - batchPackets, root, err := g.makeFECBatch(leader, signedData, signedCap, proofSize, true, parentOffset, flags, isLastInSlot, chainedRoot, dataIndex, codeIndex) + batchPackets, root, err := g.makeFECBatch(leader, signedData, signedCap, proofSize, true, parentOffset, flags, true, isLastInSlot, chainedRoot, dataIndex, codeIndex) if err != nil { return nil, solana.Hash{}, dataIndex, codeIndex, err } @@ -142,6 +143,7 @@ func (g *ShredGenerator) makeFECBatch( resigned bool, parentOffset uint16, flags byte, + dataComplete bool, isLastInSlot bool, chainedMerkleRoot solana.Hash, dataIndex uint32, @@ -196,7 +198,7 @@ func (g *ShredGenerator) makeFECBatch( dataPackets[i][dataFlagsOffset] |= shredFlagLastShredInSlot break } - } else if len(dataPackets) > 0 { + } else if dataComplete && len(dataPackets) > 0 { dataPackets[len(dataPackets)-1][dataFlagsOffset] |= shredFlagDataComplete } diff --git a/pkg/turbine/generated_component_boundary_test.go b/pkg/turbine/generated_component_boundary_test.go new file mode 100644 index 00000000..87184308 --- /dev/null +++ b/pkg/turbine/generated_component_boundary_test.go @@ -0,0 +1,47 @@ +package turbine + +import ( + "bytes" + "fmt" + "testing" + + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +// Exercise exact FEC boundaries and the smaller resigned final-set capacity. +// The receiver must see one DATA_COMPLETE per serialized component, regardless +// of how many FEC sets carry it, with proofs authenticating the final flags. +func TestGeneratedComponentHasOneCompletionBoundary(t *testing.T) { + unsigned := dataShredsPerFECBlock * dataCapacity(proofEntriesFor32x32, false) + signed := dataShredsPerFECBlock * dataCapacity(proofEntriesFor32x32, true) + for _, last := range []bool{false, true} { + for _, size := range []int{0, 1, signed, signed + 1, unsigned, unsigned + 1, 2 * unsigned, 2*unsigned + signed} { + t.Run(fmt.Sprintf("last=%t/bytes=%d", last, size), func(t *testing.T) { + leader := testShredLeader(t) + gen := ShredGenerator{Slot: 100, ParentSlot: 99, Version: 7} + payload := bytes.Repeat([]byte{0x5a}, size) + packets, _, nextData, _, err := gen.MakeShredsFromData(leader, payload, last, solana.Hash{}, 0, 0) + require.NoError(t, err) + var decoded []byte + var completed int + for _, packet := range packets { + shred, err := ParseShred(packet) + require.NoError(t, err) + require.NoError(t, shred.VerifySignature(leader.PublicKey())) + if shred.Type != ShredTypeData { + continue + } + decoded = append(decoded, shred.Data...) + if shred.DataComplete() { + completed++ + require.Equal(t, nextData-1, shred.Index) + } + require.Equal(t, last && shred.Index == nextData-1, shred.LastInSlot()) + } + require.Equal(t, 1, completed) + require.True(t, bytes.Equal(payload, decoded)) + }) + } + } +} From 3f0da7371c7de6255898bb7521b6c2592e97e8f1 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 02:07:16 -0500 Subject: [PATCH 07/22] Allocate each prepared component transaction view once --- pkg/turbine/entry_batch_transactions_test.go | 37 ++++++++++++++++++++ pkg/turbine/entry_prefetch.go | 8 ++++- 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 pkg/turbine/entry_batch_transactions_test.go diff --git a/pkg/turbine/entry_batch_transactions_test.go b/pkg/turbine/entry_batch_transactions_test.go new file mode 100644 index 00000000..28329ab0 --- /dev/null +++ b/pkg/turbine/entry_batch_transactions_test.go @@ -0,0 +1,37 @@ +package turbine + +import ( + "fmt" + "testing" + + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func TestEntryBatchTransactionsPreservesEntryOwnershipAndOrder(t *testing.T) { + entries := []Entry{{}, {Txns: make([]solana.Transaction, 3)}, {}, {Txns: make([]solana.Transaction, 2)}} + txs := entryBatchTransactions(entries) + require.Len(t, txs, 5) + for i := 0; i < 3; i++ { + require.Same(t, &entries[1].Txns[i], txs[i]) + } + for i := 0; i < 2; i++ { + require.Same(t, &entries[3].Txns[i], txs[3+i]) + } + require.Empty(t, entryBatchTransactions(nil)) + require.Empty(t, entryBatchTransactions([]Entry{{}, {}})) +} + +var entryBatchBenchmarkSink []*solana.Transaction + +func BenchmarkEntryBatchTransactions(b *testing.B) { + for _, count := range []int{4, 49, 269, 33760} { + b.Run(fmt.Sprintf("tx_%d", count), func(b *testing.B) { + entries := []Entry{{Txns: make([]solana.Transaction, count/2)}, {}, {Txns: make([]solana.Transaction, count-count/2)}} + b.ReportAllocs() + for b.Loop() { + entryBatchBenchmarkSink = entryBatchTransactions(entries) + } + }) + } +} diff --git a/pkg/turbine/entry_prefetch.go b/pkg/turbine/entry_prefetch.go index 5816b1e8..05171918 100644 --- a/pkg/turbine/entry_prefetch.go +++ b/pkg/turbine/entry_prefetch.go @@ -187,7 +187,13 @@ func (p *entryPrefetchPool) run() { } func entryBatchTransactions(entries []Entry) []*solana.Transaction { - var txs []*solana.Transaction + count := 0 + for i := range entries { + count += len(entries[i].Txns) + } + // Entries are already decoded: size this pointer view once instead of + // repeatedly reallocating and copying it while preparing each component. + txs := make([]*solana.Transaction, 0, count) for i := range entries { for j := range entries[i].Txns { txs = append(txs, &entries[i].Txns[j]) From 0f8e8b09d518014c85cb1af2debd337afddb822d Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 02:07:16 -0500 Subject: [PATCH 08/22] Document standalone streaming validation and live trial scope --- .../streaming-pr-review/2026-09-13/README.md | 99 +++++++++++++++++++ .../2026-09-13/binaries-sha256.txt | 2 + .../2026-09-13/local-race.txt | 7 ++ .../2026-09-13/local-root-fuzz.txt | 7 ++ .../2026-09-13/native-assembly.txt | 13 +++ .../2026-09-13/native-build.txt | 0 .../2026-09-13/native-race.txt | 7 ++ .../2026-09-13/native-vet.txt | 0 .../2026-09-13/pointer-after.txt | 18 ++++ .../2026-09-13/pointer-before.txt | 18 ++++ .../2026-09-13/run-native.sh | 19 ++++ .../2026-09-13/service-after.txt | 2 + .../2026-09-13/service-before.txt | 2 + .../2026-09-13/source-sha256.txt | 1 + docs/transaction_sigverify_streaming.md | 4 + 15 files changed, 199 insertions(+) create mode 100644 docs/results/streaming-pr-review/2026-09-13/README.md create mode 100644 docs/results/streaming-pr-review/2026-09-13/binaries-sha256.txt create mode 100644 docs/results/streaming-pr-review/2026-09-13/local-race.txt create mode 100644 docs/results/streaming-pr-review/2026-09-13/local-root-fuzz.txt create mode 100644 docs/results/streaming-pr-review/2026-09-13/native-assembly.txt create mode 100644 docs/results/streaming-pr-review/2026-09-13/native-build.txt create mode 100644 docs/results/streaming-pr-review/2026-09-13/native-race.txt create mode 100644 docs/results/streaming-pr-review/2026-09-13/native-vet.txt create mode 100644 docs/results/streaming-pr-review/2026-09-13/pointer-after.txt create mode 100644 docs/results/streaming-pr-review/2026-09-13/pointer-before.txt create mode 100644 docs/results/streaming-pr-review/2026-09-13/run-native.sh create mode 100644 docs/results/streaming-pr-review/2026-09-13/service-after.txt create mode 100644 docs/results/streaming-pr-review/2026-09-13/service-before.txt create mode 100644 docs/results/streaming-pr-review/2026-09-13/source-sha256.txt diff --git a/docs/results/streaming-pr-review/2026-09-13/README.md b/docs/results/streaming-pr-review/2026-09-13/README.md new file mode 100644 index 00000000..2ed33fb7 --- /dev/null +++ b/docs/results/streaming-pr-review/2026-09-13/README.md @@ -0,0 +1,99 @@ +# Standalone streaming verification PR review + +This branch extracts the receiver, transaction pool, Narya update and completion +work from trial commit `15ad18458dea3b7ff89ea21ef3bae7df04b323de` onto +`alpenglow-dev` at `33dde405`. The dependency planner is already merged in that +base. It does not include the FEC acceleration, producer batching, or subsequent +leader-packing experiments from the combined trial. + +The only shared prerequisite with FEC PR #259 is the small generator correctness +fix that places DATA_COMPLETE at the end of the serialized component, rather +than at every intermediate FEC boundary. Boundary tests cover exact unsigned +and resigned capacities, multiple sets, signature proofs, and round-trip bytes. +The assembly fixture uses the public generation API and derives roots from +packet proofs; fixture generation remains outside timing. The root cache keeps +alpenglow-dev's existing private shredSigCache type. + +## Final allocation improvement + +Early preparation knows the decoded transaction count. Allocate its pointer view +once, preserving order and pointers into the original entry storage. This avoids +repeated slice growth without copying transaction objects or changing ownership. + +Apple M4 Pro, Go 1.26.4, three 200 ms microbenchmark samples per case: + +| Transactions/component | Before bytes / allocations | After bytes / allocations | +|---|---:|---:| +| 4 | 56 / 3 | 32 / 1 | +| 49 | 1,016 / 7 | 416 / 1 | +| 269 | 4,472 / 9 | 2,304 / 1 | +| 33,760 | approximately 1,301,624 / 23 | 270,336 / 1 | + +These measure pointer-view construction alone. The 33,760 case is a helper +stress case, not a typical prefetched component. Concurrent local validation +makes the timing samples unsuitable for an end-to-end speedup claim; allocation +counts are the result of interest. Raw output is in pointer-before.txt and +pointer-after.txt. The before version is the committed trial's append-growth +implementation; this is not an alpenglow-dev comparison. + +## Earlier performance evidence and live validation + +The [streaming report](../../sigverify-streaming/2026-09-12-zen5/README.md), +[direct byte comparison report](../../sigverify-direct-cache/2026-09-12-zen5/README.md), +and [completion follow-up](../../completion-followup/2026-09-12-zen5/README.md) +retain their original raw evidence, source hashes, and baseline definitions. +Those runs used the combined trial branch. They are not a fresh comparison +against this standalone branch's current alpenglow-dev base. + +A five-minute live Alpenglow sample on September 13, 04:03:06–04:08:06 UTC, used +combined trial `15ad1845` before the later leader-packing changes. On the Ryzen 7 +9700X with GOMAXPROCS=8, two signature workers and target eight: + +- 1,241 replayed blocks, 5,700,024 transactions; 118 blocks had at least 20,000 + transactions, with median and maximum transaction count 33,760. +- Those large blocks had median full replay 78.11 ms, p95 98.67 ms; median + final-shred-to-ready 7.53 ms, p95 34.74 ms. The two stages are distinct. +- 3,449,937 of 3,844,618 large-block transactions (89.7%) were signature-verified + before full assembly. +- RPC-tip checks stayed current; last-vote lag was 1–3 slots, median two. No + logged runtime errors, bankhash mismatches, or internal verifier fallbacks + were observed in the sample. + +This was an observational trial, not a paired whole-validator speed comparison. +The live binary also contained the FEC/producer work excluded from this PR. + +## Fresh standalone Zen 5 check + +The source archive identified by source-sha256.txt contains the final production +code and tests. Documentation-only additions followed that archive. Run details +are in run-native.sh; native-assembly.txt contains all measurements. Eight +physical cores (CPUs 0–7), GOMAXPROCS=8, Go 1.26.4, native Narya r51, nice 10; +the existing validator remained active with the same PID throughout. + +For 33,760 distinct generated one-signature transactions, two workers, target +eight, three timed blocks per condition: + +| Wire bytes/tx | Tip final-shred-to-ready, overlap off → on | Tip total arrival-to-ready, off → on | Catch-up total, off → on | +|---|---:|---:|---:| +| 228 | 85.72 → 1.831 ms | 286.93 → 202.94 ms | 85.31 → 86.39 ms | +| 1,232 | 99.89 → 6.504 ms | 302.61 → 207.77 ms | 111.97 → 117.38 ms | + +Residual values are within-run medians; total times are Go benchmark means. +This small run validates the extracted implementation and illustrates overlap; +it is not a stable speedup estimate. Both sides use this PR's new batching, +Narya and completion code. It is explicitly **not old alpenglow-dev versus PR**. +Catch-up offers all shreds immediately and showed modest extra overlap overhead +in this run. The default favors latency at the tip, with no mode-switch timer. + +The tip workload distributes complete component bursts over 200 ms, using a +60 KiB transaction-byte component budget plus actual entry overhead/FEC padding. +Collection took 200.9–201.4 ms. These are synthetic arrivals, not a network trace. +Timed work includes assembly, decoding, Merkle/block identity, signatures and +publication; it excludes packet parsing, shred authentication, loss/recovery, +transaction execution, PoH, disk I/O and commit. Assertions checked exact +signature coverage, block metadata, and zero internal native fallbacks. + +Native and local race suites passed for Turbine, replay, sigverify, txverify, +block, statsd and node; vet and complete Mithril builds passed on both machines. +Raw race logs are included. Root-selection fuzzing compared the new selector +against the original sorted algorithm; local-root-fuzz.txt records the result. diff --git a/docs/results/streaming-pr-review/2026-09-13/binaries-sha256.txt b/docs/results/streaming-pr-review/2026-09-13/binaries-sha256.txt new file mode 100644 index 00000000..0bc09ac1 --- /dev/null +++ b/docs/results/streaming-pr-review/2026-09-13/binaries-sha256.txt @@ -0,0 +1,2 @@ +74a73afaccbad6587342c7c3e010a95bce33398882d7333324e432938eaebefe review-results/mithril +a613bc9efc39128dc0a051a80cccd9f32223834bcbd2bf40b4f7e8e5983c1f8e review-results/turbine.test diff --git a/docs/results/streaming-pr-review/2026-09-13/local-race.txt b/docs/results/streaming-pr-review/2026-09-13/local-race.txt new file mode 100644 index 00000000..e2c01328 --- /dev/null +++ b/docs/results/streaming-pr-review/2026-09-13/local-race.txt @@ -0,0 +1,7 @@ +ok github.com/Overclock-Validator/mithril/pkg/turbine 10.156s +ok github.com/Overclock-Validator/mithril/pkg/replay 4.316s +ok github.com/Overclock-Validator/mithril/pkg/sigverify 23.094s +ok github.com/Overclock-Validator/mithril/pkg/txverify 1.495s +ok github.com/Overclock-Validator/mithril/pkg/block 2.056s +ok github.com/Overclock-Validator/mithril/pkg/statsd 2.038s +ok github.com/Overclock-Validator/mithril/cmd/mithril/node 2.900s diff --git a/docs/results/streaming-pr-review/2026-09-13/local-root-fuzz.txt b/docs/results/streaming-pr-review/2026-09-13/local-root-fuzz.txt new file mode 100644 index 00000000..8acccc98 --- /dev/null +++ b/docs/results/streaming-pr-review/2026-09-13/local-root-fuzz.txt @@ -0,0 +1,7 @@ +fuzz: elapsed: 0s, gathering baseline coverage: 0/79 completed +fuzz: elapsed: 0s, gathering baseline coverage: 79/79 completed, now fuzzing with 4 workers +fuzz: elapsed: 3s, execs: 4734 (1577/sec), new interesting: 1 (total: 80) +fuzz: elapsed: 6s, execs: 4734 (0/sec), new interesting: 1 (total: 80) +fuzz: elapsed: 6s, execs: 4734 (0/sec), new interesting: 1 (total: 80) +PASS +ok github.com/Overclock-Validator/mithril/pkg/turbine 6.468s diff --git a/docs/results/streaming-pr-review/2026-09-13/native-assembly.txt b/docs/results/streaming-pr-review/2026-09-13/native-assembly.txt new file mode 100644 index 00000000..08ec62c6 --- /dev/null +++ b/docs/results/streaming-pr-review/2026-09-13/native-assembly.txt @@ -0,0 +1,13 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_off-8 3 85306561 ns/op 1.964 avg_cpu_cores 1.419 collection_p50-ms 1.453 collection_p95-ms 4.705 completion_parse_p50-ms 5.428 completion_parse_p95-ms 75.24 completion_sigverify_p50-ms 81.81 completion_sigverify_p95-ms 128.0 components/block 167.6 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 82.44 full_to_ready_p50-ms 88.44 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32456858 B/op 207227 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/catchup/overlap_on-8 3 86393257 ns/op 2.035 avg_cpu_cores 2.079 collection_p50-ms 2.115 collection_p95-ms 4.682 completion_parse_p50-ms 5.162 completion_parse_p95-ms 77.92 completion_sigverify_p50-ms 78.55 completion_sigverify_p95-ms 128.0 components/block 175.8 cpu-ms/block 8128 data_shreds/block 358.7 early_verified_tx/block 84.92 full_to_ready_p50-ms 85.60 full_to_ready_p95-ms 7.996 mean_width 0.0007720 preparation_wait_p50-ms 0.007895 preparation_wait_p95-ms 33760 tx/block 34990840 B/op 207362 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_off-8 3 286934835 ns/op 0.6117 avg_cpu_cores 200.9 collection_p50-ms 201.0 collection_p95-ms 3.815 completion_parse_p50-ms 4.460 completion_parse_p95-ms 80.32 completion_sigverify_p50-ms 82.17 completion_sigverify_p95-ms 128.0 components/block 175.5 cpu-ms/block 8128 data_shreds/block 0 early_verified_tx/block 85.72 full_to_ready_p50-ms 87.87 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 32411189 B/op 207227 allocs/op +BenchmarkEntryPrefetchAssembly/generated_228B/workers_2/target_8/tip_200ms/overlap_on-8 3 202937835 ns/op 0.8674 avg_cpu_cores 200.9 collection_p50-ms 201.1 collection_p95-ms 0 completion_parse_p50-ms 0 completion_parse_p95-ms 0.02486 completion_sigverify_p50-ms 0.04140 completion_sigverify_p95-ms 128.0 components/block 176.0 cpu-ms/block 8128 data_shreds/block 33760 early_verified_tx/block 1.831 full_to_ready_p50-ms 2.388 full_to_ready_p95-ms 7.912 mean_width 0.02706 preparation_wait_p50-ms 0.03109 preparation_wait_p95-ms 33760 tx/block 33110861 B/op 209062 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_off-8 3 111966851 ns/op 1.906 avg_cpu_cores 7.957 collection_p50-ms 8.498 collection_p95-ms 4.311 completion_parse_p50-ms 5.824 completion_parse_p95-ms 87.03 completion_sigverify_p50-ms 89.17 completion_sigverify_p95-ms 691.0 components/block 213.4 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 102.9 full_to_ready_p50-ms 106.7 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 113167597 B/op 227179 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/catchup/overlap_on-8 3 117376486 ns/op 2.023 avg_cpu_cores 11.83 collection_p50-ms 12.91 collection_p95-ms 4.231 completion_parse_p50-ms 5.194 completion_parse_p95-ms 89.59 completion_sigverify_p50-ms 89.71 completion_sigverify_p95-ms 691.0 components/block 237.4 cpu-ms/block 44160 data_shreds/block 2777 early_verified_tx/block 103.6 full_to_ready_p50-ms 108.8 full_to_ready_p95-ms 7.906 mean_width 0.009261 preparation_wait_p50-ms 0.01254 preparation_wait_p95-ms 33760 tx/block 115776725 B/op 228075 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_off-8 3 302607710 ns/op 0.6960 avg_cpu_cores 201.4 collection_p50-ms 201.5 collection_p95-ms 4.279 completion_parse_p50-ms 6.031 completion_parse_p95-ms 84.70 completion_sigverify_p50-ms 89.83 completion_sigverify_p95-ms 691.0 components/block 210.6 cpu-ms/block 44160 data_shreds/block 0 early_verified_tx/block 99.89 full_to_ready_p50-ms 106.4 full_to_ready_p95-ms 8.000 mean_width 0 preparation_wait_p50-ms 0 preparation_wait_p95-ms 33760 tx/block 113304938 B/op 227184 allocs/op +BenchmarkEntryPrefetchAssembly/generated_1232B/workers_2/target_8/tip_200ms/overlap_on-8 3 207771037 ns/op 1.175 avg_cpu_cores 200.9 collection_p50-ms 201.2 collection_p95-ms 0.01120 completion_parse_p50-ms 0.01515 completion_parse_p95-ms 0.3232 completion_sigverify_p50-ms 0.3461 completion_sigverify_p95-ms 691.0 components/block 244.1 cpu-ms/block 44160 data_shreds/block 33728 early_verified_tx/block 6.504 full_to_ready_p50-ms 6.869 full_to_ready_p95-ms 7.001 mean_width 0.1116 preparation_wait_p50-ms 0.1148 preparation_wait_p95-ms 33760 tx/block 115064322 B/op 236934 allocs/op +PASS diff --git a/docs/results/streaming-pr-review/2026-09-13/native-build.txt b/docs/results/streaming-pr-review/2026-09-13/native-build.txt new file mode 100644 index 00000000..e69de29b diff --git a/docs/results/streaming-pr-review/2026-09-13/native-race.txt b/docs/results/streaming-pr-review/2026-09-13/native-race.txt new file mode 100644 index 00000000..c1383a85 --- /dev/null +++ b/docs/results/streaming-pr-review/2026-09-13/native-race.txt @@ -0,0 +1,7 @@ +ok github.com/Overclock-Validator/mithril/pkg/turbine 6.090s +ok github.com/Overclock-Validator/mithril/pkg/replay 2.053s +ok github.com/Overclock-Validator/mithril/pkg/sigverify 19.684s +ok github.com/Overclock-Validator/mithril/pkg/txverify 1.014s +ok github.com/Overclock-Validator/mithril/pkg/block 1.139s +ok github.com/Overclock-Validator/mithril/pkg/statsd 1.016s +ok github.com/Overclock-Validator/mithril/cmd/mithril/node 1.149s diff --git a/docs/results/streaming-pr-review/2026-09-13/native-vet.txt b/docs/results/streaming-pr-review/2026-09-13/native-vet.txt new file mode 100644 index 00000000..e69de29b diff --git a/docs/results/streaming-pr-review/2026-09-13/pointer-after.txt b/docs/results/streaming-pr-review/2026-09-13/pointer-after.txt new file mode 100644 index 00000000..58ad062d --- /dev/null +++ b/docs/results/streaming-pr-review/2026-09-13/pointer-after.txt @@ -0,0 +1,18 @@ +goos: darwin +goarch: arm64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: Apple M4 Pro +BenchmarkEntryBatchTransactions/tx_4-12 8466316 31.27 ns/op 32 B/op 1 allocs/op +BenchmarkEntryBatchTransactions/tx_4-12 10608848 22.30 ns/op 32 B/op 1 allocs/op +BenchmarkEntryBatchTransactions/tx_4-12 12435661 27.76 ns/op 32 B/op 1 allocs/op +BenchmarkEntryBatchTransactions/tx_49-12 1330062 164.0 ns/op 416 B/op 1 allocs/op +BenchmarkEntryBatchTransactions/tx_49-12 1898496 118.3 ns/op 416 B/op 1 allocs/op +BenchmarkEntryBatchTransactions/tx_49-12 1431538 229.4 ns/op 416 B/op 1 allocs/op +BenchmarkEntryBatchTransactions/tx_269-12 375474 781.9 ns/op 2304 B/op 1 allocs/op +BenchmarkEntryBatchTransactions/tx_269-12 463562 532.6 ns/op 2304 B/op 1 allocs/op +BenchmarkEntryBatchTransactions/tx_269-12 549596 454.2 ns/op 2304 B/op 1 allocs/op +BenchmarkEntryBatchTransactions/tx_33760-12 5101 42209 ns/op 270336 B/op 1 allocs/op +BenchmarkEntryBatchTransactions/tx_33760-12 5328 44019 ns/op 270336 B/op 1 allocs/op +BenchmarkEntryBatchTransactions/tx_33760-12 4857 46700 ns/op 270336 B/op 1 allocs/op +PASS +ok github.com/Overclock-Validator/mithril/pkg/turbine 3.425s diff --git a/docs/results/streaming-pr-review/2026-09-13/pointer-before.txt b/docs/results/streaming-pr-review/2026-09-13/pointer-before.txt new file mode 100644 index 00000000..74a4218d --- /dev/null +++ b/docs/results/streaming-pr-review/2026-09-13/pointer-before.txt @@ -0,0 +1,18 @@ +goos: darwin +goarch: arm64 +pkg: github.com/Overclock-Validator/mithril/pkg/turbine +cpu: Apple M4 Pro +BenchmarkEntryBatchTransactions/tx_4-12 5438229 44.07 ns/op 56 B/op 3 allocs/op +BenchmarkEntryBatchTransactions/tx_4-12 5468454 44.47 ns/op 56 B/op 3 allocs/op +BenchmarkEntryBatchTransactions/tx_4-12 5464107 43.92 ns/op 56 B/op 3 allocs/op +BenchmarkEntryBatchTransactions/tx_49-12 938340 278.3 ns/op 1016 B/op 7 allocs/op +BenchmarkEntryBatchTransactions/tx_49-12 866556 282.0 ns/op 1016 B/op 7 allocs/op +BenchmarkEntryBatchTransactions/tx_49-12 867691 277.3 ns/op 1016 B/op 7 allocs/op +BenchmarkEntryBatchTransactions/tx_269-12 243710 969.2 ns/op 4472 B/op 9 allocs/op +BenchmarkEntryBatchTransactions/tx_269-12 239790 974.8 ns/op 4472 B/op 9 allocs/op +BenchmarkEntryBatchTransactions/tx_269-12 251006 977.0 ns/op 4472 B/op 9 allocs/op +BenchmarkEntryBatchTransactions/tx_33760-12 1576 145701 ns/op 1301628 B/op 23 allocs/op +BenchmarkEntryBatchTransactions/tx_33760-12 1692 145071 ns/op 1301624 B/op 23 allocs/op +BenchmarkEntryBatchTransactions/tx_33760-12 1538 148765 ns/op 1301624 B/op 23 allocs/op +PASS +ok github.com/Overclock-Validator/mithril/pkg/turbine 3.467s diff --git a/docs/results/streaming-pr-review/2026-09-13/run-native.sh b/docs/results/streaming-pr-review/2026-09-13/run-native.sh new file mode 100644 index 00000000..003c6f06 --- /dev/null +++ b/docs/results/streaming-pr-review/2026-09-13/run-native.sh @@ -0,0 +1,19 @@ +set -eu +review_dir=/srv/mithril-streaming-pr-review-20260913 +mkdir "$review_dir" +tar -xzf /tmp/mithril-streaming-pr-review-20260913.tar.gz -C "$review_dir" +cd "$review_dir" +export PATH=/usr/local/go/bin:$PATH +export GOMAXPROCS=8 +mkdir review-results +sha256sum /tmp/mithril-streaming-pr-review-20260913.tar.gz > review-results/source-sha256.txt +systemctl show mithril-alpenglow-pr259.service -p MainPID -p ActiveState > review-results/service-before.txt +nice -n 10 go test -race ./pkg/turbine ./pkg/replay ./pkg/sigverify ./pkg/txverify ./pkg/block ./pkg/statsd ./cmd/mithril/node > review-results/native-race.txt 2>&1 +nice -n 10 go vet ./pkg/turbine ./pkg/replay ./pkg/sigverify ./pkg/txverify ./pkg/block ./pkg/statsd ./cmd/mithril/node > review-results/native-vet.txt 2>&1 +nice -n 10 go build -o review-results/mithril ./cmd/mithril > review-results/native-build.txt 2>&1 +nice -n 10 go test -c -o review-results/turbine.test ./pkg/turbine +MITHRIL_SIGVERIFY_FLOW_BACKEND=r51 nice -n 10 taskset -c 0-7 review-results/turbine.test -test.run='^$' -test.bench='^BenchmarkEntryPrefetchAssembly$/^generated_.*$/^workers_2$/^target_8$/.*' -test.benchtime=3x -test.count=1 > review-results/native-assembly.txt 2>&1 +systemctl show mithril-alpenglow-pr259.service -p MainPID -p ActiveState > review-results/service-after.txt +sha256sum review-results/mithril review-results/turbine.test > review-results/binaries-sha256.txt +cat review-results/native-race.txt +cat review-results/native-assembly.txt diff --git a/docs/results/streaming-pr-review/2026-09-13/service-after.txt b/docs/results/streaming-pr-review/2026-09-13/service-after.txt new file mode 100644 index 00000000..bece5e70 --- /dev/null +++ b/docs/results/streaming-pr-review/2026-09-13/service-after.txt @@ -0,0 +1,2 @@ +ActiveState=active +MainPID=3846480 diff --git a/docs/results/streaming-pr-review/2026-09-13/service-before.txt b/docs/results/streaming-pr-review/2026-09-13/service-before.txt new file mode 100644 index 00000000..bece5e70 --- /dev/null +++ b/docs/results/streaming-pr-review/2026-09-13/service-before.txt @@ -0,0 +1,2 @@ +ActiveState=active +MainPID=3846480 diff --git a/docs/results/streaming-pr-review/2026-09-13/source-sha256.txt b/docs/results/streaming-pr-review/2026-09-13/source-sha256.txt new file mode 100644 index 00000000..91ac36a0 --- /dev/null +++ b/docs/results/streaming-pr-review/2026-09-13/source-sha256.txt @@ -0,0 +1 @@ +439fbf1b4313cb5aa9c98adb40d9e790346dace262441f5477e0486040e030c9 /tmp/mithril-streaming-pr-review-20260913.tar.gz diff --git a/docs/transaction_sigverify_streaming.md b/docs/transaction_sigverify_streaming.md index 8b555088..3baee788 100644 --- a/docs/transaction_sigverify_streaming.md +++ b/docs/transaction_sigverify_streaming.md @@ -116,3 +116,7 @@ The subsequent [direct cache-comparison report](results/sigverify-direct-cache/2 isolates the removal of redundant component-buffer construction at completion. The [completion follow-up report](results/completion-followup/2026-09-12-zen5/README.md) measures direct ordering, authenticated-root reuse, and the four-vector job policy. + +The [standalone PR review](results/streaming-pr-review/2026-09-13/README.md) +records extraction onto current `alpenglow-dev`, the small shared component-boundary +prerequisite, final allocation improvement, and the scope of the live trial. From 8944527303619095e88fa10e6db081c8a9e176a4 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 02:16:07 -0500 Subject: [PATCH 09/22] Improve leader packing and add near-limit synthetic block tests --- cmd/mithril/node/node.go | 91 +++++---- config.example.toml | 9 + pkg/accounts/mem_accounts.go | 15 +- pkg/accounts/mem_accounts_test.go | 15 ++ pkg/blockprod/bank.go | 52 ++++- pkg/blockprod/leader.go | 37 +++- .../leader_completion_reserve_test.go | 32 +++ pkg/blockprod/leader_processing_test.go | 81 ++++++++ pkg/blockprod/leader_throughput_bench_test.go | 117 +++++++++++ pkg/blockprod/prepared_transaction_test.go | 142 +++++++++++++ pkg/blockprod/readonly_block_bench_test.go | 187 ++++++++++++++++++ .../readonly_block_prepared_bench_test.go | 45 +++++ pkg/blockprod/readonly_load_bench_test.go | 95 +++++++++ pkg/blockprod/scheduler/buffer.go | 63 ++++-- pkg/blockprod/scheduler/buffer_bench_test.go | 45 +++++ pkg/blockprod/scheduler/buffer_order_test.go | 105 ++++++++++ pkg/blockprod/scheduler/config_test.go | 28 +++ pkg/blockprod/scheduler/scheduler.go | 56 ++++-- pkg/blockprod/scheduler/scheduler_test.go | 20 +- pkg/config/config.go | 2 + pkg/costmodel/entry_bytes.go | 7 +- pkg/costmodel/limits.go | 48 ++++- pkg/costmodel/slot_limits_test.go | 88 +++++++++ pkg/costmodel/transaction_cost.go | 15 +- pkg/merkletree/merkletree.go | 44 ++++- pkg/merkletree/root_test.go | 60 ++++++ pkg/metrics/metrics.go | 12 +- pkg/replay/chain_state.go | 11 ++ pkg/replay/transaction_preparation.go | 133 +++++++++++++ pkg/replay/transaction_processing_pure.go | 134 +++++++------ pkg/sealevel/execution_ctx.go | 16 +- pkg/tpu/txfixture/readonly_pair.go | 42 ++++ pkg/tpu/txfixture/readonly_pair_test.go | 68 +++++++ pkg/turbine/entry_hash.go | 9 +- pkg/turbine/entry_hash_bench_test.go | 24 +++ 35 files changed, 1770 insertions(+), 178 deletions(-) create mode 100644 pkg/blockprod/leader_completion_reserve_test.go create mode 100644 pkg/blockprod/leader_processing_test.go create mode 100644 pkg/blockprod/leader_throughput_bench_test.go create mode 100644 pkg/blockprod/prepared_transaction_test.go create mode 100644 pkg/blockprod/readonly_block_bench_test.go create mode 100644 pkg/blockprod/readonly_block_prepared_bench_test.go create mode 100644 pkg/blockprod/readonly_load_bench_test.go create mode 100644 pkg/blockprod/scheduler/buffer_bench_test.go create mode 100644 pkg/blockprod/scheduler/buffer_order_test.go create mode 100644 pkg/blockprod/scheduler/config_test.go create mode 100644 pkg/costmodel/slot_limits_test.go create mode 100644 pkg/merkletree/root_test.go create mode 100644 pkg/replay/transaction_preparation.go create mode 100644 pkg/tpu/txfixture/readonly_pair.go create mode 100644 pkg/tpu/txfixture/readonly_pair_test.go create mode 100644 pkg/turbine/entry_hash_bench_test.go diff --git a/cmd/mithril/node/node.go b/cmd/mithril/node/node.go index e23d9682..a7c95f9e 100644 --- a/cmd/mithril/node/node.go +++ b/cmd/mithril/node/node.go @@ -74,35 +74,37 @@ var ( }, } - bootstrapMode string // "auto", "snapshot", "new-snapshot", "new-incremental", or "accountsdb" - snapshotArchivePath string - incrementalSnapshotFilename string - accountsPath string - scratchDirectory string - rpcEndpoints []string - cluster string // "alpenglow", "mainnet-beta", "testnet", or "devnet" - legacyGenesisHash string // explicit lineage for pre-binding AccountsDB/ledger artifacts - blockSource string // "turbine", "rpc", or "lightbringer" - lightbringerEndpoint string - repairCatchupMaxGapSlots int // Resume gaps up to this fill via turbine repair instead of RPC (0 = off) - repairMaxRequestsPerSecond int // Repair request-rate ceiling override (0 = adaptive default) - blockRPCFallback bool // Allow RPC block fetch when > repairCatchupMaxGapSlots behind (default false: shreds only) - blockMaxRPS int // Rate limit for block fetching - blockMaxInflight int // Max concurrent block fetch workers - blockTipPollIntervalMs int // Tip poll interval in milliseconds - blockTipSafetyMargin int // Don't fetch within N slots of tip - consensusModeFlag string // raw --consensus-mode value (cobra binding) - consensusMode string // resolved: "verifying" (default) or "validator" - alpenglowObserverBindAddr string - alpenglowMaxMessageBytes int64 - alpenglowBLSDST string - validatorIdentityKeypair string - validatorVoteAccountKeypair string - validatorAuthorizedVoterKeypair string - validatorWithdrawerKeypair string - validatorTPUQUICBind string - validatorAdvertisedIP string - validatorSigverifyWorkers int + bootstrapMode string // "auto", "snapshot", "new-snapshot", "new-incremental", or "accountsdb" + snapshotArchivePath string + incrementalSnapshotFilename string + accountsPath string + scratchDirectory string + rpcEndpoints []string + cluster string // "alpenglow", "mainnet-beta", "testnet", or "devnet" + legacyGenesisHash string // explicit lineage for pre-binding AccountsDB/ledger artifacts + blockSource string // "turbine", "rpc", or "lightbringer" + lightbringerEndpoint string + repairCatchupMaxGapSlots int // Resume gaps up to this fill via turbine repair instead of RPC (0 = off) + repairMaxRequestsPerSecond int // Repair request-rate ceiling override (0 = adaptive default) + blockRPCFallback bool // Allow RPC block fetch when > repairCatchupMaxGapSlots behind (default false: shreds only) + blockMaxRPS int // Rate limit for block fetching + blockMaxInflight int // Max concurrent block fetch workers + blockTipPollIntervalMs int // Tip poll interval in milliseconds + blockTipSafetyMargin int // Don't fetch within N slots of tip + consensusModeFlag string // raw --consensus-mode value (cobra binding) + consensusMode string // resolved: "verifying" (default) or "validator" + alpenglowObserverBindAddr string + alpenglowMaxMessageBytes int64 + alpenglowBLSDST string + validatorIdentityKeypair string + validatorVoteAccountKeypair string + validatorAuthorizedVoterKeypair string + validatorWithdrawerKeypair string + validatorTPUQUICBind string + validatorAdvertisedIP string + validatorSigverifyWorkers int + validatorCompletionReserveMs int + validatorMaxBufferedTransactions int // Mode thresholds blockNearTipThreshold int // Enter near-tip when gap <= this @@ -547,6 +549,8 @@ func init() { Run.Flags().StringVar(&validatorTPUQUICBind, "tpu-quic-bind-addr", "", "Validator TPU QUIC listen address (default 0.0.0.0:8004)") Run.Flags().StringVar(&validatorAdvertisedIP, "validator-advertised-ip", "", "Public IP advertised for validator TPU QUIC") Run.Flags().IntVar(&validatorSigverifyWorkers, "tpu-sigverify-workers", 0, "TPU signature verification workers (0 = GOMAXPROCS)") + Run.Flags().IntVar(&validatorCompletionReserveMs, "leader-completion-reserve-ms", 0, "Time reserved for leader finalization and broadcast (0 = 75ms default; tune from measured completion times)") + Run.Flags().IntVar(&validatorMaxBufferedTransactions, "tpu-max-buffered-transactions", 0, "Maximum queued TPU transactions (0 = 131072 default)") // [tuning] section flags Run.Flags().Uint64Var(¶mArenaSizeMB, "param-arena-size-mb", 512, "Size in MB for serialized parameter arena (0 to disable)") @@ -869,6 +873,14 @@ func initConfigAndBindFlags(cmd *cobra.Command) error { } validatorAdvertisedIP = getString("validator-advertised-ip", "validator.advertised_ip") validatorSigverifyWorkers = getInt("tpu-sigverify-workers", "validator.tpu_sigverify_workers") + validatorCompletionReserveMs = getInt("leader-completion-reserve-ms", "validator.block_completion_reserve_ms") + validatorMaxBufferedTransactions = getInt("tpu-max-buffered-transactions", "validator.tpu_max_buffered_transactions") + if validatorMaxBufferedTransactions < 0 { + return fmt.Errorf("TPU maximum buffered transactions must be nonnegative") + } + if validatorCompletionReserveMs < 0 || validatorCompletionReserveMs >= int(blockprod.AlpenglowSlotDuration/time.Millisecond) { + return fmt.Errorf("leader completion reserve must be 0 (default) or between 1 and 199 milliseconds") + } // [block] section blockSource = getString("block-source", "block.source") @@ -2718,7 +2730,9 @@ postBootstrap: defer broadcaster.Close() controller := blockprod.NewController() - topicSink := scheduler.New(controller) + topicSink := scheduler.NewWithConfig(controller, scheduler.Config{ + FeatureSource: replay.ChainTipFeatures, MaxBufferedTransactions: validatorMaxBufferedTransactions, + }) topicSink.Start(ctx) defer topicSink.Stop() tpuCfg := tpu.DefaultConfig() @@ -2765,14 +2779,15 @@ postBootstrap: leaderStop := make(chan struct{}) leaderDone := make(chan struct{}) leaderLoop := blockprod.NewLeaderLoop(blockprod.LeaderLoopConfig{ - Controller: controller, - Identity: solana.PrivateKey(validatorIdentity), - AccountsDb: accountsDb, - Broadcaster: broadcaster, - ShredVersion: uint16(turbineShredVersion), - EpochSchedule: epochSchedule, - AlpenglowClock: true, - SlotDuration: blockprod.AlpenglowSlotDuration, + Controller: controller, + Identity: solana.PrivateKey(validatorIdentity), + AccountsDb: accountsDb, + Broadcaster: broadcaster, + ShredVersion: uint16(turbineShredVersion), + EpochSchedule: epochSchedule, + AlpenglowClock: true, + SlotDuration: blockprod.AlpenglowSlotDuration, + CompletionReserve: time.Duration(validatorCompletionReserveMs) * time.Millisecond, ParentContext: func(slot uint64) blockprod.ParentContext { tip := replay.ChainTipParentContext() // Blockprod owns the replay-readiness rule. In particular, the first diff --git a/config.example.toml b/config.example.toml index 2636e1b1..2cf1affb 100644 --- a/config.example.toml +++ b/config.example.toml @@ -328,6 +328,15 @@ name = "mithril" # Signature-verification workers (0 = GOMAXPROCS). tpu_sigverify_workers = 0 + # Bounded cross-slot TPU queue. Zero keeps the 131,072-transaction default. + # Larger queues can prefill four busy leader slots, using additional memory. + tpu_max_buffered_transactions = 0 + + # Milliseconds reserved for local finalization and broadcast, not consensus + # finality. Zero keeps the conservative 75ms default. Tune from measured + # completion margins; this does not change the protocol slot deadline. + block_completion_reserve_ms = 0 + # ============================================================================ # [consensus] - Alpenglow Consensus # ============================================================================ diff --git a/pkg/accounts/mem_accounts.go b/pkg/accounts/mem_accounts.go index 4158be55..630ffb42 100644 --- a/pkg/accounts/mem_accounts.go +++ b/pkg/accounts/mem_accounts.go @@ -1,7 +1,6 @@ package accounts import ( - "fmt" "sync" "github.com/Overclock-Validator/mithril/pkg/base58" @@ -13,6 +12,16 @@ type MemAccounts struct { mu *sync.RWMutex } +// A miss is an ordinary step when falling back to parent accounts. Defer the +// diagnostic encoding until it is needed, and copy the key so callers can reuse it. +type missingMemAccountError struct { + key [32]byte +} + +func (e *missingMemAccountError) Error() string { + return "no such account " + base58.Encode(e.key[:]) + " found" +} + func NewMemAccounts() MemAccounts { return MemAccounts{ Map: make(map[[32]byte]*Account), @@ -32,7 +41,7 @@ func (m MemAccounts) GetAccount(pubkey *[32]byte) (*Account, error) { defer m.mu.RUnlock() acct, ok := m.Map[*pubkey] if !ok { - return nil, fmt.Errorf("no such account %s found", base58.Encode(pubkey[:])) + return nil, &missingMemAccountError{key: *pubkey} } return acct, nil } @@ -40,7 +49,7 @@ func (m MemAccounts) GetAccount(pubkey *[32]byte) (*Account, error) { func (m MemAccounts) GetAccountWithoutLock(pubkey solana.PublicKey) (*Account, error) { acct, ok := m.Map[pubkey] if !ok { - return nil, fmt.Errorf("no such account %s found", base58.Encode(pubkey[:])) + return nil, &missingMemAccountError{key: pubkey} } return acct, nil } diff --git a/pkg/accounts/mem_accounts_test.go b/pkg/accounts/mem_accounts_test.go index bd55cb21..4a614dd9 100644 --- a/pkg/accounts/mem_accounts_test.go +++ b/pkg/accounts/mem_accounts_test.go @@ -3,8 +3,23 @@ package accounts import ( "testing" "time" + + "github.com/gagliardetto/solana-go" ) +func TestMemAccountMissingErrorRetainsLookupKey(t *testing.T) { + mem := NewMemAccounts() + key := [32]byte{} + _, lockedErr := mem.GetAccount(&key) + _, unlockedErr := mem.GetAccountWithoutLock(solana.PublicKey(key)) + key[0] = 99 // Lookup callers may reuse their key storage before reporting an error. + for _, err := range []error{lockedErr, unlockedErr} { + if err == nil || err.Error() != "no such account 11111111111111111111111111111111 found" { + t.Fatalf("missing error lost its original key: %v", err) + } + } +} + func TestMemAccountsReadsAreConcurrent(t *testing.T) { mem := NewMemAccounts() var key [32]byte diff --git a/pkg/blockprod/bank.go b/pkg/blockprod/bank.go index 26784fdf..b93e07d4 100644 --- a/pkg/blockprod/bank.go +++ b/pkg/blockprod/bank.go @@ -3,6 +3,7 @@ package blockprod import ( "sync" + "github.com/Overclock-Validator/mithril/pkg/arena" "github.com/Overclock-Validator/mithril/pkg/costmodel" "github.com/Overclock-Validator/mithril/pkg/features" "github.com/Overclock-Validator/mithril/pkg/fees" @@ -44,6 +45,10 @@ type WorkingBank struct { // seenMessages is the bank-local AlreadyProcessed status set. The TPU's // signature LRU is only an ingress optimization and is not authoritative. seenMessages map[[32]byte]struct{} + // Execution and commit are serialized by mu. Borrowed accounts never escape + // either phase, so their storage can be reset for the next transaction. + borrowedAccounts *arena.Arena[sealevel.BorrowedAccount] + preparer *replay.TransactionPreparer } type BankConfig struct { @@ -71,7 +76,12 @@ func NewWorkingBank(cfg BankConfig) *WorkingBank { if sink == nil { sink = NopBatchSink{} } + var preparer *replay.TransactionPreparer + if cfg.SlotCtx != nil { + preparer = replay.NewTransactionPreparer(cfg.SlotCtx.Features) + } return &WorkingBank{ + preparer: preparer, slotCtx: cfg.SlotCtx, slot: cfg.Slot, leader: cfg.Leader, @@ -82,6 +92,7 @@ func NewWorkingBank(cfg BankConfig) *WorkingBank { accepting: true, ancestorStatuses: cfg.TransactionStatuses, seenMessages: make(map[[32]byte]struct{}), + borrowedAccounts: arena.New[sealevel.BorrowedAccount](64), } } @@ -175,11 +186,30 @@ func (b *WorkingBank) Forge(wire []byte) (ForgeResult, costmodel.ExceedReason) { // ForgeTransaction executes and commits a parsed transaction. func (b *WorkingBank) ForgeTransaction(tx *solana.Transaction, wireSize int) (ForgeResult, costmodel.ExceedReason) { + return b.forgeTransaction(tx, wireSize, nil) +} + +// ForgePreparedTransaction reuses static work from owned, immutable TPU bytes. +// A different bank feature snapshot falls back to the ordinary execution path. +func (b *WorkingBank) ForgePreparedTransaction(tx *solana.Transaction, wireSize int, prepared *replay.PreparedTransaction) (ForgeResult, costmodel.ExceedReason) { + if b.slotCtx == nil || !b.preparer.Matches(prepared, tx, b.slotCtx.Features) { + prepared = nil + } + return b.forgeTransaction(tx, wireSize, prepared) +} + +func (b *WorkingBank) forgeTransaction(tx *solana.Transaction, wireSize int, prepared *replay.PreparedTransaction) (ForgeResult, costmodel.ExceedReason) { if tx == nil { b.RebateSchedule(wireSize) return ForgeDroppedParse, costmodel.ExceedNone } - messageHash, err := replay.TransactionMessageHash(tx) + var messageHash [32]byte + var err error + if prepared != nil { + messageHash = prepared.MessageHash() + } else { + messageHash, err = replay.TransactionMessageHash(tx) + } if err != nil { b.RebateSchedule(wireSize) return ForgeDroppedParse, costmodel.ExceedNone @@ -201,7 +231,11 @@ func (b *WorkingBank) ForgeTransaction(tx *solana.Transaction, wireSize int) (Fo f := features.NewFeaturesDefault() feats = f } - cost, err = costmodel.EstimateTransactionCost(tx, feats) + if prepared != nil { + cost = prepared.Cost() + } else { + cost, err = costmodel.EstimateTransactionCost(tx, feats) + } if err != nil { b.RebateSchedule(wireSize) return ForgeDroppedParse, costmodel.ExceedNone @@ -238,15 +272,17 @@ func (b *WorkingBank) ForgeTransaction(tx *solana.Transaction, wireSize int) (Fo if reason := b.reserveEntryBytesLocked(wireSize); reason != costmodel.ExceedNone { return ForgeDroppedCost, reason } - if err := fees.PayerCanFund(b.slotCtx, tx); err != nil { + if err := b.preparer.PayerCanFund(b.slotCtx, tx, prepared); err != nil { return ForgeDroppedExecution, costmodel.ExceedNone } - output := replay.LoadAndExecuteTransaction(replay.LoadAndExecuteTransactionInput{ - SlotCtx: b.slotCtx, - Transaction: tx, - LeanResult: true, - }) + output := b.preparer.LoadAndExecute(replay.LoadAndExecuteTransactionInput{ + SlotCtx: b.slotCtx, + Transaction: tx, + LeanResult: true, + SkipTimingMetrics: true, + Arena: b.borrowedAccounts, + }, prepared) if output.ProcessingResult.TransactionError != nil { feeInfo, err := replay.ApplyFeesOnlyTransaction(b.slotCtx, tx, output) if err != nil { diff --git a/pkg/blockprod/leader.go b/pkg/blockprod/leader.go index e304ffb8..66bce23e 100644 --- a/pkg/blockprod/leader.go +++ b/pkg/blockprod/leader.go @@ -103,11 +103,12 @@ type LeaderLoop struct { currentSlot func() uint64 leaderForSlot func(uint64) (solana.PublicKey, bool) - pollInterval time.Duration - slotDuration time.Duration - now func() time.Time - tickStartedAt time.Time - tickDeliveryLag time.Duration + pollInterval time.Duration + slotDuration time.Duration + completionReserve time.Duration + now func() time.Time + tickStartedAt time.Time + tickDeliveryLag time.Duration mu sync.Mutex activeSlot uint64 @@ -154,7 +155,10 @@ type LeaderLoopConfig struct { RewardCerts RewardCertBuilder PollInterval time.Duration SlotDuration time.Duration - Now func() time.Time + // CompletionReserve is time retained for finalization and broadcast. Zero + // uses the conservative default; tune only from measured completion times. + CompletionReserve time.Duration + Now func() time.Time } func NewLeaderLoop(cfg LeaderLoopConfig) *LeaderLoop { @@ -170,6 +174,9 @@ func NewLeaderLoop(cfg LeaderLoopConfig) *LeaderLoop { if cfg.Now == nil { cfg.Now = time.Now } + if cfg.CompletionReserve <= 0 { + cfg.CompletionReserve = leaderBlockCompletionReserve + } return &LeaderLoop{ controller: cfg.Controller, identity: cfg.Identity, @@ -187,6 +194,7 @@ func NewLeaderLoop(cfg LeaderLoopConfig) *LeaderLoop { rewardCerts: cfg.RewardCerts, pollInterval: cfg.PollInterval, slotDuration: cfg.SlotDuration, + completionReserve: cfg.CompletionReserve, now: cfg.Now, finishedLeaderSlots: make(map[uint64]struct{}), pendingFailures: make(map[uint64]leaderSlotFailure), @@ -329,9 +337,11 @@ func (l *LeaderLoop) tickScheduled(scheduledAt time.Time) { delete(l.pendingFailures, targetSlot) openedAt := l.now() l.recordTickTimingLocked(openedAt, "opened") - mlog.Log.InfofPrecise("ALPENGLOW block production: opened local leader slot=%d parent_slot=%d replay_frontier=%d live_slot=%d start_slot_ms=%d%s", + limits := l.activeBank.CostTracker().Limits() + mlog.Log.InfofPrecise("ALPENGLOW block production: opened local leader slot=%d parent_slot=%d replay_frontier=%d live_slot=%d start_slot_ms=%d block_cost_limit=%d account_cost_limit=%d entry_bytes_limit=%d%s", targetSlot, l.parentCtx.ParentSlot, global.ReplayFrontier(), wallSlot, - startDuration.Milliseconds(), l.productionStartTimingDetailLocked(targetSlot, openedAt)) + startDuration.Milliseconds(), limits.BlockCost, limits.WritableAccountCost, limits.MaxEntryBytes, + l.productionStartTimingDetailLocked(targetSlot, openedAt)) return } @@ -586,7 +596,10 @@ func (l *LeaderLoop) productionWindowDeadlineLocked(slot uint64) time.Time { if deadline.IsZero() { return time.Time{} } - reserve := leaderBlockCompletionReserve + reserve := l.completionReserve + if reserve <= 0 { + reserve = leaderBlockCompletionReserve + } if reserve >= l.slotDuration { reserve = l.slotDuration / 4 } @@ -1205,12 +1218,16 @@ func (l *LeaderLoop) startSlotLocked(slot uint64) error { if startEntryHash == (solana.Hash{}) { startEntryHash = parentCtx.ParentBankhash } + limits, err := costmodel.LimitsForSlot(slotCtx.Features, epochSchedule, slot) + if err != nil { + return fmt.Errorf("leader slot limits: %w", err) + } sink := NewShredSink(session) bank := NewWorkingBank(BankConfig{ SlotCtx: slotCtx, Slot: slot, Leader: l.identity.PublicKey(), - Limits: costmodel.LimitsForFeatures(slotCtx.Features), + Limits: limits, EntryHash: startEntryHash, Sink: sink, TransactionStatuses: parentCtx.TransactionStatuses, diff --git a/pkg/blockprod/leader_completion_reserve_test.go b/pkg/blockprod/leader_completion_reserve_test.go new file mode 100644 index 00000000..062bf84d --- /dev/null +++ b/pkg/blockprod/leader_completion_reserve_test.go @@ -0,0 +1,32 @@ +package blockprod + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestConfiguredCompletionReserveKeepsProtocolDeadlines(t *testing.T) { + ready := time.Unix(1_700_000_000, 0) + now := ready + loop := NewLeaderLoop(LeaderLoopConfig{ + SlotDuration: AlpenglowSlotDuration, CompletionReserve: 60 * time.Millisecond, + Now: func() time.Time { return now }, + }) + loop.productionWindow = leaderProductionWindow{ + active: true, startSlot: 212, endSlot: 215, nextSlot: 212, readyAt: ready, + } + for offset := uint64(0); offset < 4; offset++ { + slot := 212 + offset + deadline := ready.Add(time.Duration(offset+1) * 200 * time.Millisecond) + require.Equal(t, deadline, loop.productionWindowProtocolDeadlineLocked(slot)) + require.Equal(t, deadline.Add(-60*time.Millisecond), loop.productionWindowDeadlineLocked(slot)) + // The override admits starts during the additional 15ms, but still + // rejects at its own cutoff instead of extending the protocol deadline. + now = deadline.Add(-70 * time.Millisecond) + require.NoError(t, loop.productionStartCutoffErrorLocked(slot)) + now = deadline.Add(-60 * time.Millisecond) + require.ErrorIs(t, loop.productionStartCutoffErrorLocked(slot), errProductionStartCutoffElapsed) + } +} diff --git a/pkg/blockprod/leader_processing_test.go b/pkg/blockprod/leader_processing_test.go new file mode 100644 index 00000000..71303a2c --- /dev/null +++ b/pkg/blockprod/leader_processing_test.go @@ -0,0 +1,81 @@ +package blockprod + +import ( + "sync/atomic" + "testing" + + "github.com/Overclock-Validator/mithril/pkg/arena" + "github.com/Overclock-Validator/mithril/pkg/metrics" + "github.com/Overclock-Validator/mithril/pkg/replay" + "github.com/Overclock-Validator/mithril/pkg/sealevel" + "github.com/Overclock-Validator/mithril/pkg/tpu/txfixture" + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func TestLeaderExecutionOptionsPreserveResults(t *testing.T) { + for _, testCase := range []string{"success", "instruction_failure", "payer_failure"} { + t.Run(testCase, func(t *testing.T) { + env := NewTestEnv(TestEnvConfig{}) + defer env.Close() + tx, err := solana.TransactionFromBytes(txfixture.MustSignedTransferWire(42)) + require.NoError(t, err) + if testCase == "instruction_failure" { + tx.Message.Instructions[0].Data[0] = 0xff + } + if testCase == "payer_failure" { + setPayerLamports(t, env, 1) + } + input := replay.LoadAndExecuteTransactionInput{SlotCtx: env.SlotCtx, Transaction: tx, LeanResult: true} + reference := replay.LoadAndExecuteTransaction(input) + input.SkipTimingMetrics = true + // A one-object arena also exercises the heap fallback. Repeated calls + // reset it, while the previous result's account state remains valid. + input.Arena = arena.New[sealevel.BorrowedAccount](1) + before := atomic.LoadUint64(&metrics.GlobalBlockReplay.InstructionsAndAccountMetasFromTx.Count) + beforeDispatch := atomic.LoadUint64(&metrics.GlobalBlockReplay.GetNextIxCtx.Count) + for i := 0; i < 3; i++ { + got := replay.LoadAndExecuteTransaction(input) + require.Equal(t, reference.ProcessingResult, got.ProcessingResult) + require.Equal(t, reference.FeeInfo, got.FeeInfo) + require.Equal(t, reference.LoadedAccountsDataSize, got.LoadedAccountsDataSize) + if reference.ExecCtx != nil { + require.NotNil(t, got.ExecCtx) + require.Equal(t, reference.ExecCtx.ComputeMeter.Used(), got.ExecCtx.ComputeMeter.Used()) + require.Equal(t, reference.ExecCtx.TransactionContext.Accounts.Accounts, got.ExecCtx.TransactionContext.Accounts.Accounts) + } + } + require.Equal(t, before, atomic.LoadUint64(&metrics.GlobalBlockReplay.InstructionsAndAccountMetasFromTx.Count)) + require.Equal(t, beforeDispatch, atomic.LoadUint64(&metrics.GlobalBlockReplay.GetNextIxCtx.Count)) + }) + } +} + +func TestWorkingBankReusesBorrowedAccountsWithoutChangingMessages(t *testing.T) { + env := NewTestEnv(TestEnvConfig{}) + defer env.Close() + payerBefore, err := env.SlotCtx.GetAccount(txfixture.PayerPubkey()) + require.NoError(t, err) + payerBalance := payerBefore.Lamports + destBefore, err := env.SlotCtx.GetAccount(txfixture.DestPubkey()) + require.NoError(t, err) + destBalance := destBefore.Lamports + const count = 130 + for i := 0; i < count; i++ { + wire := txfixture.MustSignedTransferWire(uint64(i)) + tx, err := solana.TransactionFromBytes(wire) + require.NoError(t, err) + result, _ := env.Bank.ForgeTransaction(tx, len(wire)) + require.Equal(t, ForgeAccepted, result) + after, err := tx.MarshalBinary() + require.NoError(t, err) + require.Equal(t, wire, after) + } + payerAfter, err := env.SlotCtx.GetAccount(txfixture.PayerPubkey()) + require.NoError(t, err) + destAfter, err := env.SlotCtx.GetAccount(txfixture.DestPubkey()) + require.NoError(t, err) + const transferred = count * (count + 1) / 2 + require.Equal(t, payerBalance-transferred-count*5000, payerAfter.Lamports) + require.Equal(t, destBalance+transferred, destAfter.Lamports) +} diff --git a/pkg/blockprod/leader_throughput_bench_test.go b/pkg/blockprod/leader_throughput_bench_test.go new file mode 100644 index 00000000..006633c9 --- /dev/null +++ b/pkg/blockprod/leader_throughput_bench_test.go @@ -0,0 +1,117 @@ +package blockprod + +import ( + "math" + "testing" + + "github.com/Overclock-Validator/mithril/pkg/accounts" + "github.com/Overclock-Validator/mithril/pkg/addresses" + "github.com/Overclock-Validator/mithril/pkg/costmodel" + "github.com/Overclock-Validator/mithril/pkg/features" + "github.com/Overclock-Validator/mithril/pkg/replay" + "github.com/Overclock-Validator/mithril/pkg/tpu/txfixture" + "github.com/gagliardetto/solana-go" + computebudget "github.com/gagliardetto/solana-go/programs/compute-budget" +) + +// BenchmarkWorkingBankHotAccounts measures admission, execution, publication, +// and entry batching. Signing and bank setup are outside the measured region. +// All wires are unique within a bank, so this cannot benchmark dedup rejection. +func BenchmarkWorkingBankHotAccounts(b *testing.B) { benchmarkWorkingBankHotAccounts(b, "wire") } +func BenchmarkWorkingBankDecodedHotAccounts(b *testing.B) { + benchmarkWorkingBankHotAccounts(b, "decoded") +} +func BenchmarkWorkingBankPreparedHotAccounts(b *testing.B) { + benchmarkWorkingBankHotAccounts(b, "prepared") +} + +func benchmarkWorkingBankHotAccounts(b *testing.B, mode string) { + const perBank = 10000 + for _, workload := range []string{"compute_budget", "transfer"} { + b.Run(workload, func(b *testing.B) { + wires := make([][]byte, perBank) + for i := range wires { + if workload == "transfer" { + wires[i] = txfixture.MustSignedTransferWire(uint64(i)) + continue + } + tx, err := solana.NewTransaction([]solana.Instruction{ + computebudget.NewSetComputeUnitLimitInstruction(uint32(1000 + i)).Build(), + }, txfixture.TestBlockhash(), solana.TransactionPayer(txfixture.PayerPubkey())) + if err != nil { + b.Fatal(err) + } + key := txfixture.PayerPrivateKey() + if _, err = tx.Sign(func(solana.PublicKey) *solana.PrivateKey { return &key }); err != nil { + b.Fatal(err) + } + wires[i], err = tx.MarshalBinary() + if err != nil { + b.Fatal(err) + } + } + decoded := make([]*solana.Transaction, perBank) + for i, wire := range wires { + var err error + decoded[i], err = solana.TransactionFromBytes(wire) + if err != nil { + b.Fatal(err) + } + } + var prepared []*replay.PreparedTransaction + var env *TestEnv + defer func() { + if env != nil { + env.Close() + } + }() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if i%perBank == 0 { + b.StopTimer() + if env != nil { + env.Close() + } + env = NewTestEnv(TestEnvConfig{}) + env.SlotCtx.Features.EnableFeature(features.RemoveAccountsDeltaHash, 0) + env.SlotCtx.Features.EnableFeature(features.RaiseBlockLimitsTo100m, 0) + if err := env.SlotCtx.Accounts.SetAccount(&addresses.ComputeBudgetProgramAddr, &accounts.Account{ + Key: addresses.ComputeBudgetProgramAddr, Lamports: 1, + Owner: addresses.NativeLoaderAddr, Executable: true, RentEpoch: math.MaxUint64, + }); err != nil { + b.Fatal(err) + } + + if mode == "prepared" { + // Bind the immutable snapshot after all fixture feature setup is complete. + env.Bank.preparer = replay.NewTransactionPreparer(env.SlotCtx.Features) + if prepared == nil { + prepared = make([]*replay.PreparedTransaction, perBank) + for j, tx := range decoded { + prepared[j] = env.Bank.preparer.Prepare(tx) + if prepared[j] == nil { + b.Fatal("preparation failed") + } + } + } + } + b.StartTimer() + } + var result ForgeResult + var reason costmodel.ExceedReason + switch mode { + case "prepared": + result, reason = env.Bank.ForgePreparedTransaction(decoded[i%perBank], len(wires[i%perBank]), prepared[i%perBank]) + case "decoded": + result, reason = env.Bank.ForgeTransaction(decoded[i%perBank], len(wires[i%perBank])) + default: + result, reason = env.Bank.Forge(wires[i%perBank]) + } + if result != ForgeAccepted { + b.Fatalf("transaction %d: %v / %v", i, result, reason) + } + } + }) + } +} diff --git a/pkg/blockprod/prepared_transaction_test.go b/pkg/blockprod/prepared_transaction_test.go new file mode 100644 index 00000000..9a6153d6 --- /dev/null +++ b/pkg/blockprod/prepared_transaction_test.go @@ -0,0 +1,142 @@ +package blockprod + +import ( + "testing" + + "github.com/Overclock-Validator/mithril/pkg/costmodel" + "github.com/Overclock-Validator/mithril/pkg/features" + "github.com/Overclock-Validator/mithril/pkg/replay" + "github.com/Overclock-Validator/mithril/pkg/sealevel" + "github.com/Overclock-Validator/mithril/pkg/tpu/txfixture" + "github.com/gagliardetto/solana-go" + computebudget "github.com/gagliardetto/solana-go/programs/compute-budget" + "github.com/gagliardetto/solana-go/programs/system" + "github.com/stretchr/testify/require" +) + +func TestPreparedBankPreservesOutcomes(t *testing.T) { + for _, kind := range []string{"success", "instruction_failure", "fees_only", "payer_changed", "expired", "duplicate", "foreign_message"} { + t.Run(kind, func(t *testing.T) { + reference := NewTestEnv(TestEnvConfig{}) + defer reference.Close() + candidate := NewTestEnv(TestEnvConfig{}) + defer candidate.Close() + tx := mustSignedTransfer(t, 7) + if kind == "instruction_failure" { + tx.Message.Instructions[0].Data[0] = 0xff + } + if kind == "fees_only" { + tx = mustSignBankTestTransaction(t, + computebudget.NewSetLoadedAccountsDataSizeLimitInstruction(1).Build(), + system.NewTransferInstruction(1, txfixture.PayerPubkey(), txfixture.DestPubkey()).Build()) + } + if kind == "expired" { + tx.Message.RecentBlockhash = solana.Hash{99} + } + prepared := replay.NewTransactionPreparer(candidate.SlotCtx.Features.Clone()).Prepare(tx) + require.NotNil(t, prepared) + estimate, err := costmodel.EstimateTransactionCost(tx, candidate.SlotCtx.Features) + require.NoError(t, err) + require.Equal(t, estimate, prepared.Cost()) + if kind == "payer_changed" { + // Preparation succeeded while the payer was funded. Admission must + // still reject after another transaction spends that balance. + setPayerLamports(t, reference, 1) + setPayerLamports(t, candidate, 1) + } + if kind == "foreign_message" { + tx = mustSignedTransfer(t, 19) + } + wire, err := tx.MarshalBinary() + require.NoError(t, err) + for i := 0; i < 2; i++ { + a, ar := reference.Bank.ForgeTransaction(tx, len(wire)) + b, br := candidate.Bank.ForgePreparedTransaction(tx, len(wire), prepared) + require.Equal(t, a, b) + require.Equal(t, ar, br) + require.Equal(t, reference.Bank.CostTracker().BlockCost(), candidate.Bank.CostTracker().BlockCost()) + require.Equal(t, reference.Bank.TxFeeAccumulator(), candidate.Bank.TxFeeAccumulator()) + for _, key := range []solana.PublicKey{txfixture.PayerPubkey(), txfixture.DestPubkey()} { + ra, err := reference.SlotCtx.GetAccount(key) + require.NoError(t, err) + ca, err := candidate.SlotCtx.GetAccount(key) + require.NoError(t, err) + require.Equal(t, ra, ca) + } + if kind != "duplicate" { + break + } + } + after, err := tx.MarshalBinary() + require.NoError(t, err) + require.Equal(t, wire, after) + }) + } +} + +func TestPreparedBankRejectsStaleFeatures(t *testing.T) { + env := NewTestEnv(TestEnvConfig{}) + defer env.Close() + future := env.SlotCtx.Features.Clone() + future.EnableFeature(features.EnableTxV1, 0) + tx := mustSignedTransfer(t, 1) + _, err := tx.Message.SetVersion(solana.MessageVersionV1) + require.NoError(t, err) + prepared := replay.NewTransactionPreparer(future).Prepare(tx) + require.NotNil(t, prepared) + require.False(t, env.Bank.preparer.Matches(prepared, tx, env.SlotCtx.Features)) + wire, err := tx.MarshalBinary() + require.NoError(t, err) + result, _ := env.Bank.ForgePreparedTransaction(tx, len(wire), prepared) + require.Equal(t, ForgeDroppedExecution, result) + require.Empty(t, env.Bank.ForgedTransactions()) + require.Zero(t, env.Bank.TxFeeAccumulator().TotalFees) +} + +func TestPreparedLeaderStillRejectsFeePayerNoOp(t *testing.T) { + env := NewTestEnv(TestEnvConfig{}) + defer env.Close() + // Establish the complete feature snapshot before constructing the bank. + env.SlotCtx.Features.EnableFeature(features.RelaxFeePayerConstraint, 0) + bank := NewWorkingBank(BankConfig{SlotCtx: env.SlotCtx, Slot: env.SlotCtx.Slot, + TransactionStatuses: replay.NewTransactionStatusCache().View()}) + tx := mustSignedTransfer(t, 1) + prepared := bank.preparer.Prepare(tx) + require.NotNil(t, prepared) + setPayerLamports(t, env, 1) + preview := bank.preparer.LoadAndExecute(replay.LoadAndExecuteTransactionInput{ + SlotCtx: env.SlotCtx, Transaction: tx, LeanResult: true}, prepared) + require.True(t, preview.ProcessedAsNoOp) + wire, err := tx.MarshalBinary() + require.NoError(t, err) + result, _ := bank.ForgePreparedTransaction(tx, len(wire), prepared) + require.Equal(t, ForgeDroppedExecution, result) + require.Empty(t, bank.ForgedTransactions()) + require.Zero(t, bank.TxFeeAccumulator().TotalFees) +} + +func TestPreparedExecutionRechecksStateWithoutMutatingPreparation(t *testing.T) { + env := NewTestEnv(TestEnvConfig{}) + defer env.Close() + tx := mustSignedTransfer(t, 1) + p := replay.NewTransactionPreparer(env.SlotCtx.Features) + prepared := p.Prepare(tx) + require.NotNil(t, prepared) + input := replay.LoadAndExecuteTransactionInput{SlotCtx: env.SlotCtx, Transaction: tx, LeanResult: true} + for _, balance := range []uint64{10_000_000, 1, 20_000_000} { + setPayerLamports(t, env, balance) + got := p.LoadAndExecute(input, prepared) + want := replay.LoadAndExecuteTransaction(input) + require.Equal(t, want.ProcessingResult, got.ProcessingResult) + require.Equal(t, want.FeeInfo, got.FeeInfo) + require.Equal(t, want.LoadedAccountsDataSize, got.LoadedAccountsDataSize) + if want.ExecCtx != nil { + require.Equal(t, want.ExecCtx.TransactionContext.Accounts.Accounts, got.ExecCtx.TransactionContext.Accounts.Accounts) + require.Equal(t, want.ExecCtx.ComputeMeter.Used(), got.ExecCtx.ComputeMeter.Used()) + } + } + // Rent-boundary checks still use the bank's current payer state. + rent := sealevel.NewDefaultRentSysvar() + setPayerLamports(t, env, rent.MinimumBalance(0)+4999) + require.Error(t, p.PayerCanFund(env.SlotCtx, tx, prepared)) +} diff --git a/pkg/blockprod/readonly_block_bench_test.go b/pkg/blockprod/readonly_block_bench_test.go new file mode 100644 index 00000000..b3b8ed71 --- /dev/null +++ b/pkg/blockprod/readonly_block_bench_test.go @@ -0,0 +1,187 @@ +package blockprod + +import ( + "crypto/ed25519" + "crypto/sha256" + "math" + "testing" + + "github.com/Overclock-Validator/mithril/pkg/accounts" + "github.com/Overclock-Validator/mithril/pkg/addresses" + "github.com/Overclock-Validator/mithril/pkg/costmodel" + "github.com/Overclock-Validator/mithril/pkg/features" + "github.com/Overclock-Validator/mithril/pkg/tpu/txfixture" + "github.com/Overclock-Validator/mithril/pkg/turbine" + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +const readonlyBlockAccepted = 48622 // 50M budget, including the next tx's upfront loaded-data reservation. +const readonlyActualCost = 1028 + +type readonlyBlockParent struct{ mem accounts.MemAccounts } + +func (p readonlyBlockParent) GetAccount(_ uint64, key solana.PublicKey) (*accounts.Account, error) { + raw := [32]byte(key) + return p.mem.GetAccount(&raw) +} + +type readonlyBlockFixture struct { + wires [][]byte + txs []*solana.Transaction + payers []solana.PublicKey + parent readonlyBlockParent +} + +func makeReadonlyBlockFixture(tb testing.TB, count int) readonlyBlockFixture { + tb.Helper() + f := readonlyBlockFixture{parent: readonlyBlockParent{accounts.NewMemAccounts()}} + keys := make([]ed25519.PrivateKey, 8) + for i := range keys { + seed := sha256.Sum256([]byte{byte(i), 73}) + keys[i] = ed25519.NewKeyFromSeed(seed[:]) + f.payers = append(f.payers, solana.PublicKeyFromBytes(keys[i].Public().(ed25519.PublicKey))) + } + pool := make([]solana.PublicKey, txfixture.ReadonlyPairPoolSize) + for i := range pool { + pool[i] = solana.PublicKey{byte(i + 1), 77} + require.NoError(tb, f.parent.mem.SetAccountWithoutLock(pool[i], &accounts.Account{ + Key: pool[i], Lamports: 1_000_000, Data: make([]byte, 9), Owner: solana.PublicKey{11}, RentEpoch: math.MaxUint64, + })) + } + for i := 0; i < count; i++ { + wire, err := txfixture.ReadonlyPairWire(keys[i%8], txfixture.TestBlockhash(), pool, i/8) + require.NoError(tb, err) + tx, err := solana.TransactionFromBytes(wire) + require.NoError(tb, err) + f.wires = append(f.wires, wire) + f.txs = append(f.txs, tx) + } + return f +} + +func (f readonlyBlockFixture) bank(tb testing.TB, sink BatchSink) *TestEnv { + tb.Helper() + // Explicit active 200ms testnet budgets allow identical baseline/candidate + // benchmark fixtures; LimitsForSlot has separate epoch-transition tests. + limits := costmodel.DefaultLimits() + limits.BlockCost, limits.WritableAccountCost = 50_000_000, 20_000_000 + limits.AllocatedDataSizeDelta, limits.MaxEntryBytes = 50_000_000, 10*1024*1024-48 + env := NewTestEnv(TestEnvConfig{Limits: limits, Sink: sink}) + env.SlotCtx.Features.EnableFeature(features.RemoveAccountsDeltaHash, 0) + env.SlotCtx.UnrootedRead = f.parent + for _, payer := range f.payers { + require.NoError(tb, env.SlotCtx.Accounts.SetAccountWithoutLock(payer, &accounts.Account{ + Key: payer, Lamports: 10_000_000_000, Owner: addresses.SystemProgramAddr, RentEpoch: math.MaxUint64, + })) + } + return env +} + +// This is a capacity/correctness test, not a 200ms deadline assertion. It creates +// a full synthetic block, validates fee/cost accounting, and round-trips every +// emitted entry through the production shred generator and decoder. No network. +func TestReadonlyPairBlockCapacityAndShredRoundTrip(t *testing.T) { + f := makeReadonlyBlockFixture(t, readonlyBlockAccepted+1) + sink := &captureSink{} + env := f.bank(t, sink) + defer env.Close() + for i, tx := range f.txs { + result, reason := env.Bank.ForgeTransaction(tx, len(f.wires[i])) + if i < readonlyBlockAccepted { + require.Equal(t, ForgeAccepted, result, "transaction %d", i) + } else { + require.Equal(t, ForgeDroppedCost, result) + require.Equal(t, costmodel.ExceedBlockCost, reason) + } + } + env.Bank.Freeze() + require.Equal(t, uint64(readonlyBlockAccepted*readonlyActualCost), env.Bank.CostTracker().BlockCost()) + require.Equal(t, uint64(readonlyBlockAccepted), env.Bank.NumSignatures()) + require.Equal(t, uint64(readonlyBlockAccepted*5000), env.Bank.TxFeeAccumulator().TotalFees) + require.Len(t, env.Bank.ForgedTransactions(), readonlyBlockAccepted) + require.LessOrEqual(t, uint64(env.Bank.EntryBytes()), env.Bank.CostTracker().Limits().MaxEntryBytes) + for i, payer := range f.payers { + included := readonlyBlockAccepted / 8 + if i < readonlyBlockAccepted%8 { + included++ + } + acct, err := env.SlotCtx.GetAccount(payer) + require.NoError(t, err) + require.Equal(t, uint64(10_000_000_000-included*5000), acct.Lamports) + } + gen := turbine.ShredGenerator{Slot: 42, ParentSlot: 41, Version: 7} + var root solana.Hash + var nextData, nextCode uint32 + count, totalBytes := 0, 0 + previous := solana.Hash{0xab} + for i, entries := range sink.batches { + raw, err := marshalEntryBatchBytes(entries) + require.NoError(t, err) + require.Equal(t, len(raw), sink.bytes[i]) + totalBytes += len(raw) + packets, chained, d, c, err := gen.MakeShredsFromData(txfixture.PayerPrivateKey(), raw, false, root, nextData, nextCode) + require.NoError(t, err) + root, nextData, nextCode = chained, d, c + var shreds []*turbine.Shred + for _, packet := range packets { + sh, err := turbine.ParseShred(packet) + require.NoError(t, err) + if sh.Type == turbine.ShredTypeData { + shreds = append(shreds, sh) + } + } + decoded, err := turbine.DecodeEntriesFromDataShreds(shreds) + require.NoError(t, err) + require.Equal(t, entries, decoded) + for _, entry := range decoded { + require.Equal(t, turbine.NextAlpenglowEntryHash(previous, entry.NumHashes, entry.Txns), entry.Hash) + previous = entry.Hash + for _, tx := range entry.Txns { + require.Equal(t, f.txs[count].Signatures, tx.Signatures) + count++ + } + } + } + require.Equal(t, readonlyBlockAccepted, count) + require.Equal(t, env.Bank.EntryBytes(), totalBytes) + require.Equal(t, env.Bank.EntryHash(), previous) + require.Less(t, nextData, uint32(16384)) // Leaves room for header/footer/ending tick. +} + +// One serial caller; signing and fixture/bank setup are excluded. Measures +// admission, execution, account publication, entry building and final flush. +// It excludes signature verification, actual AccountsDB, network and consensus. +func BenchmarkReadonlyPairFullBlock(b *testing.B) { + f := makeReadonlyBlockFixture(b, readonlyBlockAccepted) + for _, mode := range []string{"wire", "decoded"} { + b.Run(mode, func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + env := f.bank(b, nil) + b.StartTimer() + for j, wire := range f.wires { + var result ForgeResult + if mode == "wire" { + result, _ = env.Bank.Forge(wire) + } else { + result, _ = env.Bank.ForgeTransaction(f.txs[j], len(wire)) + } + if result != ForgeAccepted { + b.Fatalf("transaction %d: %v", j, result) + } + } + env.Bank.Freeze() + b.StopTimer() + if env.Bank.CostTracker().BlockCost() != readonlyBlockAccepted*readonlyActualCost { + b.Fatal("unexpected block cost") + } + env.Close() + b.StartTimer() + } + b.ReportMetric(float64(readonlyBlockAccepted), "tx/block") + }) + } +} diff --git a/pkg/blockprod/readonly_block_prepared_bench_test.go b/pkg/blockprod/readonly_block_prepared_bench_test.go new file mode 100644 index 00000000..68957fce --- /dev/null +++ b/pkg/blockprod/readonly_block_prepared_bench_test.go @@ -0,0 +1,45 @@ +package blockprod + +import ( + "testing" + + "github.com/Overclock-Validator/mithril/pkg/replay" +) + +// Models a queue already statically prepared before leadership. Preparation is +// shifted out of this timed bank phase, not eliminated from validator CPU work. +func BenchmarkReadonlyPairPreparedFullBlock(b *testing.B) { + f := makeReadonlyBlockFixture(b, readonlyBlockAccepted) + setup := f.bank(b, nil) + preparer := replay.NewTransactionPreparer(setup.SlotCtx.Features) + prepared := make([]*replay.PreparedTransaction, len(f.txs)) + for i, tx := range f.txs { + prepared[i] = preparer.Prepare(tx) + if prepared[i] == nil { + b.Fatal("preparation failed") + } + } + setup.Close() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + b.StopTimer() + env := f.bank(b, nil) + env.Bank.preparer = replay.NewTransactionPreparer(env.SlotCtx.Features) + b.StartTimer() + for j, tx := range f.txs { + result, _ := env.Bank.ForgePreparedTransaction(tx, len(f.wires[j]), prepared[j]) + if result != ForgeAccepted { + b.Fatalf("transaction %d: %v", j, result) + } + } + env.Bank.Freeze() + b.StopTimer() + if env.Bank.CostTracker().BlockCost() != readonlyBlockAccepted*readonlyActualCost { + b.Fatal("unexpected block cost") + } + env.Close() + b.StartTimer() + } + b.ReportMetric(float64(readonlyBlockAccepted), "tx/block") +} diff --git a/pkg/blockprod/readonly_load_bench_test.go b/pkg/blockprod/readonly_load_bench_test.go new file mode 100644 index 00000000..f5d24798 --- /dev/null +++ b/pkg/blockprod/readonly_load_bench_test.go @@ -0,0 +1,95 @@ +package blockprod + +import ( + "crypto/ed25519" + "math" + "testing" + + "github.com/Overclock-Validator/mithril/pkg/accounts" + "github.com/Overclock-Validator/mithril/pkg/features" + "github.com/Overclock-Validator/mithril/pkg/replay" + "github.com/Overclock-Validator/mithril/pkg/tpu/txfixture" + "github.com/gagliardetto/solana-go" +) + +type readonlyBenchParent struct{ mem accounts.MemAccounts } + +func (p readonlyBenchParent) GetAccount(_ uint64, key solana.PublicKey) (*accounts.Account, error) { + raw := [32]byte(key) + return p.mem.GetAccount(&raw) +} + +// Only bank admission/execution/entry building are timed. The immutable parent +// is in memory; this excludes actual AccountsDB, network and signing costs. +func BenchmarkReadonlyPairBank(b *testing.B) { + const perBank = 10000 + parent := readonlyBenchParent{accounts.NewMemAccounts()} + pool := make([]solana.PublicKey, 128) + for i := range pool { + pool[i] = solana.PublicKey{byte(i + 1), 77} + parent.mem.SetAccountWithoutLock(pool[i], &accounts.Account{Key: pool[i], Lamports: 1_000_000, Data: make([]byte, 9), Owner: solana.PublicKey{11}, RentEpoch: math.MaxUint64}) + } + txs := make([]*solana.Transaction, perBank) + key := txfixture.PayerPrivateKey() + payer := key.PublicKey() + hash := txfixture.TestBlockhash() + for i := range txs { + n := (i * 7919) % (128 * 127) + a, c := n/127, n%127 + if c >= a { + c++ + } + msg := []byte{1, 0, 2, 3} + msg = append(msg, payer[:]...) + msg = append(msg, pool[a][:]...) + msg = append(msg, pool[c][:]...) + msg = append(msg, hash[:]...) + msg = append(msg, 0) + wire := []byte{1} + wire = append(wire, ed25519.Sign(ed25519.PrivateKey(key), msg)...) + wire = append(wire, msg...) + var err error + txs[i], err = solana.TransactionFromBytes(wire) + if err != nil { + b.Fatal(err) + } + } + var env *TestEnv + var prepared []*replay.PreparedTransaction + defer func() { + if env != nil { + env.Close() + } + }() + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if i%perBank == 0 { + b.StopTimer() + if env != nil { + env.Close() + } + env = NewTestEnv(TestEnvConfig{}) + env.SlotCtx.Features.EnableFeature(features.RemoveAccountsDeltaHash, 0) + env.SlotCtx.UnrootedRead = parent + env.Bank.preparer = replay.NewTransactionPreparer(env.SlotCtx.Features) + if prepared == nil { + prepared = make([]*replay.PreparedTransaction, perBank) + for j, t := range txs { + prepared[j] = env.Bank.preparer.Prepare(t) + if prepared[j] == nil { + b.Fatal("preparation failed") + } + } + } + b.StartTimer() + } + outcome, reason := env.Bank.ForgePreparedTransaction(txs[i%perBank], 198, prepared[i%perBank]) + if outcome != ForgeAccepted { + b.Fatalf("%v / %v", outcome, reason) + } + if i%perBank == perBank-1 && env.Bank.CostTracker().BlockCost() != perBank*1028 { + b.Fatalf("unexpected cost %d", env.Bank.CostTracker().BlockCost()) + } + } +} diff --git a/pkg/blockprod/scheduler/buffer.go b/pkg/blockprod/scheduler/buffer.go index 307dc0b2..bb493529 100644 --- a/pkg/blockprod/scheduler/buffer.go +++ b/pkg/blockprod/scheduler/buffer.go @@ -4,15 +4,17 @@ import ( "container/heap" "sync" + "github.com/Overclock-Validator/mithril/pkg/replay" "github.com/gagliardetto/solana-go" ) -// MaxBufferedTxns is the hard cap on cross-slot buffered transactions. +// MaxBufferedTxns is the default cap on cross-slot buffered transactions. const MaxBufferedTxns = 2 * 65536 // entry is one buffered, scored transaction. type entry struct { - tx *solana.Transaction + tx *solana.Transaction + prepared *replay.PreparedTransaction // wire is an owned copy of the packet bytes. Parsed tx fields may alias it // (solana-go decoder slices), so it must outlive any use of tx. wire []byte @@ -25,28 +27,20 @@ type entry struct { // (e.g. cost limit). The entry is retained for cross-slot retry. skipGen uint64 - alive bool - maxIdx int - minIdx int + alive bool } type maxHeap []*entry func (h maxHeap) Len() int { return len(h) } func (h maxHeap) Less(i, j int) bool { - if h[i].reward != h[j].reward { - return h[i].reward > h[j].reward - } - return h[i].seq < h[j].seq // older first on ties + return higherPriority(h[i], h[j]) } func (h maxHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] - h[i].maxIdx = i - h[j].maxIdx = j } func (h *maxHeap) Push(x any) { e := x.(*entry) - e.maxIdx = len(*h) *h = append(*h, e) } func (h *maxHeap) Pop() any { @@ -55,7 +49,6 @@ func (h *maxHeap) Pop() any { e := old[n-1] old[n-1] = nil *h = old[:n-1] - e.maxIdx = -1 return e } @@ -71,12 +64,9 @@ func (h minHeap) Less(i, j int) bool { } func (h minHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] - h[i].minIdx = i - h[j].minIdx = j } func (h *minHeap) Push(x any) { e := x.(*entry) - e.minIdx = len(*h) *h = append(*h, e) } func (h *minHeap) Pop() any { @@ -85,7 +75,6 @@ func (h *minHeap) Pop() any { e := old[n-1] old[n-1] = nil *h = old[:n-1] - e.minIdx = -1 return e } @@ -205,7 +194,7 @@ func (b *Buffer) killLocked(e *entry) { func (b *Buffer) popMaxAliveLocked() *entry { for b.max.Len() > 0 { - e := heap.Pop(&b.max).(*entry) + e := b.max.popEntry() if !e.alive { continue } @@ -215,6 +204,44 @@ func (b *Buffer) popMaxAliveLocked() *entry { return nil } +// popEntry moves the winning child into the hole at each level. This avoids +// interface dispatch and swapping two entries at every level of a large queue. +// Ordering is identical to maxHeap.Less, including FIFO for equal rewards. +func (h *maxHeap) popEntry() *entry { + nodes := *h + root := nodes[0] + last := nodes[len(nodes)-1] + nodes[len(nodes)-1] = nil + nodes = nodes[:len(nodes)-1] + if len(nodes) > 0 { + i := 0 + for { + child := 2*i + 1 + if child >= len(nodes) { + break + } + if child+1 < len(nodes) && higherPriority(nodes[child+1], nodes[child]) { + child++ + } + if !higherPriority(nodes[child], last) { + break + } + nodes[i] = nodes[child] + i = child + } + nodes[i] = last + } + *h = nodes + return root +} + +func higherPriority(a, b *entry) bool { + if a.reward != b.reward { + return a.reward > b.reward + } + return a.seq < b.seq +} + func (b *Buffer) peekMinAliveLocked() *entry { for b.min.Len() > 0 { if b.min[0].alive { diff --git a/pkg/blockprod/scheduler/buffer_bench_test.go b/pkg/blockprod/scheduler/buffer_bench_test.go new file mode 100644 index 00000000..ccdaa3af --- /dev/null +++ b/pkg/blockprod/scheduler/buffer_bench_test.go @@ -0,0 +1,45 @@ +package scheduler + +import ( + "encoding/binary" + "testing" + + "github.com/gagliardetto/solana-go" +) + +// BenchmarkBufferDrain measures selection from a large, already-filled TPU +// queue. Transaction decoding and queue filling are outside the timed region. +func BenchmarkBufferDrain(b *testing.B) { + const count = 120000 + for _, mixed := range []bool{false, true} { + name := "equal_rewards" + if mixed { + name = "mixed_rewards" + } + b.Run(name, func(b *testing.B) { + var buffer *Buffer + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if i%count == 0 { + b.StopTimer() + buffer = NewBuffer(count) + for j := 0; j < count; j++ { + e := &entry{tx: &solana.Transaction{}, seq: uint64(j), reward: 2500} + binary.LittleEndian.PutUint64(e.messageHash[:], uint64(j)) + if mixed { + e.reward = uint64(j*7919) % 1000 + } + if result, _ := buffer.Insert(e); result != InsertAccepted { + b.Fatal("queue fill failed") + } + } + b.StartTimer() + } + if buffer.PopMax() == nil { + b.Fatal("queue drained prematurely") + } + } + }) + } +} diff --git a/pkg/blockprod/scheduler/buffer_order_test.go b/pkg/blockprod/scheduler/buffer_order_test.go new file mode 100644 index 00000000..7ed342c3 --- /dev/null +++ b/pkg/blockprod/scheduler/buffer_order_test.go @@ -0,0 +1,105 @@ +package scheduler + +import ( + "container/heap" + "encoding/binary" + "math/rand" + "sort" + "testing" + + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func TestMaxHeapRemovalMatchesSortedOrder(t *testing.T) { + rng := rand.New(rand.NewSource(417)) + for _, count := range []int{1, 2, 3, 4, 7, 8, 9, 127, 128, 129, 4095, 4096, 4097} { + var h maxHeap + want := make([]*entry, count) + for i := range want { + want[i] = &entry{seq: uint64(i), reward: uint64(rng.Intn(17))} + heap.Push(&h, want[i]) + } + sort.Slice(want, func(i, j int) bool { + if want[i].reward == want[j].reward { + return want[i].seq < want[j].seq + } + return want[i].reward > want[j].reward + }) + backing := h[:cap(h)] + for i, expected := range want { + require.Same(t, expected, h.popEntry(), "count=%d pop=%d", count, i) + require.Nil(t, backing[len(h)], "removed pointer retained") + } + } +} + +// Compare interleaved insertion, eviction, cleanup and removal with a small +// unsorted reference model. Reusing hashes after removal also exercises stale +// nodes left in the other heap without reusing the entry objects themselves. +func TestBufferMixedOperationsMatchReference(t *testing.T) { + const capacity = 64 + rng := rand.New(rand.NewSource(418)) + b := NewBuffer(capacity) + model := make(map[[32]byte]*entry) + best := func(high bool) *entry { + var found *entry + for _, e := range model { + if found == nil || (high && (e.reward > found.reward || e.reward == found.reward && e.seq < found.seq)) || + (!high && (e.reward < found.reward || e.reward == found.reward && e.seq > found.seq)) { + found = e + } + } + return found + } + for step := 0; step < 10000; step++ { + switch action := rng.Intn(10); { + case action < 7: + e := &entry{tx: &solana.Transaction{}, seq: uint64(step), reward: uint64(rng.Intn(16))} + binary.LittleEndian.PutUint64(e.messageHash[:], uint64(rng.Intn(256))) + wantResult := InsertAccepted + var wantEvicted *entry + if _, duplicate := model[e.messageHash]; duplicate { + wantResult = InsertDuplicate + } else if len(model) == capacity { + lowest := best(false) + if e.reward <= lowest.reward { + wantResult = InsertRejectedCapacity + } else { + wantEvicted = lowest + delete(model, lowest.messageHash) + } + } + if wantResult == InsertAccepted { + model[e.messageHash] = e + } + got, evicted := b.Insert(e) + require.Equal(t, wantResult, got, "step=%d", step) + require.True(t, wantEvicted == evicted, "eviction differs at step=%d", step) + case action < 9: + want := best(true) + got := b.PopMax() + require.True(t, want == got, "selection differs at step=%d", step) + if want != nil { + delete(model, want.messageHash) + } + default: + mod := uint64(rng.Intn(11)) + want := 0 + for hash, e := range model { + if e.seq%11 == mod { + delete(model, hash) + want++ + } + } + require.Equal(t, want, b.Cleanup(func(e *entry) bool { return e.seq%11 == mod })) + } + require.Equal(t, len(model), b.Len(), "step=%d", step) + } + for len(model) > 0 { + want := best(true) + require.Same(t, want, b.PopMax()) + delete(model, want.messageHash) + } + require.Nil(t, b.PopMax()) +} diff --git a/pkg/blockprod/scheduler/config_test.go b/pkg/blockprod/scheduler/config_test.go new file mode 100644 index 00000000..9f43ed51 --- /dev/null +++ b/pkg/blockprod/scheduler/config_test.go @@ -0,0 +1,28 @@ +package scheduler + +import ( + "testing" + + "github.com/Overclock-Validator/mithril/pkg/features" + "github.com/stretchr/testify/require" +) + +func TestConfiguredQueueCapacityAndPreparation(t *testing.T) { + defaults := NewWithConfig(nil, Config{}) + require.Equal(t, MaxBufferedTxns, defaults.buffer.Capacity()) + feats := features.NewFeaturesDefault() + custom := NewWithConfig(nil, Config{MaxBufferedTransactions: 2, FeatureSource: func() *features.Features { return feats }}) + require.Equal(t, 2, custom.buffer.Capacity()) + require.NotNil(t, custom.preparer.Load()) + for i := byte(1); i <= 2; i++ { + result, _ := custom.buffer.Insert(testEntry(10, uint64(i), i)) + require.Equal(t, InsertAccepted, result) + } + result, evicted := custom.buffer.Insert(testEntry(9, 3, 3)) + require.Equal(t, InsertRejectedCapacity, result) + require.Nil(t, evicted) + result, evicted = custom.buffer.Insert(testEntry(11, 4, 4)) + require.Equal(t, InsertAccepted, result) + require.Equal(t, uint64(2), evicted.seq) + require.Equal(t, 2, custom.Buffered()) +} diff --git a/pkg/blockprod/scheduler/scheduler.go b/pkg/blockprod/scheduler/scheduler.go index de6196f3..88e6435b 100644 --- a/pkg/blockprod/scheduler/scheduler.go +++ b/pkg/blockprod/scheduler/scheduler.go @@ -9,6 +9,7 @@ import ( "github.com/Overclock-Validator/mithril/pkg/blockprod" "github.com/Overclock-Validator/mithril/pkg/costmodel" "github.com/Overclock-Validator/mithril/pkg/features" + "github.com/Overclock-Validator/mithril/pkg/replay" "github.com/Overclock-Validator/mithril/pkg/tpu/packet" "github.com/gagliardetto/solana-go" ) @@ -43,9 +44,12 @@ type Stats struct { // Scheduler buffers verified TPU transactions in a reward-ordered heap and // drains into the active WorkingBank when one is published. type Scheduler struct { - banks BankSource - feats *features.Features - buffer *Buffer + banks BankSource + feats *features.Features + buffer *Buffer + preparer atomic.Pointer[replay.TransactionPreparer] + featureSource func() *features.Features + lastPreparationRefresh time.Time seq atomic.Uint64 wake chan struct{} @@ -80,6 +84,37 @@ func New(banks BankSource) *Scheduler { } } +// NewWithFeatureSource prepares queued messages against a replay-tip snapshot. +// The active bank independently verifies feature compatibility before reuse. +func NewWithFeatureSource(banks BankSource, source func() *features.Features) *Scheduler { + return NewWithConfig(banks, Config{FeatureSource: source}) +} + +// Config controls the bounded TPU queue and static preparation source. +type Config struct { + // MaxBufferedTransactions defaults to MaxBufferedTxns when zero. + MaxBufferedTransactions int + FeatureSource func() *features.Features +} + +func NewWithConfig(banks BankSource, cfg Config) *Scheduler { + s := New(banks) + if cfg.MaxBufferedTransactions > 0 { + s.buffer = NewBuffer(cfg.MaxBufferedTransactions) + } + s.featureSource = cfg.FeatureSource + s.refreshPreparation() + return s +} + +func (s *Scheduler) refreshPreparation() { + if s.featureSource == nil || time.Since(s.lastPreparationRefresh) < 100*time.Millisecond { + return + } + s.preparer.Store(replay.NewTransactionPreparer(s.featureSource())) + s.lastPreparationRefresh = time.Now() +} + // Start launches the bank-gated drain loop. func (s *Scheduler) Start(ctx context.Context) { s.startOnce.Do(func() { @@ -138,6 +173,7 @@ func (s *Scheduler) Receive(pkt packet.Packet) { e := &entry{ tx: tx, + prepared: s.preparer.Load().Prepare(tx), wire: wire, wireSize: len(wire), messageHash: messageHash, @@ -219,6 +255,7 @@ func (s *Scheduler) drainLoop(ctx context.Context) { if ctx.Err() != nil { return } + s.refreshPreparation() bank := s.banks.WorkingBank() s.noteBank(bank) if bank == nil { @@ -258,15 +295,10 @@ func (s *Scheduler) drainLoop(ctx context.Context) { continue } - // Prefer the owned wire so forge reparses from stable bytes even if the - // retained tx view was somehow mutated after buffering. - var result blockprod.ForgeResult - var reason costmodel.ExceedReason - if len(e.wire) > 0 { - result, reason = bank.Forge(e.wire) - } else { - result, reason = bank.ForgeTransaction(e.tx, e.wireSize) - } + // Receive owns the wire and its decoded transaction for the entire queue + // lifetime. Execution modifies transaction-local account clones, not + // this immutable message, so reuse the decoded transaction across banks. + result, reason := bank.ForgePreparedTransaction(e.tx, e.wireSize, e.prepared) switch result { case blockprod.ForgeDroppedNoLeader: s.rebuffer(e) diff --git a/pkg/blockprod/scheduler/scheduler_test.go b/pkg/blockprod/scheduler/scheduler_test.go index e4b74e09..f8371131 100644 --- a/pkg/blockprod/scheduler/scheduler_test.go +++ b/pkg/blockprod/scheduler/scheduler_test.go @@ -7,6 +7,7 @@ import ( "github.com/Overclock-Validator/mithril/pkg/blockprod" "github.com/Overclock-Validator/mithril/pkg/costmodel" + "github.com/Overclock-Validator/mithril/pkg/features" "github.com/Overclock-Validator/mithril/pkg/fees" "github.com/Overclock-Validator/mithril/pkg/tpu/packet" "github.com/Overclock-Validator/mithril/pkg/tpu/txfixture" @@ -244,7 +245,9 @@ func TestMaxBufferedTxnsConstant(t *testing.T) { } func TestSchedulerCopiesPooledPacketBytes(t *testing.T) { - sched := New(blockprod.NewController()) + env := blockprod.NewTestEnv(blockprod.TestEnvConfig{}) + defer env.Close() + sched := NewWithFeatureSource(blockprod.NewController(), func() *features.Features { return env.SlotCtx.Features.Clone() }) pool := packet.NewPool(1) buf, idx, ok := pool.Acquire() require.True(t, ok) @@ -269,11 +272,26 @@ func TestSchedulerCopiesPooledPacketBytes(t *testing.T) { e := sched.buffer.PopMax() require.NotNil(t, e) require.Equal(t, wire, e.wire) + require.NotNil(t, e.prepared) // Re-parse and verify the buffered transaction still has a valid signature. tx, err := solana.TransactionFromBytes(e.wire) require.NoError(t, err) require.Equal(t, e.tx.Signatures[0], tx.Signatures[0]) + + // Exercise the actual drain path using the retained decoded transaction, + // after its original pooled packet has already been overwritten. + res, _ := sched.buffer.Insert(e) + require.Equal(t, InsertAccepted, res) + sched.banks = env.Controller + sched.Start(context.Background()) + defer sched.Stop() + require.Eventually(t, func() bool { return sched.Stats().Accepted == 1 }, time.Second, time.Millisecond) + forged := env.Bank.ForgedTransactions() + require.Len(t, forged, 1) + forgedWire, err := forged[0].MarshalBinary() + require.NoError(t, err) + require.Equal(t, wire, forgedWire) } func TestClassifyBufferedExpired(t *testing.T) { diff --git a/pkg/config/config.go b/pkg/config/config.go index 1e7416c9..d37fb3c0 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -235,6 +235,8 @@ type ValidatorConfig struct { TPUQUICBindAddr string `toml:"tpu_quic_bind_addr" mapstructure:"tpu_quic_bind_addr"` AdvertisedIP string `toml:"advertised_ip" mapstructure:"advertised_ip"` TPUSigverifyWorkers int `toml:"tpu_sigverify_workers" mapstructure:"tpu_sigverify_workers"` + BlockCompletionReserveMs int `toml:"block_completion_reserve_ms" mapstructure:"block_completion_reserve_ms"` + TPUMaxBufferedTransactions int `toml:"tpu_max_buffered_transactions" mapstructure:"tpu_max_buffered_transactions"` } // Config holds all configuration options for Mithril (Firedancer-style hierarchy) diff --git a/pkg/costmodel/entry_bytes.go b/pkg/costmodel/entry_bytes.go index ab91b8d5..7cfec648 100644 --- a/pkg/costmodel/entry_bytes.go +++ b/pkg/costmodel/entry_bytes.go @@ -28,8 +28,11 @@ func PackEntryBytesMax(slotMaxDataShreds, maxMicroblock uint64) uint64 { // DefaultPackEntryBytes is min(shred-safe, SIMD-0525) minus one ending tick. func DefaultPackEntryBytes() uint64 { - shredSafe := PackEntryBytesMax(DefaultMaxDataShredsPerSlot, MaxMicroblockBytes) - cap := uint64(DefaultMaxEntryBytesPerSlot) + return packEntryBytes(DefaultMaxDataShredsPerSlot, DefaultMaxEntryBytesPerSlot) +} + +func packEntryBytes(maxDataShreds, cap uint64) uint64 { + shredSafe := PackEntryBytesMax(maxDataShreds, MaxMicroblockBytes) if shredSafe > 0 && shredSafe < cap { cap = shredSafe } diff --git a/pkg/costmodel/limits.go b/pkg/costmodel/limits.go index 2e476656..999cde5b 100644 --- a/pkg/costmodel/limits.go +++ b/pkg/costmodel/limits.go @@ -1,7 +1,11 @@ package costmodel import ( + "fmt" + "github.com/Overclock-Validator/mithril/pkg/features" + "github.com/Overclock-Validator/mithril/pkg/safemath" + "github.com/Overclock-Validator/mithril/pkg/sealevel" "github.com/gagliardetto/solana-go" ) @@ -75,11 +79,53 @@ func DefaultLimits() Limits { } } -// LimitsForFeatures returns the cost limits selected by the bank's feature set. +// LimitsForFeatures returns the legacy 400ms budgets. Live banks must use +// LimitsForSlot to apply slot-time reductions at the correct epoch boundary. func LimitsForFeatures(feats *features.Features) Limits { limits := DefaultLimits() if feats != nil && feats.IsActive(features.RaiseBlockLimitsTo100m) { limits.BlockCost = MaxBlockUnitsSIMD0286 + limits.WritableAccountCost = 40_000_000 } return limits } + +// LimitsForSlot mirrors Agave v4.3.0-rc.1 runtime/slot_params.rs. A slot-time +// gate takes effect in the epoch after activation; among effective gates the +// shortest duration wins, even if longer-duration gates activate later. +func LimitsForSlot(feats *features.Features, schedule *sealevel.SysvarEpochSchedule, slot uint64) (Limits, error) { + limits := DefaultLimits() + for _, transition := range []struct { + gate features.FeatureGate + account, block, data, shreds, entries uint64 + }{ + {features.ReduceSlotTimeTo350ms, 21_000_000, 52_500_000, 87_500_000, 28_672, 18_350_080}, + {features.ReduceSlotTimeTo300ms, 18_000_000, 45_000_000, 75_000_000, 24_576, 15_728_640}, + {features.ReduceSlotTimeTo250ms, 15_000_000, 37_500_000, 62_500_000, 20_480, 13_107_200}, + {features.ReduceSlotTimeTo200ms, 12_000_000, 30_000_000, 50_000_000, 16_384, 10_485_760}, + } { + if feats == nil { + break + } + activation, active := feats.ActivationSlot(transition.gate) + if !active { + continue + } + if schedule == nil || schedule.SlotsPerEpoch == 0 { + return Limits{}, fmt.Errorf("epoch schedule required for slot-time cost limits") + } + effective := schedule.FirstSlotInEpoch(safemath.SaturatingAddU64(schedule.GetEpoch(activation), 1)) + if effective > slot { + continue + } + limits.WritableAccountCost = transition.account + limits.BlockCost = transition.block + limits.AllocatedDataSizeDelta = transition.data + limits.MaxEntryBytes = packEntryBytes(transition.shreds, transition.entries) + } + if feats != nil && feats.IsActive(features.RaiseBlockLimitsTo100m) { + limits.BlockCost = limits.BlockCost * 100 / 60 + limits.WritableAccountCost = limits.WritableAccountCost * 100 / 60 + } + return limits, nil +} diff --git a/pkg/costmodel/slot_limits_test.go b/pkg/costmodel/slot_limits_test.go new file mode 100644 index 00000000..0d521e74 --- /dev/null +++ b/pkg/costmodel/slot_limits_test.go @@ -0,0 +1,88 @@ +package costmodel + +import ( + "testing" + + "github.com/Overclock-Validator/mithril/pkg/features" + "github.com/Overclock-Validator/mithril/pkg/sealevel" + "github.com/stretchr/testify/require" +) + +func TestSlotLimitsMatchAgaveTable(t *testing.T) { + schedule := &sealevel.SysvarEpochSchedule{SlotsPerEpoch: 100} + for _, tc := range []struct { + name string + gate features.FeatureGate + account, block, data, shreds, entries uint64 + }{ + {"400ms", features.FeatureGate{}, 24_000_000, 60_000_000, 100_000_000, 32768, 20 * 1024 * 1024}, + {"350ms", features.ReduceSlotTimeTo350ms, 21_000_000, 52_500_000, 87_500_000, 28672, 18_350_080}, + {"300ms", features.ReduceSlotTimeTo300ms, 18_000_000, 45_000_000, 75_000_000, 24576, 15_728_640}, + {"250ms", features.ReduceSlotTimeTo250ms, 15_000_000, 37_500_000, 62_500_000, 20480, 13_107_200}, + {"200ms", features.ReduceSlotTimeTo200ms, 12_000_000, 30_000_000, 50_000_000, 16384, 10_485_760}, + } { + t.Run(tc.name, func(t *testing.T) { + f := features.NewFeaturesDefault() + if tc.name != "400ms" { + f.EnableFeature(tc.gate, 50) + } + before, err := LimitsForSlot(f, schedule, 99) + require.NoError(t, err) + require.Equal(t, DefaultLimits(), before) + for _, raise := range []bool{false, true} { + if raise { + f.EnableFeature(features.RaiseBlockLimitsTo100m, 0) + } + got, err := LimitsForSlot(f, schedule, 100) + require.NoError(t, err) + account, block := tc.account, tc.block + if raise { + account = account * 100 / 60 + block = block * 100 / 60 + } + require.Equal(t, account, got.WritableAccountCost) + require.Equal(t, block, got.BlockCost) + require.Equal(t, tc.data, got.AllocatedDataSizeDelta) + require.Equal(t, tc.entries-EntryHeaderBytes, got.MaxEntryBytes) + require.LessOrEqual(t, got.MaxEntryBytes, PackEntryBytesMax(tc.shreds, MaxMicroblockBytes)) + require.Equal(t, uint64(DefaultTargetBatchBytes), got.MaxBatchBytes) + } + }) + } +} + +func TestSlotLimitsDoNotLengthenSlotsForLaterGates(t *testing.T) { + f := features.NewFeaturesDefault() + f.EnableFeature(features.ReduceSlotTimeTo200ms, 50) + f.EnableFeature(features.ReduceSlotTimeTo350ms, 150) + schedule := &sealevel.SysvarEpochSchedule{SlotsPerEpoch: 100} + for _, slot := range []uint64{100, 199, 200, 400} { + limits, err := LimitsForSlot(f, schedule, slot) + require.NoError(t, err) + require.Equal(t, uint64(30_000_000), limits.BlockCost) + } +} + +func TestSlotLimitsActivationDuringWarmup(t *testing.T) { + f := features.NewFeaturesDefault() + f.EnableFeature(features.ReduceSlotTimeTo200ms, 40) + // Epoch 0: [0,32), epoch 1: [32,96), normal epoch 2: [96,224). + schedule := &sealevel.SysvarEpochSchedule{Warmup: true, SlotsPerEpoch: 128, FirstNormalEpoch: 2, FirstNormalSlot: 96} + before, err := LimitsForSlot(f, schedule, 95) + require.NoError(t, err) + require.Equal(t, uint64(60_000_000), before.BlockCost) + after, err := LimitsForSlot(f, schedule, 96) + require.NoError(t, err) + require.Equal(t, uint64(30_000_000), after.BlockCost) +} + +func TestSlotLimitsRequireScheduleForActiveReductions(t *testing.T) { + f := features.NewFeaturesDefault() + _, err := LimitsForSlot(f, nil, 10) + require.NoError(t, err) + f.EnableFeature(features.ReduceSlotTimeTo200ms, 0) + _, err = LimitsForSlot(f, nil, 10) + require.Error(t, err) + _, err = LimitsForSlot(f, &sealevel.SysvarEpochSchedule{}, 10) + require.Error(t, err) +} diff --git a/pkg/costmodel/transaction_cost.go b/pkg/costmodel/transaction_cost.go index 1dd4f0c7..3a804b03 100644 --- a/pkg/costmodel/transaction_cost.go +++ b/pkg/costmodel/transaction_cost.go @@ -55,6 +55,13 @@ func EstimateTransactionCost(tx *solana.Transaction, feats *features.Features) ( }, nil } + return EstimatePreparedTransactionCost(tx, instrs, limits, feats), nil +} + +// EstimatePreparedTransactionCost reuses successfully parsed instructions and +// compute limits from the same immutable transaction and feature snapshot. +func EstimatePreparedTransactionCost(tx *solana.Transaction, instrs []sealevel.Instruction, limits *sealevel.ComputeBudgetLimits, feats *features.Features) TransactionCost { + writable := writableAccounts(tx) loadedDataCost := loadedAccountsDataSizeCost(limits.LoadedAccountBytes) // Banking-stage admission must reserve at least one page for the fee // payer, including V1 transactions whose inline loaded-data limit is zero. @@ -62,13 +69,13 @@ func EstimateTransactionCost(tx *solana.Transaction, feats *features.Features) ( loadedDataCost = max(loadedDataCost, uint64(HeapCost)) return TransactionCost{ SignatureCost: signatureCost(tx, instrs, feats), - WriteLockCost: writeLockCost(countWriteLocks(tx)), + WriteLockCost: writeLockCost(uint64(len(writable))), DataBytesCost: instructionDataCost(tx), ProgramsExecutionCost: uint64(limits.ComputeUnitLimit), LoadedAccountsDataSizeCost: loadedDataCost, AllocatedAccountsDataSize: estimateAllocDelta(instrs, feats), - WritableAccounts: writableAccounts(tx), - }, nil + WritableAccounts: writable, + } } func signatureCost(tx *solana.Transaction, instrs []sealevel.Instruction, feats *features.Features) uint64 { @@ -177,7 +184,7 @@ func replayInstrsAndAcctMetas(tx *solana.Transaction, feats *features.Features) } upgradeableLoaderPresent := false for _, key := range tx.Message.AccountKeys { - if key.String() == "BPFLoaderUpgradeab1e11111111111111111111111" { + if key == addresses.BpfLoaderUpgradeableAddr { upgradeableLoaderPresent = true break } diff --git a/pkg/merkletree/merkletree.go b/pkg/merkletree/merkletree.go index 988f2633..0de1d98f 100644 --- a/pkg/merkletree/merkletree.go +++ b/pkg/merkletree/merkletree.go @@ -46,8 +46,29 @@ func (n *Nodes) GetRoot() (out *[32]byte) { return &n.Nodes[len(n.Nodes)-1] } -// TODO provide a method for memory-efficient Merkle construction when only the root is requested. -// Can be implemented using recursion root level downwards +// HashRoot computes the same root as HashNodes without retaining proof nodes. +// Empty input returns zero. Each level overwrites the preceding level, so only +// one hash per leaf is allocated. Leaves are never modified. +func HashRoot(leaves [][]byte) (root [32]byte) { + if len(leaves) == 0 { + return root + } + if len(leaves) == 1 { + return HashLeaf(leaves[0]) + } + nodes := make([][32]byte, len(leaves)) + for i, leaf := range leaves { + nodes[i] = HashLeaf(leaf) + } + for len(nodes) > 1 { + for i := 0; i < len(nodes); i += 2 { + right := min(i+1, len(nodes)-1) + nodes[i/2] = HashIntermediate(&nodes[i], &nodes[right]) + } + nodes = nodes[:(len(nodes)+1)/2] + } + return nodes[0] +} // HashNodes constructs proof data from a set of leaves. // @@ -97,6 +118,14 @@ func HashNodes(leaves [][]byte) (out Nodes) { // HashLeaf returns the hash of a leaf node. func HashLeaf(data []byte) (out [32]byte) { + if len(data) == 64 { + // Transaction signatures are fixed-width leaves. A single buffer avoids + // incremental hash writes while retaining the leaf domain separator. + var input [65]byte + input[0] = TypeLeaf + copy(input[1:], data) + return sha256.Sum256(input[:]) + } h := sha256.New() h.Write([]byte{TypeLeaf}) h.Write(data) @@ -106,12 +135,11 @@ func HashLeaf(data []byte) (out [32]byte) { // HashIntermediate returns the hash of an intermediate node. func HashIntermediate(left *[32]byte, right *[32]byte) (out [32]byte) { - h := sha256.New() - h.Write([]byte{TypeIntermediate}) - h.Write(left[:]) - h.Write(right[:]) - h.Sum(out[:0]) - return + var input [65]byte + input[0] = TypeIntermediate + copy(input[1:33], left[:]) + copy(input[33:], right[:]) + return sha256.Sum256(input[:]) } // nextLevelLen returns the amount of nodes in the layer above the current one, diff --git a/pkg/merkletree/root_test.go b/pkg/merkletree/root_test.go new file mode 100644 index 00000000..1437c383 --- /dev/null +++ b/pkg/merkletree/root_test.go @@ -0,0 +1,60 @@ +package merkletree + +import ( + "bytes" + "crypto/sha256" + "fmt" + "testing" +) + +// Independent construction: retain separate levels and use one-shot SHA256 +// over explicit domain-prefixed bytes, without the production hash helpers. +func referenceRoot(leaves [][]byte) [32]byte { + if len(leaves) == 0 { + return [32]byte{} + } + level := make([][32]byte, len(leaves)) + for i, leaf := range leaves { + level[i] = sha256.Sum256(append([]byte{0}, leaf...)) + } + for len(level) > 1 { + next := make([][32]byte, (len(level)+1)/2) + for i := range next { + left, right := level[2*i], level[min(2*i+1, len(level)-1)] + data := append([]byte{1}, left[:]...) + data = append(data, right[:]...) + next[i] = sha256.Sum256(data) + } + level = next + } + return level[0] +} + +func TestHashRootMatchesCanonicalTree(t *testing.T) { + counts := []int{0, 1, 2, 3, 7, 8, 9, 31, 32, 33, 63, 64, 65, 127, 128, 129, 255, 256, 257, 311, 511, 512, 513, 1023, 1024, 1025} + for _, size := range []int{0, 31, 32, 55, 56, 63, 64, 65, 1232} { + for _, count := range counts { + t.Run(fmt.Sprintf("%dx%d", count, size), func(t *testing.T) { + leaves := make([][]byte, count) + for i := range leaves { + leaves[i] = make([]byte, size) + for j := range leaves[i] { + leaves[i][j] = byte(i*71 + i/256 + j*17) + } + } + before := bytes.Join(leaves, nil) + want := referenceRoot(leaves) + if got := HashRoot(leaves); got != want { + t.Fatalf("root %x, want %x", got, want) + } + nodes := HashNodes(leaves) + if got := nodes.GetRoot(); got != nil && *got != want { + t.Fatalf("proof root %x, want %x", *got, want) + } + if !bytes.Equal(before, bytes.Join(leaves, nil)) { + t.Fatal("hashing modified input leaves") + } + }) + } + } +} diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index fee3c92f..3f87fa28 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -15,8 +15,18 @@ func (t *Timing) AddTiming(d time.Duration) { atomic.AddUint64(&t.SumNanoseconds, uint64(d.Nanoseconds())) } +// StartTiming avoids reading the clock when a caller does not record timings. +func StartTiming(enabled bool) time.Time { + if enabled { + return time.Now() + } + return time.Time{} +} + func (t *Timing) AddTimingSince(start time.Time) { - t.AddTiming(time.Since(start)) + if !start.IsZero() { + t.AddTiming(time.Since(start)) + } } // AccountLoader is the per-slot decomposition of LoadBlockAccounts. Counters diff --git a/pkg/replay/chain_state.go b/pkg/replay/chain_state.go index 649146c9..70c404b2 100644 --- a/pkg/replay/chain_state.go +++ b/pkg/replay/chain_state.go @@ -266,3 +266,14 @@ func ChainTipFeatureActive(gate features.FeatureGate) bool { defer chainTipMu.RUnlock() return chainTipFeatures != nil && chainTipFeatures.IsActive(gate) } + +// ChainTipFeatures returns an independent feature snapshot for queued transaction +// preparation. Bank admission checks compatibility again before reuse. +func ChainTipFeatures() *features.Features { + chainTipMu.RLock() + defer chainTipMu.RUnlock() + if chainTipFeatures == nil { + return nil + } + return chainTipFeatures.Clone() +} diff --git a/pkg/replay/transaction_preparation.go b/pkg/replay/transaction_preparation.go new file mode 100644 index 00000000..303dcc3e --- /dev/null +++ b/pkg/replay/transaction_preparation.go @@ -0,0 +1,133 @@ +package replay + +import ( + "crypto/sha256" + "encoding/binary" + "sort" + + "github.com/Overclock-Validator/mithril/pkg/costmodel" + "github.com/Overclock-Validator/mithril/pkg/features" + "github.com/Overclock-Validator/mithril/pkg/fees" + "github.com/Overclock-Validator/mithril/pkg/sealevel" + "github.com/Overclock-Validator/mithril/pkg/txverify" + "github.com/gagliardetto/solana-go" +) + +// TransactionPreparer binds static transaction preparation to an immutable bank +// feature snapshot. Its source must remain immutable for the bank's lifetime. +// Prepared messages must also remain immutable, including their backing bytes. +// Account contents, transaction age and duplicate status are never cached here. +type TransactionPreparer struct { + source *features.Features + feats *features.Features + key [32]byte +} + +// PreparedTransaction contains only message/feature-derived data. Private fields +// prevent callers from substituting an unchecked hash, cost or instruction list. +type PreparedTransaction struct { + tx *solana.Transaction + key [32]byte + hash [32]byte + cost costmodel.TransactionCost + instrs []sealevel.Instruction + instructionAccts [][]sealevel.InstructionAccount + accountMetas []*solana.AccountMeta + limits *sealevel.ComputeBudgetLimits +} + +func NewTransactionPreparer(f *features.Features) *TransactionPreparer { + if f == nil { + return nil + } + clone := f.Clone() + gates := make([]features.FeatureGate, 0, len(*clone)) + for gate := range *clone { + gates = append(gates, gate) + } + sort.Slice(gates, func(i, j int) bool { + if gates[i].Address != gates[j].Address { + return string(gates[i].Address[:]) < string(gates[j].Address[:]) + } + return gates[i].Name < gates[j].Name + }) + h := sha256.New() + var value [8]byte + for _, gate := range gates { + info := (*clone)[gate] + h.Write(gate.Address[:]) + binary.LittleEndian.PutUint64(value[:], uint64(len(gate.Name))) + h.Write(value[:]) + h.Write([]byte(gate.Name)) + if info.Enabled { + h.Write([]byte{1}) + } else { + h.Write([]byte{0}) + } + binary.LittleEndian.PutUint64(value[:], info.ActivationSlot) + h.Write(value[:]) + } + p := &TransactionPreparer{source: f, feats: clone} + copy(p.key[:], h.Sum(nil)) + return p +} + +// Prepare returns nil on a static validation failure. Callers retain their normal +// processing path in that case, preserving its error classification and ordering. +func (p *TransactionPreparer) Prepare(tx *solana.Transaction) *PreparedTransaction { + if p == nil || tx == nil { + return nil + } + if tx.Message.GetVersion() == solana.MessageVersionV1 && !p.feats.IsActive(features.EnableTxV1) { + return nil + } + if txverify.SanitizeTransaction(tx) != nil { + return nil + } + if p.feats.IsActive(features.StaticInstructionLimit) && len(tx.Message.Instructions) > maxInstrTraceCapacity { + return nil + } + instrs, instructionAccts, metas, err := instrsAndAcctMetasFromTx(tx, p.feats) + if err != nil { + return nil + } + limits, err := sealevel.ComputeBudgetLimitsForTransaction(tx, instrs, p.feats) + if err != nil { + return nil + } + hash, err := TransactionMessageHash(tx) + if err != nil { + return nil + } + return &PreparedTransaction{tx: tx, key: p.key, hash: hash, + cost: costmodel.EstimatePreparedTransactionCost(tx, instrs, limits, p.feats), + instrs: instrs, instructionAccts: instructionAccts, accountMetas: metas, limits: limits} +} + +func (p *TransactionPreparer) Matches(prepared *PreparedTransaction, tx *solana.Transaction, f *features.Features) bool { + return p != nil && p.source == f && prepared != nil && prepared.tx == tx && p.key == prepared.key +} + +func (p *PreparedTransaction) MessageHash() [32]byte { return p.hash } + +// Cost returns the estimate; its writable-account slice is read-only. +func (p *PreparedTransaction) Cost() costmodel.TransactionCost { return p.cost } + +// LoadAndExecute reuses preparation only for the same immutable message and a +// matching feature snapshot. All bank-dependent checks still run on every call. +func (p *TransactionPreparer) LoadAndExecute(input LoadAndExecuteTransactionInput, prepared *PreparedTransaction) LoadAndExecuteTransactionOutput { + if input.SlotCtx == nil || p == nil || p.source != input.SlotCtx.Features || !p.Matches(prepared, input.Transaction, input.SlotCtx.Features) { + return LoadAndExecuteTransaction(input) + } + return loadAndExecuteTransaction(input, prepared) +} + +// PayerCanFund keeps strict leader admission, using current payer state while +// sharing the already-validated instructions and compute limits. +func (p *TransactionPreparer) PayerCanFund(slotCtx *sealevel.SlotCtx, tx *solana.Transaction, prepared *PreparedTransaction) error { + if slotCtx == nil || !p.Matches(prepared, tx, slotCtx.Features) { + return fees.PayerCanFund(slotCtx, tx) + } + _, err := fees.ValidateTransactionFeePayer(slotCtx, tx, prepared.instrs, prepared.limits) + return err +} diff --git a/pkg/replay/transaction_processing_pure.go b/pkg/replay/transaction_processing_pure.go index 061703db..43434646 100644 --- a/pkg/replay/transaction_processing_pure.go +++ b/pkg/replay/transaction_processing_pure.go @@ -3,7 +3,6 @@ package replay import ( "errors" "math" - "time" "github.com/Overclock-Validator/mithril/pkg/accounts" "github.com/Overclock-Validator/mithril/pkg/arena" @@ -39,6 +38,10 @@ type LoadAndExecuteTransactionInput struct { // banks also skip writable-account result materialization; RPC simulation // leaves this false to retain the rich result. LeanResult bool + // SkipTimingMetrics omits the detailed transaction and instruction-dispatch + // timings for leader execution. Replay and simulation retain their default + // instrumentation; program-specific instrumentation is independent. + SkipTimingMetrics bool // CapturePreBalances retains pre-fee balances in lean mode. Rich mode // always captures them for RPC compatibility. CapturePreBalances bool @@ -124,6 +127,10 @@ func feeOnlyRollbackAccountsDataSize(slotCtx *sealevel.SlotCtx, tx *solana.Trans } func LoadAndExecuteTransaction(input LoadAndExecuteTransactionInput) LoadAndExecuteTransactionOutput { + return loadAndExecuteTransaction(input, nil) +} + +func loadAndExecuteTransaction(input LoadAndExecuteTransactionInput, prepared *PreparedTransaction) LoadAndExecuteTransactionOutput { tx := input.Transaction slotCtx := input.SlotCtx @@ -131,64 +138,76 @@ func LoadAndExecuteTransaction(input LoadAndExecuteTransactionInput) LoadAndExec input.Arena.Reset() } - if tx == nil || slotCtx == nil || slotCtx.Features == nil { - return sanitizeFailureOutput() - } + var instrs []sealevel.Instruction + var instructionAcctsPerInstr [][]sealevel.InstructionAccount + var txAcctMetas []*solana.AccountMeta + var computeBudgetLimits *sealevel.ComputeBudgetLimits + var err error + start := metrics.StartTiming(false) + if prepared != nil { + instrs, instructionAcctsPerInstr, txAcctMetas = prepared.instrs, prepared.instructionAccts, prepared.accountMetas + computeBudgetLimits = prepared.limits + } else { + if tx == nil || slotCtx == nil || slotCtx.Features == nil { + return sanitizeFailureOutput() + } - // Match Agave's Bank verification order: once a transaction has decoded as - // v1, the feature gate is checked before structural sanitization. - if tx.Message.GetVersion() == solana.MessageVersionV1 && !slotCtx.Features.IsActive(features.EnableTxV1) { - return LoadAndExecuteTransactionOutput{ - ProcessingResult: TransactionProcessingResult{ - TransactionError: &TransactionError{ - ErrorType: TransactionErrorUnsupportedVersion, - InstructionError: TxErrUnsupportedVersion, + // Match Agave's Bank verification order: once a transaction has decoded as + // v1, the feature gate is checked before structural sanitization. + if tx.Message.GetVersion() == solana.MessageVersionV1 && !slotCtx.Features.IsActive(features.EnableTxV1) { + return LoadAndExecuteTransactionOutput{ + ProcessingResult: TransactionProcessingResult{ + TransactionError: &TransactionError{ + ErrorType: TransactionErrorUnsupportedVersion, + InstructionError: TxErrUnsupportedVersion, + }, }, - }, + } + } + // Reject malformed transactions before account-indexed code can observe + // them. The helper also handles Mithril's already-resolved v0 messages. + if err := txverify.SanitizeTransaction(tx); err != nil { + return sanitizeFailureOutput() + } + // Mirror block-replay's StaticInstructionLimit cap so pre-activation + // clusters fail mid-execution like Agave instead of SanitizeFailure. + if slotCtx.Features.IsActive(features.StaticInstructionLimit) && + len(tx.Message.Instructions) > maxInstrTraceCapacity { + return sanitizeFailureOutput() } - } - // Reject malformed transactions before account-indexed code can observe - // them. The helper also handles Mithril's already-resolved v0 messages. - if err := txverify.SanitizeTransaction(tx); err != nil { - return sanitizeFailureOutput() - } - // Mirror block-replay's StaticInstructionLimit cap so pre-activation - // clusters fail mid-execution like Agave instead of SanitizeFailure. - if slotCtx.Features.IsActive(features.StaticInstructionLimit) && - len(tx.Message.Instructions) > maxInstrTraceCapacity { - return sanitizeFailureOutput() - } - // Parse instructions and account metas - start := time.Now() - instrs, instructionAcctsPerInstr, txAcctMetas, err := instrsAndAcctMetasFromTx(tx, slotCtx.Features) - if err != nil { - return LoadAndExecuteTransactionOutput{ - ProcessingResult: TransactionProcessingResult{ - TransactionError: &TransactionError{ - ErrorType: TransactionErrorSanitizeFailure, - InstructionError: err, + // Parse instructions and account metas + start = metrics.StartTiming(!input.SkipTimingMetrics) + instrs, instructionAcctsPerInstr, txAcctMetas, err = instrsAndAcctMetasFromTx(tx, slotCtx.Features) + if err != nil { + return LoadAndExecuteTransactionOutput{ + ProcessingResult: TransactionProcessingResult{ + TransactionError: &TransactionError{ + ErrorType: TransactionErrorSanitizeFailure, + InstructionError: err, + }, }, - }, + } } - } - metrics.GlobalBlockReplay.InstructionsAndAccountMetasFromTx.AddTimingSince(start) - - // Compute budget limits - start = time.Now() - computeBudgetLimits, err := sealevel.ComputeBudgetLimitsForTransaction(tx, instrs, slotCtx.Features) - if err != nil { - return LoadAndExecuteTransactionOutput{ - ProcessingResult: TransactionProcessingResult{ - TransactionError: &TransactionError{ - ErrorType: TransactionErrorInstructionError, - InstructionError: err, + metrics.GlobalBlockReplay.InstructionsAndAccountMetasFromTx.AddTimingSince(start) + + // Compute budget limits + start = metrics.StartTiming(!input.SkipTimingMetrics) + computeBudgetLimits, err = sealevel.ComputeBudgetLimitsForTransaction(tx, instrs, slotCtx.Features) + if err != nil { + return LoadAndExecuteTransactionOutput{ + ProcessingResult: TransactionProcessingResult{ + TransactionError: &TransactionError{ + ErrorType: TransactionErrorInstructionError, + InstructionError: err, + }, }, - }, - Instrs: instrs, + Instrs: instrs, + } } + metrics.GlobalBlockReplay.ComputeBudgetExecutionInstructions.AddTimingSince(start) + } - metrics.GlobalBlockReplay.ComputeBudgetExecutionInstructions.AddTimingSince(start) // Validate transaction age if !sealevel.IsTransactionAgeValid(tx, instrs, slotCtx) { @@ -236,7 +255,7 @@ func LoadAndExecuteTransaction(input LoadAndExecuteTransactionInput) LoadAndExec } // Load and validate accounts - start = time.Now() + start = metrics.StartTiming(!input.SkipTimingMetrics) instructionsSysvarIdx := instructionsSysvarAccountIndex(tx) var instrsAcct *accounts.Account if instructionsSysvarIdx >= 0 { @@ -303,6 +322,7 @@ func LoadAndExecuteTransaction(input LoadAndExecuteTransactionInput) LoadAndExec execCtx.TransactionContext.Signature = tx.Signatures[0] execCtx.TransactionContext.BorrowedAccountArena = input.Arena execCtx.IsSimulation = input.IsSimulation + execCtx.SkipTimingMetrics = input.SkipTimingMetrics execCtx.RecordInnerInstructions = input.RecordInnerInstructions // Capture pre-balance lamports (before fee deduction) @@ -332,7 +352,7 @@ func LoadAndExecuteTransaction(input LoadAndExecuteTransactionInput) LoadAndExec // Calculate and deduct fees. RentForSlot supplies the exemption // minimum so a rent-exempt payer is rejected before instructions run. - start = time.Now() + start = metrics.StartTiming(!input.SkipTimingMetrics) txFeeInfo, _, err := fees.CalculateAndDeductTxFees(tx, input.TxMeta, instrs, &execCtx.TransactionContext.Accounts, computeBudgetLimits, slotCtx.Features, fees.RentForSlot(slotCtx), input.IsSimulation) if err != nil { errType, accountIndex := feePayerTransactionError(err) @@ -355,7 +375,7 @@ func LoadAndExecuteTransaction(input LoadAndExecuteTransactionInput) LoadAndExec metrics.GlobalBlockReplay.CalcAndDeductFees.AddTimingSince(start) // Read rent sysvar - start = time.Now() + start = metrics.StartTiming(!input.SkipTimingMetrics) rentSysvar, err := sealevel.ReadRentSysvar(execCtx) if err != nil { // Rent sysvar unreadable; return cleanly so the RPC worker @@ -374,18 +394,18 @@ func LoadAndExecuteTransaction(input LoadAndExecuteTransactionInput) LoadAndExec metrics.GlobalBlockReplay.ReadRentSysvar.AddTimingSince(start) // Set rent-exempt rent epoch max and compute pre-tx rent states - start = time.Now() + start = metrics.StartTiming(!input.SkipTimingMetrics) rent.MaybeSetRentExemptRentEpochMax(slotCtx, &rentSysvar, &execCtx.Features, &execCtx.TransactionContext.Accounts) preTxRentStates := rent.NewRentStateInfo(&rentSysvar, execCtx.TransactionContext, &execCtx.Features) metrics.GlobalBlockReplay.PreTxRentStates.AddTimingSince(start) // Execute all instructions var instrErr error - start = time.Now() + start = metrics.StartTiming(!input.SkipTimingMetrics) for instrIdx, instr := range tx.Message.Instructions { execCtx.SetCurrentTopLevelInstr(uint8(instrIdx)) if instructionsSysvarIdx >= 0 { - ixStart := time.Now() + ixStart := metrics.StartTiming(!input.SkipTimingMetrics) err = fixupInstructionsSysvarAcct(execCtx, instructionsSysvarIdx, uint16(instrIdx)) if err != nil { instrErr = err @@ -421,7 +441,7 @@ func LoadAndExecuteTransaction(input LoadAndExecuteTransactionInput) LoadAndExec metrics.GlobalBlockReplay.IxLoop.AddTimingSince(start) // Check rent state transitions - start = time.Now() + start = metrics.StartTiming(!input.SkipTimingMetrics) postTxRentStates := rent.NewRentStateInfo(&rentSysvar, execCtx.TransactionContext, &execCtx.Features) rentStateErr := rent.VerifyRentStateChanges(preTxRentStates, postTxRentStates, execCtx.TransactionContext) metrics.GlobalBlockReplay.PostTxRentStates.AddTimingSince(start) diff --git a/pkg/sealevel/execution_ctx.go b/pkg/sealevel/execution_ctx.go index 348b4647..ba54cc26 100644 --- a/pkg/sealevel/execution_ctx.go +++ b/pkg/sealevel/execution_ctx.go @@ -5,7 +5,6 @@ import ( "fmt" "sync" "sync/atomic" - "time" "github.com/Overclock-Validator/mithril/pkg/accounts" "github.com/Overclock-Validator/mithril/pkg/accountsdb" @@ -19,6 +18,9 @@ import ( ) type ExecutionCtx struct { + // SkipTimingMetrics disables instruction-dispatch timing collection for + // leader execution; it never changes instruction validation or CU charging. + SkipTimingMetrics bool Log Logger Accounts accounts.Accounts TransactionContext *TransactionCtx @@ -237,14 +239,14 @@ func (execCtx *ExecutionCtx) PrepareInstruction(ix Instruction, signers []solana } func (execCtx *ExecutionCtx) ProcessInstruction(instrData []byte, instructionAccts []InstructionAccount, programIndices []uint64) error { - start := time.Now() + start := metrics.StartTiming(!execCtx.SkipTimingMetrics) nextInstrCtx, err := execCtx.TransactionContext.NextInstructionCtx() if err != nil { return err } metrics.GlobalBlockReplay.GetNextIxCtx.AddTimingSince(start) - start = time.Now() + start = metrics.StartTiming(!execCtx.SkipTimingMetrics) nextInstrCtx.Configure(programIndices, instructionAccts, instrData) metrics.GlobalBlockReplay.NextIxCtxConfigure.AddTimingSince(start) @@ -267,7 +269,7 @@ func (execCtx *ExecutionCtx) ProcessInstruction(instrData []byte, instructionAcc }) } - start = time.Now() + start = metrics.StartTiming(!execCtx.SkipTimingMetrics) err = execCtx.Push() if err != nil { return err @@ -283,7 +285,7 @@ func (execCtx *ExecutionCtx) ProcessInstruction(instrData []byte, instructionAcc err1 := execCtx.ExecuteInstruction() - start = time.Now() + start = metrics.StartTiming(!execCtx.SkipTimingMetrics) err2 := execCtx.Pop() metrics.GlobalBlockReplay.IxPop.AddTimingSince(start) @@ -304,7 +306,7 @@ func (execCtx *ExecutionCtx) AddModifiedVoteState(pubkey solana.PublicKey, state } func (execCtx *ExecutionCtx) ExecuteInstruction() error { - start := time.Now() + start := metrics.StartTiming(!execCtx.SkipTimingMetrics) txCtx := execCtx.TransactionContext instrCtx, err := txCtx.CurrentInstructionCtx() @@ -334,7 +336,7 @@ func (execCtx *ExecutionCtx) ExecuteInstruction() error { } metrics.GlobalBlockReplay.ExecIxResolveNativeProgram.AddTimingSince(start) - start = time.Now() + start = metrics.StartTiming(!execCtx.SkipTimingMetrics) err = nativeProgramFn(execCtx) switch nativeProgramStr { case a.SystemProgramAddrStr: diff --git a/pkg/tpu/txfixture/readonly_pair.go b/pkg/tpu/txfixture/readonly_pair.go new file mode 100644 index 00000000..c1de0a07 --- /dev/null +++ b/pkg/tpu/txfixture/readonly_pair.go @@ -0,0 +1,42 @@ +package txfixture + +import ( + "crypto/ed25519" + "fmt" + + "github.com/gagliardetto/solana-go" +) + +const ReadonlyPairPoolSize = 128 +const ReadonlyPairCapacity = ReadonlyPairPoolSize * (ReadonlyPairPoolSize - 1) + +// ReadonlyPairWire builds the 198-byte, single-signature, zero-instruction +// workload used for leader packing tests. Varying ordered pairs of existing +// readonly accounts gives distinct messages without adding instructions or +// forcing a lookup of a new nonexistent account for every transaction. +// Reusing an ordinal requires a different payer or recent blockhash. +func ReadonlyPairWire(key ed25519.PrivateKey, hash solana.Hash, pool []solana.PublicKey, ordinal int) ([]byte, error) { + if len(key) != ed25519.PrivateKeySize || len(pool) != ReadonlyPairPoolSize || ordinal < 0 || ordinal >= ReadonlyPairCapacity { + return nil, fmt.Errorf("invalid readonly-pair fixture key, pool or ordinal") + } + n := ordinal * 7919 % ReadonlyPairCapacity + a, b := n/(ReadonlyPairPoolSize-1), n%(ReadonlyPairPoolSize-1) + if b >= a { + b++ + } + payer := solana.PublicKeyFromBytes(key.Public().(ed25519.PublicKey)) + if payer == pool[a] || payer == pool[b] || pool[a] == pool[b] { + return nil, fmt.Errorf("readonly-pair accounts must be distinct") + } + message := make([]byte, 0, 133) + message = append(message, 1, 0, 2, 3) + message = append(message, payer[:]...) + message = append(message, pool[a][:]...) + message = append(message, pool[b][:]...) + message = append(message, hash[:]...) + message = append(message, 0) + wire := make([]byte, 0, 198) + wire = append(wire, 1) + wire = append(wire, ed25519.Sign(key, message)...) + return append(wire, message...), nil +} diff --git a/pkg/tpu/txfixture/readonly_pair_test.go b/pkg/tpu/txfixture/readonly_pair_test.go new file mode 100644 index 00000000..70efa16d --- /dev/null +++ b/pkg/tpu/txfixture/readonly_pair_test.go @@ -0,0 +1,68 @@ +package txfixture + +import ( + "crypto/ed25519" + "crypto/sha256" + "testing" + + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +// Reproduce the full preloaded four-slot experiment without RPC, funds or sends. +func TestReadonlyPair200KDistinctMessages(t *testing.T) { + keys := make([]ed25519.PrivateKey, 8) + for i := range keys { + seed := sha256.Sum256([]byte{byte(i), 73}) + keys[i] = ed25519.NewKeyFromSeed(seed[:]) + } + pool := make([]solana.PublicKey, ReadonlyPairPoolSize) + for i := range pool { + pool[i] = solana.PublicKey{byte(i + 1), 77} + } + seen := make(map[[32]byte]struct{}, 200000) + for i := 0; i < 200000; i++ { + hash, index := solana.Hash{1}, i + if i >= 120000 { + hash, index = solana.Hash{2}, i-120000 + } + wire, err := ReadonlyPairWire(keys[index%8], hash, pool, index/8) + require.NoError(t, err) + require.Len(t, wire, 198) + h := sha256.Sum256(wire[65:]) + if _, ok := seen[h]; ok { + t.Fatalf("duplicate message %d", i) + } + seen[h] = struct{}{} + if i%ReadonlyPairCapacity == 0 || i == 119999 || i == 120000 || i == 199999 { + tx, err := solana.TransactionFromBytes(wire) + require.NoError(t, err) + require.Len(t, tx.Signatures, 1) + require.Empty(t, tx.Message.Instructions) + require.Equal(t, hash, tx.Message.RecentBlockhash) + require.True(t, ed25519.Verify(ed25519.PublicKey(tx.Message.AccountKeys[0][:]), wire[65:], wire[1:65])) + } + } +} + +func TestReadonlyPairRejectsInvalidInputs(t *testing.T) { + key := ed25519.PrivateKey(PayerPrivateKey()) + pool := make([]solana.PublicKey, ReadonlyPairPoolSize) + for i := range pool { + pool[i] = solana.PublicKey{byte(i + 1), 77} + } + for _, ordinal := range []int{-1, ReadonlyPairCapacity} { + _, err := ReadonlyPairWire(key, TestBlockhash(), pool, ordinal) + require.Error(t, err) + } + _, err := ReadonlyPairWire(nil, TestBlockhash(), pool, 0) + require.Error(t, err) + _, err = ReadonlyPairWire(key, TestBlockhash(), nil, 0) + require.Error(t, err) + pool[0] = solana.PublicKeyFromBytes(key.Public().(ed25519.PublicKey)) + _, err = ReadonlyPairWire(key, TestBlockhash(), pool, 0) + require.Error(t, err) + pool[0], pool[1] = solana.PublicKey{9}, solana.PublicKey{9} + _, err = ReadonlyPairWire(key, TestBlockhash(), pool, 0) + require.Error(t, err) +} diff --git a/pkg/turbine/entry_hash.go b/pkg/turbine/entry_hash.go index 72af6bdb..35ee7561 100644 --- a/pkg/turbine/entry_hash.go +++ b/pkg/turbine/entry_hash.go @@ -90,14 +90,7 @@ func hashTransactions(txns []solana.Transaction) solana.Hash { } func hashSignatures(signatures [][]byte) solana.Hash { - if len(signatures) == 0 { - return solana.Hash{} - } - nodes := merkletree.HashNodes(signatures) - if root := nodes.GetRoot(); root != nil { - return solana.Hash(*root) - } - return solana.Hash{} + return solana.Hash(merkletree.HashRoot(signatures)) } func sha256Hash(data []byte) solana.Hash { diff --git a/pkg/turbine/entry_hash_bench_test.go b/pkg/turbine/entry_hash_bench_test.go new file mode 100644 index 00000000..e8d7d103 --- /dev/null +++ b/pkg/turbine/entry_hash_bench_test.go @@ -0,0 +1,24 @@ +package turbine + +import ( + "encoding/binary" + "fmt" + "testing" +) + +func BenchmarkEntrySignatureRoot(b *testing.B) { + for _, count := range []int{1, 64, 311, 512, 1024} { + b.Run(fmt.Sprint(count), func(b *testing.B) { + sigs := make([][]byte, count) + for i := range sigs { + sigs[i] = make([]byte, 64) + binary.LittleEndian.PutUint64(sigs[i], uint64(i)) + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = hashSignatures(sigs) + } + }) + } +} From 403305b099c715e57b0a167b025fe8124b03d72e Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 02:16:07 -0500 Subject: [PATCH 10/22] Document current-base leader benchmarks and finalized load results --- docs/leader_block_packing.md | 94 ++++ .../leader-block-packing/2026-09-13/README.md | 129 +++++ .../2026-09-13/baseline-capacity.txt | 1 + .../best-block/all-finalized-costs.json | 56 ++ .../best-block/largest-block-metrics.json | 16 + .../2026-09-13/best-block/metadata.json | 17 + .../2026-09-13/best-block/send.json | 12 + .../2026-09-13/best-block/validation.json | 99 ++++ .../2026-09-13/binary-sha256.txt | 3 + .../2026-09-13/evidence-manifest.json | 52 ++ .../all-finalized-costs.json | 35 ++ .../largest-block-metrics.json | 16 + .../four-block-prefill/metadata.json | 17 + .../2026-09-13/four-block-prefill/send.json | 11 + .../four-block-prefill/validation.json | 60 +++ .../2026-09-13/live-preload.go.txt | 510 ++++++++++++++++++ .../2026-09-13/local-race.txt | 11 + .../2026-09-13/local-unit.txt | 10 + .../2026-09-13/native-build.txt | 0 .../2026-09-13/native-race.txt | 11 + .../2026-09-13/native-vet.txt | 0 .../2026-09-13/round-1-baseline.txt | 7 + .../2026-09-13/round-1-candidate.txt | 8 + .../2026-09-13/round-2-baseline.txt | 7 + .../2026-09-13/round-2-candidate.txt | 8 + .../2026-09-13/round-3-baseline.txt | 7 + .../2026-09-13/round-3-candidate.txt | 8 + .../2026-09-13/run-native.sh | 31 ++ .../2026-09-13/service-after.txt | 2 + .../2026-09-13/service-before.txt | 2 + .../2026-09-13/source-sha256.txt | 2 + .../2026-09-13/summarize.py | 20 + .../2026-09-13/summary.json | 136 +++++ .../2026-09-13/topology.txt | 17 + 34 files changed, 1415 insertions(+) create mode 100644 docs/leader_block_packing.md create mode 100644 docs/results/leader-block-packing/2026-09-13/README.md create mode 100644 docs/results/leader-block-packing/2026-09-13/baseline-capacity.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/best-block/all-finalized-costs.json create mode 100644 docs/results/leader-block-packing/2026-09-13/best-block/largest-block-metrics.json create mode 100644 docs/results/leader-block-packing/2026-09-13/best-block/metadata.json create mode 100644 docs/results/leader-block-packing/2026-09-13/best-block/send.json create mode 100644 docs/results/leader-block-packing/2026-09-13/best-block/validation.json create mode 100644 docs/results/leader-block-packing/2026-09-13/binary-sha256.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/evidence-manifest.json create mode 100644 docs/results/leader-block-packing/2026-09-13/four-block-prefill/all-finalized-costs.json create mode 100644 docs/results/leader-block-packing/2026-09-13/four-block-prefill/largest-block-metrics.json create mode 100644 docs/results/leader-block-packing/2026-09-13/four-block-prefill/metadata.json create mode 100644 docs/results/leader-block-packing/2026-09-13/four-block-prefill/send.json create mode 100644 docs/results/leader-block-packing/2026-09-13/four-block-prefill/validation.json create mode 100644 docs/results/leader-block-packing/2026-09-13/live-preload.go.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/local-race.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/local-unit.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/native-build.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/native-race.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/native-vet.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/round-1-baseline.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/round-1-candidate.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/round-2-baseline.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/round-2-candidate.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/round-3-baseline.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/round-3-candidate.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/run-native.sh create mode 100644 docs/results/leader-block-packing/2026-09-13/service-after.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/service-before.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/source-sha256.txt create mode 100644 docs/results/leader-block-packing/2026-09-13/summarize.py create mode 100644 docs/results/leader-block-packing/2026-09-13/summary.json create mode 100644 docs/results/leader-block-packing/2026-09-13/topology.txt diff --git a/docs/leader_block_packing.md b/docs/leader_block_packing.md new file mode 100644 index 00000000..c650221e --- /dev/null +++ b/docs/leader_block_packing.md @@ -0,0 +1,94 @@ +# Leader block packing and synthetic load tests + +This change reduces work performed during a leader's available packing window. +The scheduler owns and decodes packet bytes once, and prepares static message +validation, instructions/account metadata, compute limits, message hash and cost +while transactions are queued. Each bank checks the immutable feature snapshot +before reuse. Account state, age, duplicates, strict fee-payer eligibility, rent, +execution and all resource budgets remain bank-dependent checks. + +Leader execution reuses borrowed-account scratch and skips detailed replay +stage timers. Missing-current-bank lookup errors defer base58 formatting until +used, avoiding wasted work before parent lookup. Entry Merkle hashing retains +only the root-building scratch, and queue removal avoids heap interface dispatch +and unused index updates while preserving priority/FIFO order. + +## Capacity and protocol limits + +Live banks use slot-dependent budgets with slot-time feature gates taking effect +in the epoch after activation. For the September 13 cluster's 200ms regime with +RaiseBlockLimitsTo100m, the budget was 50M block-cost units, 20M writable-account +cost units, 50MB allocated-data growth and 10MiB entry bytes. The entry packer +reserves 48 bytes for the ending tick. A 100M per-slot budget would be incorrect +in this regime. Active limits are logged when a leader bank opens. + +The readonly-pair fixture has one signature, one writable fee payer, two existing +readonly accounts, no instructions, and 198 wire bytes. Its observed actual cost +is 1,028 units: 720 signature, 300 write lock and eight loaded-account units. Its +program execution cost is zero. The theoretical cost-only ceiling is 48,638, +but upfront admission must fit the larger estimated loaded-data reservation: +the offline bank test includes 48,622 before rejecting the next transaction. +This workload is designed for signature/packing load, not application execution. + +## Reproduce locally + +All commands below are offline. They use deterministic test keys and in-memory +accounts; no RPC, faucet, funding or transaction submission occurs. + +```sh +# The 200,000-message, eight-payer, two-blockhash workload used to prefill four slots. +go test ./pkg/tpu/txfixture -run '^TestReadonlyPair200KDistinctMessages$' -count=1 + +# Fill a 50M-cost bank, reject the next tx, check fees, and round-trip all entries +# through actual shred generation and decoding, preserving transaction order/hash. +go test ./pkg/blockprod -run '^TestReadonlyPairBlockCapacityAndShredRoundTrip$' -count=1 + +# Whole-bank construction: one serial caller; pre-signed unique transactions. +GOMAXPROCS=8 go test ./pkg/blockprod -run '^$' \ + -bench '^BenchmarkReadonlyPair(FullBlock|PreparedFullBlock)$' -benchtime=3x -count=3 + +# More representative instruction workloads and smaller component microbenchmarks. +GOMAXPROCS=8 go test ./pkg/blockprod -run '^$' \ + -bench '^BenchmarkWorkingBank(Decoded|Prepared)?HotAccounts$' -benchtime=2s -count=3 +``` + +The whole-bank benchmark admits 48,622 transactions and includes execution, +account publication, entry batching/hash work and final entry flush. Signing and +bank setup are excluded. The prepared variant additionally does static +preparation before timing, modeling a queue ready before leadership. That work +is moved, not eliminated. Actual AccountsDB, signature verification, network, +consensus and the protocol deadline are outside this benchmark. The correctness +test's shred round-trip is also outside the timed benchmark. + +`txfixture.ReadonlyPairWire` provides the same ordered-pair construction as the +live experiment: 128×127 unique messages per payer/blockhash. Repeated ordinals +need a different payer or blockhash. The 200k test verifies uniqueness across +all messages and decodes/verifies representative signatures and phase boundaries. + +## Queue supply and completion reserve + +```toml +[validator] +tpu_max_buffered_transactions = 0 # default 131072 +block_completion_reserve_ms = 0 # default 75ms +``` + +The corresponding flags are `--tpu-max-buffered-transactions` and +`--leader-completion-reserve-ms`. The measured full-prefill trial used 262,144 +queue entries and a 60ms reserve. Those are opt-in tuning values; defaults stay +unchanged. A larger queue uses additional memory for owned wire, decoded and +prepared objects. A shorter reserve needs measured local finalization/broadcast +margin and does not change the protocol deadline. Shifting completion also +shifts later bank start times, so it does not add the same packing time to all +four blocks. + +The live results showed why total supply and timing matter: 120k transactions +cannot fill four approximately 48.6k blocks. An 80k refill competed with ongoing +work. Preloading 200k into a larger queue improved the observed four-block total, +while the first block still had less usable time and later banks awaited local +replay/adoption. These observations do not isolate a single CPU bottleneck. + +[Measured results, exact baselines and evidence](results/leader-block-packing/2026-09-13/README.md) +include both the successful near-limit block and the still-underfilled four-slot +window. The archived live helper is historical experiment source with explicit +cluster/identity/path constants; the offline fixture is the portable reproduction. diff --git a/docs/results/leader-block-packing/2026-09-13/README.md b/docs/results/leader-block-packing/2026-09-13/README.md new file mode 100644 index 00000000..06e270cb --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/README.md @@ -0,0 +1,129 @@ +# Leader block packing: standalone PR and live experiment results + +Goal: keep Mithril's own 200ms leader slots supplied and reduce work in the +packing window, while enforcing the actual cluster cost/byte limits. This PR +extracts the live-tested leader changes onto current `alpenglow-dev` at +`33dde4050d9250557583395810799aaac2f54017`. It can be reviewed independently of +FEC PR #259 and the streaming transaction-verification follow-up. + +## Fresh comparison against current alpenglow-dev + +Ryzen 7 9700X (8 physical cores/16 threads), Go 1.26.4, GOMAXPROCS=8, affinity +CPUs 0–7, nice 10. One serial benchmark caller builds a bank containing **48,622 +unique 198-byte one-signature, zero-instruction transactions**. Both versions +use identical in-memory accounts, 50M block-cost/20M writable-account budgets, +10MiB entry-byte limits and the same fixture/benchmark source. The copied test +harness is the only addition to the baseline; production code is unchanged. +Three paired rounds alternate order (base/PR, PR/base, base/PR); each reported +sample times three full banks. Tables show medians of the three sample means. +The validator continued running, so normal shared-host variation remains. + +| Bank input path | alpenglow-dev | PR | Allocated MB/block, base → PR | +|---|---:|---:|---:| +| Wire bytes | 209.64 ms | **150.30 ms** | 259.27 → 223.45 | +| Already decoded | 189.73 ms | **132.40 ms** | 237.88 → 202.06 | +| Already decoded and statically prepared | — | **101.92 ms** | — → 173.67 | + +The like-for-like wire path takes **28.3% less bank time**; the decoded path +30.2% less. The prepared bank phase additionally moves static work before +leadership. It does not eliminate that work from total CPU use. Its three +samples were 121.71, 101.92 and 101.60 ms. No whole-validator 2× claim is made. + +Timing includes bank admission, execution, account publication, entry building, +Merkle hashing and final entry flush. It excludes signing, signature verification, +bank setup, actual AccountsDB, network/shred broadcast, consensus and replay +adoption. Thus this is not a promise to fit the same block inside every live +packing window. The offline correctness test independently round-trips all +emitted entries through shred generation and decoding, verifies fees/hash/order, +and rejects the next transaction at the block-cost boundary. + +`run-native.sh` records the commands and baseline harness copying. Source and +binary hashes, topology, race/vet/build logs and every paired measurement are +included. `python3 summarize.py` rebuilds summary.json. The source archive was +captured before documentation-only additions; its Go/module files match the +published implementation. The live validator's PID stayed unchanged. + +## What enabled the live result + +- Decode owned TPU bytes once. Prepare immutable static transaction data before + leadership; recheck feature compatibility and bank-dependent state on use. +- Reuse borrowed-account scratch during serialized bank execution, and omit + detailed replay timing from that leader hot path. +- Format missing-bank-account diagnostics lazily before falling back to parent + accounts; calculate writable keys once and avoid base58 loader comparisons. +- Calculate signature Merkle roots without retaining unused proof nodes. +- Remove scheduler heap dispatch/index-store overhead, preserving priority/FIFO. +- Expose bounded queue capacity and local completion reserve as tuning settings, + keeping the 131,072-entry and 75ms defaults. +- Correct slot-time-dependent resource budgets. In the tested 200ms regime, + RaiseBlockLimitsTo100m means **50M**, not 100M per block. The skipped initial + 42,827-transaction compute-budget block exceeded the real limit and is not a + successful result. After correction, 42,338 such transactions finalized at + 49,916,502 cost units. The lighter readonly-pair workload then approached 48.6k. + +Slot-budget reference: [Agave release 2e10d67 slot parameters](https://github.com/anza-xyz/agave/blob/2e10d67f909c23a9586e1b3d099f7b8ef810ccfe/runtime/src/slot_params.rs), +including following-epoch activation and scaling of both account and block costs. + +## Live results: separate from the standalone benchmark + +The live binaries combined these changes with FEC/producer and streaming +verification work from trial `15ad1845`. No new live traffic or deployment was +performed while preparing this PR. These sequential windows are observations, +not controlled attribution of each optimization. + +**Best single finalized block:** slot **3073657**, **48,523 transactions**, one +signature each, zero instructions/execution CU, zero failures, **49,881,644 / +50,000,000 block-cost units**. Its local completion deadline margin was 49ms. +RPC blockhash: `H6iU43SrXp94LRvfpvhLDskboUJA4CupsEVFJcCRLANF`. + +The cost-only ceiling is floor(50,000,000 / 1,028) = 48,638. Upfront loaded-data +reservation reduces the offline admission limit to 48,622; runtime timing and +background work can reduce live inclusion further. The best live block is 99 +transactions below that offline admission count. We did not reach 50,000. + +The later fully preloaded four-block test offered 200,000 transactions at 150k +transactions/sec before our leader window, with queue capacity 262,144 and a +60ms completion reserve. The four finalized blocks were: + +| Slot | Included transactions | Cost units | Completion margin | +|---|---:|---:|---:| +| 3077076 | 26,687 | 27,434,236 | 39ms | +| 3077077 | 42,150 | 43,330,200 | 45ms | +| 3077078 | 41,387 | 42,545,836 | 41ms | +| 3077079 | 42,607 | 43,799,996 | 41ms | + +Total **152,831**, all successful; the other 47,169 expired without inclusion. +The preceding timely-refill run included 124,988. The 22.3% observed increase +coincides with different queue size, prefill timing, process age and live +conditions; it is not an isolated software speedup. **Four maximum-size blocks +were not achieved.** First-block packing time was shorter; later starts waited +for local replay/adoption. A reserve reduction does not add an equal amount of +packing time to every slot. Buffered log output was too delayed to be a useful +refill trigger, so the subsequent test used RPC slot observations and finally +full prefill. + +Small extracted finalized-validation records are in best-block/ and +four-block-prefill/. evidence-manifest.json hashes the original source records. +Full prior evidence archives remain identified by SHA-256: + +- Best-block experiment: `f785aeaad00d5cc85e868bf1488e4ebb7adb0346f97308f0fc6d8ae538c943a5`. +- Four-block prefill: `e7a3f03b5f88f4110c9dc58d306f14142bee4c5ba2f33b5b348d5ea05ded525f`. + +`live-preload.go.txt` is the original bounded live sender source (no key material). +Its RPC/genesis/identity/path constants are historical experiment settings, not +portable defaults. The offline tests reproduce its message construction and +capacity checks without funds or network. See [reproduction commands](../../../leader_block_packing.md). + +## Validation and remaining work + +Local and native Zen 5 race suites passed for accounts, blockprod/scheduler, +costmodel, merkletree, replay, fees, turbine, txfixture, config and node. Vet and +full Mithril builds passed on both machines. The full repository suite is not +claimed to pass: prior baseline/candidate trials reproduced unrelated BPF-loader +failures. Tests cover owned packet lifetimes, stale feature preparation, +current payer-state changes, fees-only/instruction failures, duplicates/age, +arena reuse, queue ordering/eviction, Merkle parity and slot-budget activation. + +The discarded status-map allocation experiment showed no clear timing gain and +is not included. Reusing message hashes through local replay and shortening +replay/adoption transitions remain possible follow-ups, not completed gains. diff --git a/docs/results/leader-block-packing/2026-09-13/baseline-capacity.txt b/docs/results/leader-block-packing/2026-09-13/baseline-capacity.txt new file mode 100644 index 00000000..fb360e97 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/baseline-capacity.txt @@ -0,0 +1 @@ +ok github.com/Overclock-Validator/mithril/pkg/blockprod 1.076s diff --git a/docs/results/leader-block-packing/2026-09-13/best-block/all-finalized-costs.json b/docs/results/leader-block-packing/2026-09-13/best-block/all-finalized-costs.json new file mode 100644 index 00000000..d635f286 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/best-block/all-finalized-costs.json @@ -0,0 +1,56 @@ +{ + "stage": "window-fill", + "finalized_load_transactions": 200000, + "not_included_after_expiry": 0, + "blocks": [ + { + "slot": 3073656, + "transactions": 31732, + "load_transactions": 31732, + "cost_units": 32620496, + "failed_transactions": 0 + }, + { + "slot": 3073657, + "transactions": 48523, + "load_transactions": 48523, + "cost_units": 49881644, + "failed_transactions": 0 + }, + { + "slot": 3073658, + "transactions": 39911, + "load_transactions": 39745, + "cost_units": 41103706, + "failed_transactions": 0 + }, + { + "slot": 3073659, + "transactions": 138, + "load_transactions": 0, + "cost_units": 204378, + "failed_transactions": 0 + }, + { + "slot": 3073740, + "transactions": 34188, + "load_transactions": 34150, + "cost_units": 35162478, + "failed_transactions": 0 + }, + { + "slot": 3073741, + "transactions": 45094, + "load_transactions": 45094, + "cost_units": 46356632, + "failed_transactions": 0 + }, + { + "slot": 3073742, + "transactions": 852, + "load_transactions": 756, + "cost_units": 919344, + "failed_transactions": 0 + } + ] +} \ No newline at end of file diff --git a/docs/results/leader-block-packing/2026-09-13/best-block/largest-block-metrics.json b/docs/results/leader-block-packing/2026-09-13/best-block/largest-block-metrics.json new file mode 100644 index 00000000..85a32860 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/best-block/largest-block-metrics.json @@ -0,0 +1,16 @@ +{ + "slot": 3073657, + "transactions": 48523, + "load_transactions": 48523, + "block_cost_units": 49881644, + "execution_cu": 0, + "failed_transactions": 0, + "load_cost_units_each": [ + 1028 + ], + "load_execution_cu_each": [ + 0 + ], + "finalized": true, + "blockhash": "H6iU43SrXp94LRvfpvhLDskboUJA4CupsEVFJcCRLANF" +} \ No newline at end of file diff --git a/docs/results/leader-block-packing/2026-09-13/best-block/metadata.json b/docs/results/leader-block-packing/2026-09-13/best-block/metadata.json new file mode 100644 index 00000000..d625573f --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/best-block/metadata.json @@ -0,0 +1,17 @@ +{ + "blockhash": "4X7hVhH1RJReoEkJcFQSV2BTKbjFQT8JK16dD3bEEwuv", + "count": 200000, + "last_valid_block_height": 2839082, + "offered_tps": 150000, + "prefill_count": 120000, + "prefill_slots": 10, + "prepare_seconds": 2.715821878, + "sender": "127.0.0.1:8011; GOMAXPROCS=2; signatures prepared before send", + "shape": "readonly-pair", + "signatures_per_transaction": 1, + "topup_blockhash": "DTSw2jqMLS6A3yCuJ27d7BpH5sWttE53sy6Pw6FMwWie", + "topup_count": 80000, + "topup_trigger": "first local block broadcast", + "transaction_bytes": 198, + "window": 3073656 +} \ No newline at end of file diff --git a/docs/results/leader-block-packing/2026-09-13/best-block/send.json b/docs/results/leader-block-packing/2026-09-13/best-block/send.json new file mode 100644 index 00000000..4f4fd60e --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/best-block/send.json @@ -0,0 +1,12 @@ +{ + "end_slot": 3073659, + "end_utc": "2026-09-13T06:45:12.217473403Z", + "first_error": "", + "not_sent": 0, + "send_errors": 0, + "send_seconds": 3.291255092, + "sent": 200000, + "start_utc": "2026-09-13T06:45:08.926218321Z", + "target_window": 3073656, + "topup_start_utc": "2026-09-13T06:45:11.683951471Z" +} \ No newline at end of file diff --git a/docs/results/leader-block-packing/2026-09-13/best-block/validation.json b/docs/results/leader-block-packing/2026-09-13/best-block/validation.json new file mode 100644 index 00000000..6af787f4 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/best-block/validation.json @@ -0,0 +1,99 @@ +{ + "utc": "2026-09-13T06:48:40.355016+00:00", + "stage": "window-fill", + "blocks": [ + { + "slot": 3073656, + "blockhash": "6CGAhXwTZSMsS5Uda6CD19VRqaYeBUQoodZJW7X9PXcx", + "alpenglow_block_id": "8xkgbbwhcXujEMbn1bKhkWvTEiEwBfoPZVkTNs4ihcgj", + "certificate_logs": [ + "(+17m19s) ALPENGLOW voting: vote landed source=votor-quic proof=verified-aggregate vote=notarize slot=3073656 rank=21 certificate=notarize block=8xkgbbwhcXujEMbn1bKhkWvTEiEwBfoPZVkTNs4ihcgj network_landed=8654" + ], + "finalized": true, + "finalization_ms": 7, + "deadline_margin_ms": 50, + "transactions": 31732, + "log": "(+17m19s) ALPENGLOW block production: broadcast local leader slot=3073656 terminal=complete cause=complete reason=complete replay_frontier=3073651 live_slot=3073655 block=8xkgbbwhcXujEMbn1bKhkWvTEiEwBfoPZVkTNs4ihcgj parent_slot=3073651 txns=31732 window_elapsed_ms=149 finalization_ms=7 deadline_margin_ms=50 local_handoff_ms=0" + }, + { + "slot": 3073657, + "blockhash": "H6iU43SrXp94LRvfpvhLDskboUJA4CupsEVFJcCRLANF", + "alpenglow_block_id": "AQi4tKfzW3MRNHGhndxpL9sq6QBr5aoyR22ZPBA8RJUy", + "certificate_logs": [ + "(+17m19s) ALPENGLOW voting: vote landed source=votor-quic proof=verified-aggregate vote=notarize slot=3073657 rank=21 certificate=notarize block=AQi4tKfzW3MRNHGhndxpL9sq6QBr5aoyR22ZPBA8RJUy network_landed=8656" + ], + "finalized": true, + "finalization_ms": 9, + "deadline_margin_ms": 49, + "transactions": 48523, + "log": "(+17m19s) ALPENGLOW block production: broadcast local leader slot=3073657 terminal=complete cause=complete reason=complete replay_frontier=3073656 live_slot=3073656 block=AQi4tKfzW3MRNHGhndxpL9sq6QBr5aoyR22ZPBA8RJUy parent_slot=3073656 txns=48523 window_elapsed_ms=350 finalization_ms=9 deadline_margin_ms=49 local_handoff_ms=0" + }, + { + "slot": 3073658, + "blockhash": "939M6Zfyuj9wwyrMVnzhF5WmyqzpSZfUQJwBA5gk2jto", + "alpenglow_block_id": "EWr2Nh8MFMUMQXvLsPAW6Z3xSPcuFP8efDrxjYtdz9s5", + "certificate_logs": [ + "(+17m19s) ALPENGLOW voting: vote landed source=votor-quic proof=verified-aggregate vote=notarize slot=3073658 rank=21 certificate=notarize-fallback block=EWr2Nh8MFMUMQXvLsPAW6Z3xSPcuFP8efDrxjYtdz9s5 network_landed=8658" + ], + "finalized": true, + "finalization_ms": 8, + "deadline_margin_ms": 49, + "transactions": 39911, + "log": "(+17m19s) ALPENGLOW block production: broadcast local leader slot=3073658 terminal=complete cause=complete reason=complete replay_frontier=3073657 live_slot=3073657 block=EWr2Nh8MFMUMQXvLsPAW6Z3xSPcuFP8efDrxjYtdz9s5 parent_slot=3073657 txns=39911 window_elapsed_ms=550 finalization_ms=8 deadline_margin_ms=49 local_handoff_ms=0" + }, + { + "slot": 3073659, + "blockhash": "EXX1YCesL18q3KAUEehr4WsDZPcuSd2ErYKatMPBSDdS", + "alpenglow_block_id": "2JUsPbPn4JeyUhGkXqersNaYyKHZJut4NDgp6sFkarXe", + "certificate_logs": [ + "(+17m19s) ALPENGLOW voting: vote landed source=votor-quic proof=verified-aggregate vote=notarize slot=3073659 rank=21 certificate=notarize-fallback block=2JUsPbPn4JeyUhGkXqersNaYyKHZJut4NDgp6sFkarXe network_landed=8659" + ], + "finalized": true, + "finalization_ms": 10, + "deadline_margin_ms": 47, + "transactions": 138, + "log": "(+17m19s) ALPENGLOW block production: broadcast local leader slot=3073659 terminal=complete cause=complete reason=complete replay_frontier=3073658 live_slot=3073658 block=2JUsPbPn4JeyUhGkXqersNaYyKHZJut4NDgp6sFkarXe parent_slot=3073658 txns=138 window_elapsed_ms=752 finalization_ms=10 deadline_margin_ms=47 local_handoff_ms=0" + }, + { + "slot": 3073740, + "blockhash": "saQYhwkvXUsuqa9xN2gE1cGMPZpAPJmB4fTZWYrQ6wf", + "alpenglow_block_id": "CzcvprhgxhmS4uU4Xxav9g68aBozMt9KTj22SFxF6fNi", + "certificate_logs": [ + "(+17m37s) ALPENGLOW voting: vote landed source=votor-quic proof=verified-aggregate vote=notarize slot=3073740 rank=21 certificate=notarize block=CzcvprhgxhmS4uU4Xxav9g68aBozMt9KTj22SFxF6fNi network_landed=8804" + ], + "finalized": true, + "finalization_ms": 10, + "deadline_margin_ms": 49, + "transactions": 34188, + "log": "(+17m37s) ALPENGLOW block production: broadcast local leader slot=3073740 terminal=complete cause=complete reason=complete replay_frontier=3073735 live_slot=3073739 block=CzcvprhgxhmS4uU4Xxav9g68aBozMt9KTj22SFxF6fNi parent_slot=3073735 txns=34188 window_elapsed_ms=150 finalization_ms=10 deadline_margin_ms=49 local_handoff_ms=0" + }, + { + "slot": 3073741, + "blockhash": "BN1bpbetrHiU2tzm4DCam4T9F8ZisDkgDPnqA35S9gFi", + "alpenglow_block_id": "146JxvWuGXKR1wvsA4vmosPDMNVoEBEoD93u6fv1abWX", + "certificate_logs": [ + "(+17m37s) ALPENGLOW voting: vote landed source=votor-quic proof=verified-aggregate vote=notarize slot=3073741 rank=21 certificate=notarize block=146JxvWuGXKR1wvsA4vmosPDMNVoEBEoD93u6fv1abWX network_landed=8806" + ], + "finalized": true, + "finalization_ms": 10, + "deadline_margin_ms": 49, + "transactions": 45094, + "log": "(+17m37s) ALPENGLOW block production: broadcast local leader slot=3073741 terminal=complete cause=complete reason=complete replay_frontier=3073740 live_slot=3073740 block=146JxvWuGXKR1wvsA4vmosPDMNVoEBEoD93u6fv1abWX parent_slot=3073740 txns=45094 window_elapsed_ms=350 finalization_ms=10 deadline_margin_ms=49 local_handoff_ms=0" + }, + { + "slot": 3073742, + "blockhash": "AFytPNQRtXwRbWwnzi6D9aAVz55h43KknkHTJUWnqtr7", + "alpenglow_block_id": "3STHfQmzAywmCDRJuosJ6U6ASHJZxeXpRxaBnTfhiSEb", + "certificate_logs": [ + "(+17m37s) ALPENGLOW voting: vote landed source=votor-quic proof=verified-aggregate vote=notarize slot=3073742 rank=21 certificate=notarize block=3STHfQmzAywmCDRJuosJ6U6ASHJZxeXpRxaBnTfhiSEb network_landed=8808" + ], + "finalized": true, + "finalization_ms": 9, + "deadline_margin_ms": 48, + "transactions": 852, + "log": "(+17m37s) ALPENGLOW block production: broadcast local leader slot=3073742 terminal=complete cause=complete reason=complete replay_frontier=3073741 live_slot=3073741 block=3STHfQmzAywmCDRJuosJ6U6ASHJZxeXpRxaBnTfhiSEb parent_slot=3073741 txns=852 window_elapsed_ms=551 finalization_ms=9 deadline_margin_ms=48 local_handoff_ms=0" + } + ], + "log_errors": [], + "included": 200000 +} \ No newline at end of file diff --git a/docs/results/leader-block-packing/2026-09-13/binary-sha256.txt b/docs/results/leader-block-packing/2026-09-13/binary-sha256.txt new file mode 100644 index 00000000..f9451ea6 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/binary-sha256.txt @@ -0,0 +1,3 @@ +302484874e65f2b01265ecf1f7c4ea3f8e850077844e795ef2a6bea55749ef6c /srv/mithril-leader-packing-pr-20260913/results/baseline.test +02395f246f80ce6a3e337000dc030dec1e04daf35a7149db4481f20587f406dc /srv/mithril-leader-packing-pr-20260913/results/candidate.test +230f6474e438ef29623537bef8aec6d5fa9fbb14014313231f132bf8e78a445f /srv/mithril-leader-packing-pr-20260913/results/mithril diff --git a/docs/results/leader-block-packing/2026-09-13/evidence-manifest.json b/docs/results/leader-block-packing/2026-09-13/evidence-manifest.json new file mode 100644 index 00000000..6091e4c1 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/evidence-manifest.json @@ -0,0 +1,52 @@ +[ + { + "source": "leader-window-fill-20260913/evidence/load/window-fill/validation.json", + "path": "best-block/validation.json", + "sha256": "a6c503c254c76138da8142c81cee665f31338f6bff756c77c4e9f5b85229b1c5" + }, + { + "source": "leader-window-fill-20260913/evidence/load/window-fill/largest-block-metrics.json", + "path": "best-block/largest-block-metrics.json", + "sha256": "733203a4bb62a7fd28f92b73ff0bdfb6592855d9aef2ece3f4447b4f45ac7c54" + }, + { + "source": "leader-window-fill-20260913/evidence/load/window-fill/all-finalized-costs.json", + "path": "best-block/all-finalized-costs.json", + "sha256": "f310f47b66bf49d14272109c3a97dc9c5f73ee740edac67aea783a38cffbf897" + }, + { + "source": "leader-window-fill-20260913/evidence/load/window-fill/metadata.json", + "path": "best-block/metadata.json", + "sha256": "265ca56fd39ff78b8d4ff2f2643f4e5400db69b4b32e6ffc5e027d3dbe3a1203" + }, + { + "source": "leader-window-fill-20260913/evidence/load/window-fill/send.json", + "path": "best-block/send.json", + "sha256": "0ebdc42dd32bee8085f1147512546b7aef851cd0a6e7e03cb45d0c22a818ae05" + }, + { + "source": "leader-window-prefill-20260913/evidence/load/window-fill/validation.json", + "path": "four-block-prefill/validation.json", + "sha256": "2223cb7bdf044ff9e23c6b08cfb40c1f428010de0f591322035b4574733567d0" + }, + { + "source": "leader-window-prefill-20260913/evidence/load/window-fill/largest-block-metrics.json", + "path": "four-block-prefill/largest-block-metrics.json", + "sha256": "8f3d6c54334eb3a0fec8568425ebe4895966ca756534fc5c8bbb6f40ab6af20e" + }, + { + "source": "leader-window-prefill-20260913/evidence/load/window-fill/all-finalized-costs.json", + "path": "four-block-prefill/all-finalized-costs.json", + "sha256": "3f46aa6eb8fe6e0d6d738a7cc7026780ae4135bd1bee54f5a13ef3a5f6ccd14c" + }, + { + "source": "leader-window-prefill-20260913/evidence/load/window-fill/metadata.json", + "path": "four-block-prefill/metadata.json", + "sha256": "2e6c576474cd573124700a9abb8b40429d8c14092259ad50cf47c3d4bce0dea4" + }, + { + "source": "leader-window-prefill-20260913/evidence/load/window-fill/send.json", + "path": "four-block-prefill/send.json", + "sha256": "cddce7d90b8d039b06167ad5d845a0a0388fe678f95f5775bd08e9dd314643b9" + } +] diff --git a/docs/results/leader-block-packing/2026-09-13/four-block-prefill/all-finalized-costs.json b/docs/results/leader-block-packing/2026-09-13/four-block-prefill/all-finalized-costs.json new file mode 100644 index 00000000..ed4e7097 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/four-block-prefill/all-finalized-costs.json @@ -0,0 +1,35 @@ +{ + "stage": "window-fill", + "finalized_load_transactions": 152831, + "not_included_after_expiry": 47169, + "blocks": [ + { + "slot": 3077076, + "transactions": 26687, + "load_transactions": 26687, + "cost_units": 27434236, + "failed_transactions": 0 + }, + { + "slot": 3077077, + "transactions": 42150, + "load_transactions": 42150, + "cost_units": 43330200, + "failed_transactions": 0 + }, + { + "slot": 3077078, + "transactions": 41387, + "load_transactions": 41387, + "cost_units": 42545836, + "failed_transactions": 0 + }, + { + "slot": 3077079, + "transactions": 42607, + "load_transactions": 42607, + "cost_units": 43799996, + "failed_transactions": 0 + } + ] +} \ No newline at end of file diff --git a/docs/results/leader-block-packing/2026-09-13/four-block-prefill/largest-block-metrics.json b/docs/results/leader-block-packing/2026-09-13/four-block-prefill/largest-block-metrics.json new file mode 100644 index 00000000..22e907bc --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/four-block-prefill/largest-block-metrics.json @@ -0,0 +1,16 @@ +{ + "slot": 3077079, + "transactions": 42607, + "load_transactions": 42607, + "block_cost_units": 43799996, + "execution_cu": 0, + "failed_transactions": 0, + "load_cost_units_each": [ + 1028 + ], + "load_execution_cu_each": [ + 0 + ], + "finalized": true, + "blockhash": "FXcMHsNzG53SnuW1kR2ZzxGLx6p9FxeUsgeLvMzS1rhL" +} \ No newline at end of file diff --git a/docs/results/leader-block-packing/2026-09-13/four-block-prefill/metadata.json b/docs/results/leader-block-packing/2026-09-13/four-block-prefill/metadata.json new file mode 100644 index 00000000..1a321b46 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/four-block-prefill/metadata.json @@ -0,0 +1,17 @@ +{ + "blockhash": "Cjef4Kov7MMcdEeY2vUrfT2pineEzykVvsy6321CPbtE", + "count": 200000, + "last_valid_block_height": 2842026, + "offered_tps": 150000, + "prefill_count": 200000, + "prefill_slots": 20, + "prepare_seconds": 2.571031937, + "sender": "127.0.0.1:8011; GOMAXPROCS=2; signatures prepared before send", + "shape": "readonly-pair", + "signatures_per_transaction": 1, + "topup_blockhash": "GUgh5AT5Ci7p2Et6GzZFscesHLQ7gVmmpHyzhuutcvQx", + "topup_count": 0, + "topup_trigger": "none; entire workload preloaded", + "transaction_bytes": 198, + "window": 3077076 +} \ No newline at end of file diff --git a/docs/results/leader-block-packing/2026-09-13/four-block-prefill/send.json b/docs/results/leader-block-packing/2026-09-13/four-block-prefill/send.json new file mode 100644 index 00000000..2656f8f7 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/four-block-prefill/send.json @@ -0,0 +1,11 @@ +{ + "end_slot": 3077062, + "end_utc": "2026-09-13T06:57:13.621742904Z", + "first_error": "", + "not_sent": 0, + "send_errors": 0, + "send_seconds": 1.333626813, + "sent": 200000, + "start_utc": "2026-09-13T06:57:12.288116091Z", + "target_window": 3077076 +} \ No newline at end of file diff --git a/docs/results/leader-block-packing/2026-09-13/four-block-prefill/validation.json b/docs/results/leader-block-packing/2026-09-13/four-block-prefill/validation.json new file mode 100644 index 00000000..d571065a --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/four-block-prefill/validation.json @@ -0,0 +1,60 @@ +{ + "utc": "2026-09-13T06:58:15.837757+00:00", + "stage": "window-fill", + "blocks": [ + { + "slot": 3077076, + "blockhash": "6WQ6hRYS9KLCsAWcvvGuwRujFL8uUH6AX28wUrxCAPfw", + "alpenglow_block_id": "A3cMotBW2NT7LaGk1NgEQ7RinuWAeVwNiAeCZQ2GYpYA", + "certificate_logs": [ + "(+ 1m13s) ALPENGLOW voting: vote landed source=votor-quic proof=verified-aggregate vote=notarize slot=3077076 rank=21 certificate=notarize block=A3cMotBW2NT7LaGk1NgEQ7RinuWAeVwNiAeCZQ2GYpYA network_landed=553" + ], + "finalized": true, + "finalization_ms": 15, + "deadline_margin_ms": 39, + "transactions": 26687, + "log": "(+ 1m13s) ALPENGLOW block production: broadcast local leader slot=3077076 terminal=complete cause=complete reason=complete replay_frontier=3077075 live_slot=3077077 block=A3cMotBW2NT7LaGk1NgEQ7RinuWAeVwNiAeCZQ2GYpYA parent_slot=3077075 txns=26687 window_elapsed_ms=160 finalization_ms=15 deadline_margin_ms=39 local_handoff_ms=0" + }, + { + "slot": 3077077, + "blockhash": "A4DT5t7mxKQ7Udb4ixU7dLiww9feWkPHswGzGyFCe5at", + "alpenglow_block_id": "8tMUnRr5rik2yiKdwxJnKyLLDsc5vyn6Q9vT7jazvvsX", + "certificate_logs": [ + "(+ 1m13s) ALPENGLOW voting: vote landed source=votor-quic proof=verified-aggregate vote=notarize slot=3077077 rank=21 certificate=notarize block=8tMUnRr5rik2yiKdwxJnKyLLDsc5vyn6Q9vT7jazvvsX network_landed=555" + ], + "finalized": true, + "finalization_ms": 14, + "deadline_margin_ms": 45, + "transactions": 42150, + "log": "(+ 1m13s) ALPENGLOW block production: broadcast local leader slot=3077077 terminal=complete cause=complete reason=complete replay_frontier=3077076 live_slot=3077076 block=8tMUnRr5rik2yiKdwxJnKyLLDsc5vyn6Q9vT7jazvvsX parent_slot=3077076 txns=42150 window_elapsed_ms=354 finalization_ms=14 deadline_margin_ms=45 local_handoff_ms=0" + }, + { + "slot": 3077078, + "blockhash": "A48aPKzQzo5P7TN4qWn3hv1CwK8yRLvsCXPXnc3J6Yyj", + "alpenglow_block_id": "5FhWEtjG9WGj11z1ZkD5Gx3jYAkVaPytx1PPacQ6bR4H", + "certificate_logs": [ + "(+ 1m13s) ALPENGLOW voting: vote landed source=votor-quic proof=verified-aggregate vote=notarize slot=3077078 rank=21 certificate=notarize block=5FhWEtjG9WGj11z1ZkD5Gx3jYAkVaPytx1PPacQ6bR4H network_landed=557" + ], + "finalized": true, + "finalization_ms": 13, + "deadline_margin_ms": 41, + "transactions": 41387, + "log": "(+ 1m13s) ALPENGLOW block production: broadcast local leader slot=3077078 terminal=complete cause=complete reason=complete replay_frontier=3077077 live_slot=3077077 block=5FhWEtjG9WGj11z1ZkD5Gx3jYAkVaPytx1PPacQ6bR4H parent_slot=3077077 txns=41387 window_elapsed_ms=558 finalization_ms=13 deadline_margin_ms=41 local_handoff_ms=0" + }, + { + "slot": 3077079, + "blockhash": "FXcMHsNzG53SnuW1kR2ZzxGLx6p9FxeUsgeLvMzS1rhL", + "alpenglow_block_id": "ChYL4dneUd22GQc3YxFafh7Lc1XvEdCTFPLLaAhAVDDQ", + "certificate_logs": [ + "(+ 1m13s) ALPENGLOW voting: vote landed source=votor-quic proof=verified-aggregate vote=notarize slot=3077079 rank=21 certificate=notarize-fallback block=ChYL4dneUd22GQc3YxFafh7Lc1XvEdCTFPLLaAhAVDDQ network_landed=559" + ], + "finalized": true, + "finalization_ms": 13, + "deadline_margin_ms": 41, + "transactions": 42607, + "log": "(+ 1m13s) ALPENGLOW block production: broadcast local leader slot=3077079 terminal=complete cause=complete reason=complete replay_frontier=3077078 live_slot=3077078 block=ChYL4dneUd22GQc3YxFafh7Lc1XvEdCTFPLLaAhAVDDQ parent_slot=3077078 txns=42607 window_elapsed_ms=758 finalization_ms=13 deadline_margin_ms=41 local_handoff_ms=0" + } + ], + "log_errors": [], + "included": 152831 +} \ No newline at end of file diff --git a/docs/results/leader-block-packing/2026-09-13/live-preload.go.txt b/docs/results/leader-block-packing/2026-09-13/live-preload.go.txt new file mode 100644 index 00000000..ca979fa3 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/live-preload.go.txt @@ -0,0 +1,510 @@ +// Bounded, own-validator-only Alpenglow load experiment. Not production code. +package main + +import ( + "bytes" + "context" + "crypto/ed25519" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "encoding/binary" + "encoding/json" + "flag" + "fmt" + "net/http" + "os" + "path/filepath" + "sync" + "sync/atomic" + "time" + + "github.com/Overclock-Validator/mithril/pkg/tpu/quicserver/testutils" + "github.com/gagliardetto/solana-go" +) + +const identity = "AzDzG3imFz5ihvetCcGixiwuieH7jfsaivnGDXgZo6Z8" +const genesis = "D5SkuirXeXeUTR7z2A65nzgU1GZ6uJ4vzf2tCy3Zt88b" +const rpcURL = "https://rpc.ag.validator1.net" + +var root = flag.String("root", "/srv/mithril-leader-window-prefill-20260913/load", "private experiment directory") +var mode = flag.String("mode", "selfcheck", "init, selfcheck, simulate, or run") +var count = flag.Int("count", 32, "unique transactions; hard limit 200000") +var rate = flag.Int("rate", 100, "offered transactions/s; hard limit 150000") +var stage = flag.String("stage", "pilot", "unique result directory name") +var prefill = flag.Int("prefill-slots", 0, "send this many slots before leadership (0..20)") +var shape = flag.String("shape", "readonly-pair", "account-nonce, compute-budget or readonly-pair") +var transport = &http.Client{Timeout: 15 * time.Second} + +func must(err error) { + if err != nil { + panic(err) + } +} +func logEvent(v any) { b, e := json.Marshal(v); must(e); fmt.Println(string(b)) } +func save(path string, v any) { + b, e := json.MarshalIndent(v, "", " ") + must(e) + must(os.WriteFile(path, b, 0600)) +} +func rpc(method string, params any, out any) error { + b, e := json.Marshal(map[string]any{"jsonrpc": "2.0", "id": 1, "method": method, "params": params}) + if e != nil { + return e + } + r, e := transport.Post(rpcURL, "application/json", bytes.NewReader(b)) + if e != nil { + return e + } + defer r.Body.Close() + var env struct { + Result json.RawMessage `json:"result"` + Error json.RawMessage `json:"error"` + } + if e = json.NewDecoder(r.Body).Decode(&env); e != nil { + return e + } + if len(env.Error) > 0 && string(env.Error) != "null" { + return fmt.Errorf("RPC %s: %s", method, env.Error) + } + return json.Unmarshal(env.Result, out) +} +func keyPath(i int) string { return filepath.Join(*root, "keys", fmt.Sprintf("payer-%d.json", i)) } +func initKeys() { + must(os.MkdirAll(filepath.Join(*root, "keys"), 0700)) + var pubs []string + for i := 0; i < 8; i++ { + _, p, e := ed25519.GenerateKey(rand.Reader) + must(e) + vals := make([]int, len(p)) + for j, b := range p { + vals[j] = int(b) + } + b, e := json.Marshal(vals) + must(e) + f, e := os.OpenFile(keyPath(i), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0600) + must(e) + _, e = f.Write(b) + must(e) + must(f.Close()) + pubs = append(pubs, solana.PublicKeyFromBytes(p.Public().(ed25519.PublicKey)).String()) + } + save(filepath.Join(*root, "payers.json"), pubs) + logEvent(map[string]any{"payers": pubs}) +} +func loadKeys() []ed25519.PrivateKey { + keys := make([]ed25519.PrivateKey, 8) + for i := range keys { + b, e := os.ReadFile(keyPath(i)) + must(e) + var a []int + must(json.Unmarshal(b, &a)) + if len(a) != 64 { + panic("invalid key length") + } + p := make([]byte, 64) + for j, v := range a { + if v < 0 || v > 255 { + panic("invalid key byte") + } + p[j] = byte(v) + } + keys[i] = ed25519.PrivateKey(p) + } + return keys +} +func pairIndices(ordinal int) (int, int) { + if ordinal < 0 || ordinal >= 128*127 { + panic("pair ordinal outside pool capacity") + } + // A permutation distributes early transactions over the fixed account pool. + n := (ordinal * 7919) % (128 * 127) + a, b := n/127, n%127 + if b >= a { + b++ + } + return a, b +} +func loadPool() []solana.PublicKey { + b, e := os.ReadFile(filepath.Join(*root, "pool.json")) + must(e) + var names []string + must(json.Unmarshal(b, &names)) + if len(names) != 128 { + panic("wrong pool length") + } + p := make([]solana.PublicKey, 128) + seen := map[solana.PublicKey]bool{} + for i, n := range names { + p[i] = solana.MustPublicKeyFromBase58(n) + if seen[p[i]] { + panic("duplicate pool account") + } + seen[p[i]] = true + } + return p +} +func makePairWire(key ed25519.PrivateKey, hash solana.Hash, pool []solana.PublicKey, ordinal int) []byte { + a, b := pairIndices(ordinal) + payer := solana.PublicKeyFromBytes(key.Public().(ed25519.PublicKey)) + if payer == pool[a] || payer == pool[b] { + panic("payer must be distinct from readonly pool") + } + msg := []byte{1, 0, 2, 3} + msg = append(msg, payer[:]...) + msg = append(msg, pool[a][:]...) + msg = append(msg, pool[b][:]...) + msg = append(msg, hash[:]...) + msg = append(msg, 0) + w := []byte{1} + w = append(w, ed25519.Sign(key, msg)...) + return append(w, msg...) +} + +func makeWire(key ed25519.PrivateKey, hash solana.Hash, nonce [32]byte) []byte { + // One writable signer, one unique readonly unsigned account, no instructions. + msg := []byte{1, 0, 1, 2} + msg = append(msg, key.Public().(ed25519.PublicKey)...) + msg = append(msg, nonce[:]...) + msg = append(msg, hash[:]...) + msg = append(msg, 0) + sig := ed25519.Sign(key, msg) + w := []byte{1} + w = append(w, sig...) + return append(w, msg...) +} +func makeBudgetWire(key ed25519.PrivateKey, hash solana.Hash, limit uint32) []byte { + program := solana.MustPublicKeyFromBase58("ComputeBudget111111111111111111111111111111") + msg := []byte{1, 0, 1, 2} + msg = append(msg, key.Public().(ed25519.PublicKey)...) + msg = append(msg, program[:]...) + msg = append(msg, hash[:]...) + msg = append(msg, 1, 1, 0, 5, 2) // one instruction: program 1, no accounts, 5 data bytes, SetComputeUnitLimit + msg = binary.LittleEndian.AppendUint32(msg, limit) + w := []byte{1} + w = append(w, ed25519.Sign(key, msg)...) + return append(w, msg...) +} +func check(w []byte) { + t, e := solana.TransactionFromBytes(w) + must(e) + if (len(w) != 166 && len(w) != 174 && len(w) != 198) || len(t.Signatures) != 1 || len(t.Message.Instructions) > 1 { + panic("wrong transaction shape") + } + if !ed25519.Verify(ed25519.PublicKey(t.Message.AccountKeys[0][:]), w[65:], w[1:65]) { + panic("invalid signature") + } +} +func getSlot() uint64 { + var s uint64 + must(rpc("getSlot", []any{map[string]any{"commitment": "processed"}}, &s)) + return s +} +func getHash() (solana.Hash, uint64) { + var r struct { + Value struct { + Blockhash string + LastValidBlockHeight uint64 + } + } + must(rpc("getLatestBlockhash", []any{map[string]any{"commitment": "confirmed"}}, &r)) + h, e := solana.HashFromBase58(r.Value.Blockhash) + must(e) + return h, r.Value.LastValidBlockHeight +} +func selectWindow() uint64 { + var e struct{ AbsoluteSlot, SlotIndex uint64 } + must(rpc("getEpochInfo", []any{}, &e)) + var sched map[string][]uint64 + must(rpc("getLeaderSchedule", []any{nil, map[string]any{"identity": identity}}, &sched)) + base := e.AbsoluteSlot - e.SlotIndex + for _, s := range sched[identity] { + abs := base + s + if abs%4 == 0 && abs > e.AbsoluteSlot+65 { + return abs + } + } + panic("no suitable leader window in this epoch") +} +func awaitSlot(target uint64) uint64 { + deadline := time.Now().Add(10 * time.Minute) + for time.Now().Before(deadline) { + s := getSlot() + if s >= target { + return s + } + if target-s > 30 { + time.Sleep(2 * time.Second) + } else { + time.Sleep(75 * time.Millisecond) + } + } + panic("wait deadline") +} +func makePhasedPairWire(keys []ed25519.PrivateKey, first, second solana.Hash, pool []solana.PublicKey, i int) []byte { + if i < 0 || i >= 200000 || first == second { + panic("invalid phased load input") + } + txHash, ordinal := first, i/8 + if i >= 120000 { + txHash, ordinal = second, (i-120000)/8 + } + return makePairWire(keys[i%8], txHash, pool, ordinal) +} + +func main() { + flag.Parse() + if *shape != "readonly-pair" && *shape != "account-nonce" && *shape != "compute-budget" { + panic("invalid shape") + } + if *mode == "init" { + initKeys() + return + } + if *count < 1 || *count > 200000 || *rate < 1 || *rate > 150000 || *prefill < 0 || *prefill > 20 { + panic("outside experiment bounds") + } + if filepath.Base(*stage) != *stage { + panic("invalid stage") + } + if *mode == "selfcheck" { + _, p, e := ed25519.GenerateKey(rand.Reader) + must(e) + w := makeWire(p, solana.Hash{}, sha256.Sum256([]byte("test"))) + check(w) + check(makeBudgetWire(p, solana.Hash{}, 1000)) + pool := make([]solana.PublicKey, 128) + for i := range pool { + pool[i] = solana.PublicKey{byte(i + 1), 77} + } + seen := map[[64]byte]bool{} + for i := 0; i < 128*127; i++ { + a, b := pairIndices(i) + var pair [64]byte + copy(pair[:32], pool[a][:]) + copy(pair[32:], pool[b][:]) + if a == b || seen[pair] { + panic("pair collision") + } + seen[pair] = true + } + for _, i := range []int{0, 1, 126, 127, 14999, 16255} { + check(makePairWire(p, solana.Hash{}, pool, i)) + } + logEvent(map[string]any{"wire_bytes": 198, "signatures": 1, "signature_verified": true, "unique_pairs": len(seen)}) + return + } + var g string + must(rpc("getGenesisHash", []any{}, &g)) + if g != genesis { + panic("unexpected cluster genesis") + } + keys := loadKeys() + pool := loadPool() + if *mode == "simulate" { + h, _ := getHash() + w := makeWire(keys[0], h, sha256.Sum256([]byte("simulation"))) + if *shape == "compute-budget" { + w = makeBudgetWire(keys[0], h, 1000) + } + if *shape == "readonly-pair" { + w = makePairWire(keys[0], h, pool, 14999) + } + check(w) + var sim any + must(rpc("simulateTransaction", []any{base64.StdEncoding.EncodeToString(w), map[string]any{"encoding": "base64", "sigVerify": true, "commitment": "confirmed"}}, &sim)) + save(filepath.Join(*root, *shape+"-simulation.json"), sim) + logEvent(sim) + return + } + if *mode != "run" { + panic("unknown mode") + } + // Claim the stage before signing/sending. No automatic replay of uncertain runs. + out := filepath.Join(*root, *stage) + must(os.Mkdir(out, 0700)) + // Bound all experiment sends by a durable count, including uncertain prior runs. + ledger := filepath.Join(*root, "authorized-count.json") + used := 0 + if b, e := os.ReadFile(ledger); e == nil { + must(json.Unmarshal(b, &used)) + } else if !os.IsNotExist(e) { + must(e) + } + if used+*count > 200000 { + panic("cumulative maximum fee 1.0 testnet SOL exceeded") + } + save(ledger, used+*count) + window := selectWindow() + logEvent(map[string]any{"event": "waiting_to_prepare", "leader_window": window, "now_slot": getSlot(), "count": *count, "offered_tps": *rate}) + awaitSlot(window - 55) + hash, lastHeight := getHash() + hashLedger := filepath.Join(*root, "used-blockhashes.json") + usedHashes := map[string]bool{} + if data, err := os.ReadFile(hashLedger); err == nil { + must(json.Unmarshal(data, &usedHashes)) + } else if !os.IsNotExist(err) { + must(err) + } + if usedHashes[hash.String()] { + panic("blockhash already used by this experiment") + } + usedHashes[hash.String()] = true + save(hashLedger, usedHashes) + secondHash, secondHeight := hash, lastHeight + if *count > 120000 { + for attempts := 0; attempts < 40; attempts++ { + time.Sleep(100 * time.Millisecond) + secondHash, secondHeight = getHash() + if secondHash != hash { + break + } + } + if secondHash == hash || usedHashes[secondHash.String()] { + panic("no fresh top-up blockhash") + } + usedHashes[secondHash.String()] = true + save(hashLedger, usedHashes) + } + nonce := make([]byte, 32) + _, e := rand.Read(nonce) + must(e) + wires := make([][]byte, *count) + next := atomic.Int64{} + var wg sync.WaitGroup + beg := time.Now() + for k := 0; k < 4; k++ { + wg.Add(1) + go func() { + defer wg.Done() + for { + i := int(next.Add(1) - 1) + if i >= len(wires) { + return + } + n := append([]byte(nil), nonce...) + n = binary.LittleEndian.AppendUint64(n, uint64(i)) + wires[i] = makeWire(keys[i%8], hash, sha256.Sum256(n)) + if *shape == "compute-budget" { + wires[i] = makeBudgetWire(keys[i%8], hash, uint32(1000+i)) + } + if *shape == "readonly-pair" { + if len(wires) > 120000 { + wires[i] = makePhasedPairWire(keys, hash, secondHash, pool, i) + } else { + wires[i] = makePairWire(keys[i%8], hash, pool, i/8) + } + } + } + }() + } + wg.Wait() + check(wires[0]) + check(wires[len(wires)-1]) + if len(wires) > 120000 { + check(wires[120000]) + } + ids := make([]string, len(wires)) + for i, w := range wires { + ids[i] = solana.SignatureFromBytes(w[1:65]).String() + } + save(filepath.Join(out, "signatures.json"), ids) + meta := map[string]any{"window": window, "count": *count, "offered_tps": *rate, "transaction_bytes": len(wires[0]), "shape": *shape, "signatures_per_transaction": 1, "prefill_slots": *prefill, "blockhash": hash.String(), "last_valid_block_height": max(lastHeight, secondHeight), "topup_blockhash": secondHash.String(), "prefill_count": *count, "topup_count": 0, "topup_trigger": "none; entire workload preloaded", "prepare_seconds": time.Since(beg).Seconds(), "sender": "127.0.0.1:8011; GOMAXPROCS=2; signatures prepared before send"} + save(filepath.Join(out, "metadata.json"), meta) + if getSlot() >= window-uint64(*prefill) { + panic("missed preparation deadline; no sends") + } + client, e := testutils.NewClient() + must(e) + // Four connections and four streams each respect the validator's existing limits. + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + type senderFn func(context.Context, []byte) error + senders := make([]senderFn, 4) + var closers []func() + for i := range senders { + c, e := client.Dial(ctx, "127.0.0.1:8011") + must(e) + senders[i] = func(cctx context.Context, w []byte) error { return client.Send(cctx, c, w) } + closers = append(closers, func() { _ = c.CloseWithError(0, "") }) + } + defer func() { + for _, f := range closers { + f() + } + }() + observed := awaitSlot(window - uint64(*prefill)) + if observed > window+1 { + panic("missed send window; no sends") + } + var height uint64 + must(rpc("getBlockHeight", []any{map[string]any{"commitment": "confirmed"}}, &height)) + if height+10 >= lastHeight { + panic("blockhash too old") + } + logEvent(map[string]any{"event": "sending", "observed_slot": observed, "leader_window": window}) + start := time.Now() + jobs := make(chan int, 512) + var sent, errors, skipped atomic.Uint64 + var firstErr string + var errMu sync.Mutex + var endMu sync.Mutex + lastSend := start + sendCtx, stop := context.WithTimeout(context.Background(), 3*time.Second) + defer stop() + for k := 0; k < 16; k++ { + wg.Add(1) + go func(worker int) { + defer wg.Done() + for i := range jobs { + if sendCtx.Err() != nil { + skipped.Add(1) + continue + } + e := senders[worker%4](sendCtx, wires[i]) + if e != nil { + errors.Add(1) + errMu.Lock() + if firstErr == "" { + firstErr = e.Error() + } + errMu.Unlock() + } else { + sent.Add(1) + endMu.Lock() + lastSend = time.Now() + endMu.Unlock() + } + } + }(k) + } + for i := 0; i < len(wires); { + + if sendCtx.Err() != nil { + skipped.Add(uint64(len(wires) - i)) + break + } + end := i + 150 + if end > len(wires) { + end = len(wires) + } + target := start.Add(time.Duration(float64(i) / float64(*rate) * float64(time.Second))) + if d := time.Until(target); d > 0 { + time.Sleep(d) + } + for ; i < end; i++ { + select { + case jobs <- i: + case <-sendCtx.Done(): + skipped.Add(1) + } + } + } + close(jobs) + wg.Wait() + summary := map[string]any{"sent": sent.Load(), "send_errors": errors.Load(), "not_sent": skipped.Load(), "first_error": firstErr, "start_utc": start.UTC().Format(time.RFC3339Nano), "end_utc": lastSend.UTC().Format(time.RFC3339Nano), "send_seconds": lastSend.Sub(start).Seconds(), "end_slot": getSlot(), "target_window": window} + save(filepath.Join(out, "send.json"), summary) + logEvent(summary) + // Let already-written stream data be acknowledged before closing QUIC connections. + time.Sleep(500 * time.Millisecond) +} diff --git a/docs/results/leader-block-packing/2026-09-13/local-race.txt b/docs/results/leader-block-packing/2026-09-13/local-race.txt new file mode 100644 index 00000000..1a9f78b6 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/local-race.txt @@ -0,0 +1,11 @@ +ok github.com/Overclock-Validator/mithril/pkg/accounts 1.305s +ok github.com/Overclock-Validator/mithril/pkg/blockprod 21.817s +ok github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler 1.949s +ok github.com/Overclock-Validator/mithril/pkg/costmodel 2.008s +ok github.com/Overclock-Validator/mithril/pkg/merkletree 2.233s +ok github.com/Overclock-Validator/mithril/pkg/replay 4.644s +ok github.com/Overclock-Validator/mithril/pkg/fees 1.961s +ok github.com/Overclock-Validator/mithril/pkg/turbine 5.114s +ok github.com/Overclock-Validator/mithril/pkg/tpu/txfixture 54.959s +ok github.com/Overclock-Validator/mithril/pkg/config 2.348s +ok github.com/Overclock-Validator/mithril/cmd/mithril/node 3.229s diff --git a/docs/results/leader-block-packing/2026-09-13/local-unit.txt b/docs/results/leader-block-packing/2026-09-13/local-unit.txt new file mode 100644 index 00000000..0cb4e105 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/local-unit.txt @@ -0,0 +1,10 @@ +ok github.com/Overclock-Validator/mithril/pkg/accounts 0.223s +ok github.com/Overclock-Validator/mithril/pkg/blockprod 0.499s +ok github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler 0.593s +ok github.com/Overclock-Validator/mithril/pkg/costmodel 0.816s +ok github.com/Overclock-Validator/mithril/pkg/merkletree 0.625s +ok github.com/Overclock-Validator/mithril/pkg/replay 1.666s +ok github.com/Overclock-Validator/mithril/pkg/fees 0.985s +ok github.com/Overclock-Validator/mithril/pkg/turbine 2.322s +ok github.com/Overclock-Validator/mithril/pkg/config 1.300s +ok github.com/Overclock-Validator/mithril/cmd/mithril/node 1.840s diff --git a/docs/results/leader-block-packing/2026-09-13/native-build.txt b/docs/results/leader-block-packing/2026-09-13/native-build.txt new file mode 100644 index 00000000..e69de29b diff --git a/docs/results/leader-block-packing/2026-09-13/native-race.txt b/docs/results/leader-block-packing/2026-09-13/native-race.txt new file mode 100644 index 00000000..30a569aa --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/native-race.txt @@ -0,0 +1,11 @@ +ok github.com/Overclock-Validator/mithril/pkg/accounts 1.108s +ok github.com/Overclock-Validator/mithril/pkg/blockprod 14.102s +ok github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler 1.384s +ok github.com/Overclock-Validator/mithril/pkg/costmodel 1.181s +ok github.com/Overclock-Validator/mithril/pkg/merkletree 1.364s +ok github.com/Overclock-Validator/mithril/pkg/replay 2.236s +ok github.com/Overclock-Validator/mithril/pkg/fees 1.135s +ok github.com/Overclock-Validator/mithril/pkg/turbine 3.135s +ok github.com/Overclock-Validator/mithril/pkg/tpu/txfixture 31.532s +ok github.com/Overclock-Validator/mithril/pkg/config 1.007s +ok github.com/Overclock-Validator/mithril/cmd/mithril/node 1.181s diff --git a/docs/results/leader-block-packing/2026-09-13/native-vet.txt b/docs/results/leader-block-packing/2026-09-13/native-vet.txt new file mode 100644 index 00000000..e69de29b diff --git a/docs/results/leader-block-packing/2026-09-13/round-1-baseline.txt b/docs/results/leader-block-packing/2026-09-13/round-1-baseline.txt new file mode 100644 index 00000000..8e75ec08 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/round-1-baseline.txt @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/blockprod +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkReadonlyPairFullBlock/wire-8 3 209635460 ns/op 48622 tx/block 259271528 B/op 3363451 allocs/op +BenchmarkReadonlyPairFullBlock/decoded-8 3 188551991 ns/op 48622 tx/block 237879586 B/op 3071729 allocs/op +PASS diff --git a/docs/results/leader-block-packing/2026-09-13/round-1-candidate.txt b/docs/results/leader-block-packing/2026-09-13/round-1-candidate.txt new file mode 100644 index 00000000..2b12f04a --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/round-1-candidate.txt @@ -0,0 +1,8 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/blockprod +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkReadonlyPairFullBlock/wire-8 3 167507945 ns/op 48622 tx/block 223454984 B/op 2728489 allocs/op +BenchmarkReadonlyPairFullBlock/decoded-8 3 129990183 ns/op 48622 tx/block 202061266 B/op 2436756 allocs/op +BenchmarkReadonlyPairPreparedFullBlock-8 3 121710569 ns/op 48622 tx/block 173666002 B/op 1950536 allocs/op +PASS diff --git a/docs/results/leader-block-packing/2026-09-13/round-2-baseline.txt b/docs/results/leader-block-packing/2026-09-13/round-2-baseline.txt new file mode 100644 index 00000000..9270847f --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/round-2-baseline.txt @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/blockprod +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkReadonlyPairFullBlock/wire-8 3 206873746 ns/op 48622 tx/block 259272293 B/op 3363455 allocs/op +BenchmarkReadonlyPairFullBlock/decoded-8 3 214317833 ns/op 48622 tx/block 237880618 B/op 3071736 allocs/op +PASS diff --git a/docs/results/leader-block-packing/2026-09-13/round-2-candidate.txt b/docs/results/leader-block-packing/2026-09-13/round-2-candidate.txt new file mode 100644 index 00000000..ba552945 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/round-2-candidate.txt @@ -0,0 +1,8 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/blockprod +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkReadonlyPairFullBlock/wire-8 3 145202527 ns/op 48622 tx/block 223454946 B/op 2728489 allocs/op +BenchmarkReadonlyPairFullBlock/decoded-8 3 151023757 ns/op 48622 tx/block 202061384 B/op 2436757 allocs/op +BenchmarkReadonlyPairPreparedFullBlock-8 3 101921901 ns/op 48622 tx/block 173666021 B/op 1950536 allocs/op +PASS diff --git a/docs/results/leader-block-packing/2026-09-13/round-3-baseline.txt b/docs/results/leader-block-packing/2026-09-13/round-3-baseline.txt new file mode 100644 index 00000000..ef23596b --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/round-3-baseline.txt @@ -0,0 +1,7 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/blockprod +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkReadonlyPairFullBlock/wire-8 3 209864927 ns/op 48622 tx/block 259274824 B/op 3363464 allocs/op +BenchmarkReadonlyPairFullBlock/decoded-8 3 189729779 ns/op 48622 tx/block 237880589 B/op 3071738 allocs/op +PASS diff --git a/docs/results/leader-block-packing/2026-09-13/round-3-candidate.txt b/docs/results/leader-block-packing/2026-09-13/round-3-candidate.txt new file mode 100644 index 00000000..a2279a51 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/round-3-candidate.txt @@ -0,0 +1,8 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/blockprod +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkReadonlyPairFullBlock/wire-8 3 150295927 ns/op 48622 tx/block 223455032 B/op 2728489 allocs/op +BenchmarkReadonlyPairFullBlock/decoded-8 3 132400481 ns/op 48622 tx/block 202061368 B/op 2436757 allocs/op +BenchmarkReadonlyPairPreparedFullBlock-8 3 101595082 ns/op 48622 tx/block 173666032 B/op 1950536 allocs/op +PASS diff --git a/docs/results/leader-block-packing/2026-09-13/run-native.sh b/docs/results/leader-block-packing/2026-09-13/run-native.sh new file mode 100644 index 00000000..8961a311 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/run-native.sh @@ -0,0 +1,31 @@ +set -eu +review_dir=/srv/mithril-leader-packing-pr-20260913 +mkdir "$review_dir" +mkdir "$review_dir/candidate" "$review_dir/baseline" "$review_dir/results" +tar -xzf /tmp/mithril-leader-packing-pr-20260913.tar.gz -C "$review_dir/candidate" +tar -xf /tmp/mithril-leader-base-20260913.tar -C "$review_dir/baseline" +cp "$review_dir/candidate/pkg/blockprod/readonly_block_bench_test.go" "$review_dir/baseline/pkg/blockprod/" +cp "$review_dir/candidate/pkg/tpu/txfixture/readonly_pair.go" "$review_dir/baseline/pkg/tpu/txfixture/" +export PATH=/usr/local/go/bin:$PATH +export GOMAXPROCS=8 +systemctl show mithril-alpenglow-pr259.service -p MainPID -p ActiveState > "$review_dir/results/service-before.txt" +sha256sum /tmp/mithril-leader-packing-pr-20260913.tar.gz /tmp/mithril-leader-base-20260913.tar > "$review_dir/results/source-sha256.txt" +lscpu -e=CPU,CORE,SOCKET > "$review_dir/results/topology.txt" +cd "$review_dir/candidate" +nice -n 10 go test -race ./pkg/accounts ./pkg/blockprod/... ./pkg/costmodel ./pkg/merkletree ./pkg/replay ./pkg/fees ./pkg/turbine ./pkg/tpu/txfixture ./pkg/config ./cmd/mithril/node > "$review_dir/results/native-race.txt" 2>&1 +nice -n 10 go vet ./pkg/accounts ./pkg/blockprod/... ./pkg/costmodel ./pkg/merkletree ./pkg/replay ./pkg/fees ./pkg/turbine ./pkg/tpu/txfixture ./pkg/config ./cmd/mithril/node > "$review_dir/results/native-vet.txt" 2>&1 +nice -n 10 go build -o "$review_dir/results/mithril" ./cmd/mithril > "$review_dir/results/native-build.txt" 2>&1 +nice -n 10 go test -c -o "$review_dir/results/candidate.test" ./pkg/blockprod +cd "$review_dir/baseline" +nice -n 10 go test -run '^TestReadonlyPairBlockCapacityAndShredRoundTrip$' ./pkg/blockprod > "$review_dir/results/baseline-capacity.txt" 2>&1 +nice -n 10 go test -c -o "$review_dir/results/baseline.test" ./pkg/blockprod +for round in 1 2 3; do + if [ "$round" = 2 ]; then order='candidate baseline'; else order='baseline candidate'; fi + for variant in $order; do + nice -n 10 taskset -c 0-7 "$review_dir/results/$variant.test" -test.run='^$' -test.bench='^BenchmarkReadonlyPair(FullBlock|PreparedFullBlock)$' -test.benchtime=3x -test.count=1 > "$review_dir/results/round-$round-$variant.txt" 2>&1 + done +done +systemctl show mithril-alpenglow-pr259.service -p MainPID -p ActiveState > "$review_dir/results/service-after.txt" +sha256sum "$review_dir/results/"*.test "$review_dir/results/mithril" > "$review_dir/results/binary-sha256.txt" +cat "$review_dir/results/native-race.txt" +cat "$review_dir/results/round-"*.txt diff --git a/docs/results/leader-block-packing/2026-09-13/service-after.txt b/docs/results/leader-block-packing/2026-09-13/service-after.txt new file mode 100644 index 00000000..bece5e70 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/service-after.txt @@ -0,0 +1,2 @@ +ActiveState=active +MainPID=3846480 diff --git a/docs/results/leader-block-packing/2026-09-13/service-before.txt b/docs/results/leader-block-packing/2026-09-13/service-before.txt new file mode 100644 index 00000000..bece5e70 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/service-before.txt @@ -0,0 +1,2 @@ +ActiveState=active +MainPID=3846480 diff --git a/docs/results/leader-block-packing/2026-09-13/source-sha256.txt b/docs/results/leader-block-packing/2026-09-13/source-sha256.txt new file mode 100644 index 00000000..c4996c08 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/source-sha256.txt @@ -0,0 +1,2 @@ +c08b4bee669cd754aed39abe4235ab493679ce15ac9f12f928027ea40015c830 /tmp/mithril-leader-packing-pr-20260913.tar.gz +e695e40e720139547b6b6ad5b84ed65ff1d21423874ef352a5bdb681121065ae /tmp/mithril-leader-base-20260913.tar diff --git a/docs/results/leader-block-packing/2026-09-13/summarize.py b/docs/results/leader-block-packing/2026-09-13/summarize.py new file mode 100644 index 00000000..6ae50531 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/summarize.py @@ -0,0 +1,20 @@ +"""Summarize the paired standalone bank benchmarks; run beside the raw logs.""" +import json +from pathlib import Path +from statistics import median + +root = Path(__file__).resolve().parent +samples = {} +for path in sorted(root.glob('round-*.txt')): + variant = path.stem.rsplit('-', 1)[1] + for line in path.read_text().splitlines(): + if not line.startswith('Benchmark'): + continue + fields = line.split() + name = fields[0].rsplit('-', 1)[0] + metrics = dict(zip(fields[3::2], map(float, fields[2::2]))) + samples.setdefault(variant + ':' + name, []).append(metrics) +summary = {key: {metric: median(s[metric] for s in values) for metric in values[0]} + for key, values in sorted(samples.items())} +(root / 'summary.json').write_text(json.dumps({'samples': samples, 'medians': summary}, indent=2) + '\n') +print(json.dumps(summary, indent=2)) diff --git a/docs/results/leader-block-packing/2026-09-13/summary.json b/docs/results/leader-block-packing/2026-09-13/summary.json new file mode 100644 index 00000000..c8bcef79 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/summary.json @@ -0,0 +1,136 @@ +{ + "samples": { + "baseline:BenchmarkReadonlyPairFullBlock/wire": [ + { + "ns/op": 209635460.0, + "tx/block": 48622.0, + "B/op": 259271528.0, + "allocs/op": 3363451.0 + }, + { + "ns/op": 206873746.0, + "tx/block": 48622.0, + "B/op": 259272293.0, + "allocs/op": 3363455.0 + }, + { + "ns/op": 209864927.0, + "tx/block": 48622.0, + "B/op": 259274824.0, + "allocs/op": 3363464.0 + } + ], + "baseline:BenchmarkReadonlyPairFullBlock/decoded": [ + { + "ns/op": 188551991.0, + "tx/block": 48622.0, + "B/op": 237879586.0, + "allocs/op": 3071729.0 + }, + { + "ns/op": 214317833.0, + "tx/block": 48622.0, + "B/op": 237880618.0, + "allocs/op": 3071736.0 + }, + { + "ns/op": 189729779.0, + "tx/block": 48622.0, + "B/op": 237880589.0, + "allocs/op": 3071738.0 + } + ], + "candidate:BenchmarkReadonlyPairFullBlock/wire": [ + { + "ns/op": 167507945.0, + "tx/block": 48622.0, + "B/op": 223454984.0, + "allocs/op": 2728489.0 + }, + { + "ns/op": 145202527.0, + "tx/block": 48622.0, + "B/op": 223454946.0, + "allocs/op": 2728489.0 + }, + { + "ns/op": 150295927.0, + "tx/block": 48622.0, + "B/op": 223455032.0, + "allocs/op": 2728489.0 + } + ], + "candidate:BenchmarkReadonlyPairFullBlock/decoded": [ + { + "ns/op": 129990183.0, + "tx/block": 48622.0, + "B/op": 202061266.0, + "allocs/op": 2436756.0 + }, + { + "ns/op": 151023757.0, + "tx/block": 48622.0, + "B/op": 202061384.0, + "allocs/op": 2436757.0 + }, + { + "ns/op": 132400481.0, + "tx/block": 48622.0, + "B/op": 202061368.0, + "allocs/op": 2436757.0 + } + ], + "candidate:BenchmarkReadonlyPairPreparedFullBlock": [ + { + "ns/op": 121710569.0, + "tx/block": 48622.0, + "B/op": 173666002.0, + "allocs/op": 1950536.0 + }, + { + "ns/op": 101921901.0, + "tx/block": 48622.0, + "B/op": 173666021.0, + "allocs/op": 1950536.0 + }, + { + "ns/op": 101595082.0, + "tx/block": 48622.0, + "B/op": 173666032.0, + "allocs/op": 1950536.0 + } + ] + }, + "medians": { + "baseline:BenchmarkReadonlyPairFullBlock/decoded": { + "ns/op": 189729779.0, + "tx/block": 48622.0, + "B/op": 237880589.0, + "allocs/op": 3071736.0 + }, + "baseline:BenchmarkReadonlyPairFullBlock/wire": { + "ns/op": 209635460.0, + "tx/block": 48622.0, + "B/op": 259272293.0, + "allocs/op": 3363455.0 + }, + "candidate:BenchmarkReadonlyPairFullBlock/decoded": { + "ns/op": 132400481.0, + "tx/block": 48622.0, + "B/op": 202061368.0, + "allocs/op": 2436757.0 + }, + "candidate:BenchmarkReadonlyPairFullBlock/wire": { + "ns/op": 150295927.0, + "tx/block": 48622.0, + "B/op": 223454984.0, + "allocs/op": 2728489.0 + }, + "candidate:BenchmarkReadonlyPairPreparedFullBlock": { + "ns/op": 101921901.0, + "tx/block": 48622.0, + "B/op": 173666021.0, + "allocs/op": 1950536.0 + } + } +} diff --git a/docs/results/leader-block-packing/2026-09-13/topology.txt b/docs/results/leader-block-packing/2026-09-13/topology.txt new file mode 100644 index 00000000..4647ea84 --- /dev/null +++ b/docs/results/leader-block-packing/2026-09-13/topology.txt @@ -0,0 +1,17 @@ +CPU CORE SOCKET + 0 0 0 + 1 1 0 + 2 2 0 + 3 3 0 + 4 4 0 + 5 5 0 + 6 6 0 + 7 7 0 + 8 0 0 + 9 1 0 + 10 2 0 + 11 3 0 + 12 4 0 + 13 5 0 + 14 6 0 + 15 7 0 From 91c268fb1658f23777c916fe73327eee40c4edee Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 02:44:22 -0500 Subject: [PATCH 11/22] Reduce VM allocation and observer overhead before Alpenglow voting --- pkg/alpenglow/observer.go | 17 +++++- pkg/alpenglow/observer_bench_test.go | 22 +++++++ pkg/alpenglow/observer_stats_test.go | 85 ++++++++++++++++++++++++++++ pkg/config/config.go | 3 + pkg/config/pooling_defaults_test.go | 28 +++++++++ pkg/sbpf/pooling_test.go | 75 ++++++++++++++++++++++++ 6 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 pkg/alpenglow/observer_bench_test.go create mode 100644 pkg/alpenglow/observer_stats_test.go create mode 100644 pkg/config/pooling_defaults_test.go create mode 100644 pkg/sbpf/pooling_test.go diff --git a/pkg/alpenglow/observer.go b/pkg/alpenglow/observer.go index 9b84e58c..d9c6d3f8 100644 --- a/pkg/alpenglow/observer.go +++ b/pkg/alpenglow/observer.go @@ -95,6 +95,11 @@ type Observer struct { replayBlocks map[uint64]BlockID replayOrder []uint64 replayChecks map[CertificateKey]certificateReplayCheck + // Votes do not change certificate/replay reconciliation. Reuse its exact + // statistics until one of those inputs changes instead of scanning every + // retained certificate for each incoming vote. + pendingStats certificateReplayPendingStats + pendingStatsValid bool votesObserved uint64 certificatesObserved uint64 @@ -211,6 +216,7 @@ func (o *Observer) ObserveCertificate(cert Certificate) (Observation, error) { key := cert.Key() _, exists := o.certificates[key] if !exists { + o.pendingStatsValid = false tracked := o.trackCertificateLocked(key, cert) o.certificatesObserved++ o.applyCertificateLocked(cert) @@ -225,6 +231,7 @@ func (o *Observer) ObserveCertificate(cert Certificate) (Observation, error) { func (o *Observer) ObserveReplayBlock(obs ReplayBlockObservation) Observation { o.mu.Lock() defer o.mu.Unlock() + o.pendingStatsValid = false if obs.At.IsZero() { obs.At = time.Now() @@ -260,8 +267,8 @@ func (o *Observer) ObserveReplayResult(obs ReplayResultObservation) Observation } func (o *Observer) Snapshot() Snapshot { - o.mu.RLock() - defer o.mu.RUnlock() + o.mu.Lock() + defer o.mu.Unlock() return o.snapshotLocked() } @@ -454,7 +461,11 @@ func (o *Observer) certificateReplayPendingStatsLocked() certificateReplayPendin } func (o *Observer) snapshotLocked() Snapshot { - pending := o.certificateReplayPendingStatsLocked() + if !o.pendingStatsValid { + o.pendingStats = o.certificateReplayPendingStatsLocked() + o.pendingStatsValid = true + } + pending := o.pendingStats return Snapshot{ VotesObserved: o.votesObserved, CertificatesObserved: o.certificatesObserved, diff --git a/pkg/alpenglow/observer_bench_test.go b/pkg/alpenglow/observer_bench_test.go new file mode 100644 index 00000000..8ff14d39 --- /dev/null +++ b/pkg/alpenglow/observer_bench_test.go @@ -0,0 +1,22 @@ +package alpenglow + +import "testing" + +func BenchmarkObserverVoteWithRetainedCertificates(b *testing.B) { + o := NewObserver() + for slot := uint64(1); slot <= DefaultMaxTrackedCertificates; slot++ { + _, err := o.ObserveCertificate(Certificate{Type: CertificateNotarize, Slot: slot, + BlockHash: testHash(1), IncludedStake: 80, TotalStake: 100}) + if err != nil { + b.Fatal(err) + } + } + msg := VoteMessage{Vote: NewSkipVote(5000), Rank: 1} + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + if _, err := o.ObserveVote(msg); err != nil { + b.Fatal(err) + } + } +} diff --git a/pkg/alpenglow/observer_stats_test.go b/pkg/alpenglow/observer_stats_test.go new file mode 100644 index 00000000..3000de92 --- /dev/null +++ b/pkg/alpenglow/observer_stats_test.go @@ -0,0 +1,85 @@ +package alpenglow + +import ( + "fmt" + "math/rand" + "sync" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestObserverPendingStatsMatchFullScan(t *testing.T) { + // Exercise eviction, duplicate certificates, out-of-order and hashless + // replay, and retained matches/mismatches with and without retention. + for _, retention := range []int{0, 1, 8, 64} { + t.Run(fmt.Sprint(retention), func(t *testing.T) { + o := NewObserverWithConfig(ObserverConfig{MaxTrackedVotes: 8, + MaxTrackedCertificates: retention, MaxTrackedReplayBlocks: retention}) + rng := rand.New(rand.NewSource(37)) + for i := 0; i < 1000; i++ { + slot := uint64(rng.Intn(80) + 1) + switch rng.Intn(5) { + case 0, 1: + typ := CertificateNotarize + if i%3 == 0 { + typ = CertificateFinalizeFast + } + _, err := o.ObserveCertificate(Certificate{Type: typ, Slot: slot, + BlockHash: testHash(byte(slot%3 + 1)), IncludedStake: 80, TotalStake: 100}) + require.NoError(t, err) + case 2: + block := BlockID{Slot: slot} + if i%5 != 0 { + block.Hash = testHash(byte(slot%4 + 1)) + } + o.ObserveReplayBlock(ReplayBlockObservation{Block: block}) + case 3: + _, err := o.ObserveVote(VoteMessage{Vote: NewSkipVote(slot), Rank: 1}) + require.NoError(t, err) + case 4: + o.ObserveReplayResult(ReplayResultObservation{Slot: slot}) + } + o.Snapshot() + o.mu.RLock() + cached, scanned := o.pendingStats, o.certificateReplayPendingStatsLocked() + o.mu.RUnlock() + require.Equal(t, scanned, cached, "operation %d", i) + } + }) + } +} + +func TestObserverConcurrentSnapshotsAndReconciliation(t *testing.T) { + o := NewObserver() + var wg sync.WaitGroup + for worker := 0; worker < 4; worker++ { + wg.Add(1) + go func(worker int) { + defer wg.Done() + for slot := uint64(1); slot <= 100; slot++ { + switch worker { + case 0: + o.ObserveReplayBlock(ReplayBlockObservation{Block: BlockID{Slot: slot, Hash: testHash(1)}}) + case 1: + _, err := o.ObserveCertificate(Certificate{Type: CertificateNotarize, Slot: slot, + BlockHash: testHash(1), IncludedStake: 80, TotalStake: 100}) + if err != nil { + t.Error(err) + } + case 2: + _, err := o.ObserveVote(VoteMessage{Vote: NewSkipVote(slot), Rank: 1}) + if err != nil { + t.Error(err) + } + case 3: + o.Snapshot() + } + } + }(worker) + } + wg.Wait() + snapshot := o.Snapshot() + require.Equal(t, uint64(100), snapshot.CertificateReplayMatches) + require.Zero(t, snapshot.CertificateReplayPending) +} diff --git a/pkg/config/config.go b/pkg/config/config.go index d37fb3c0..ef7ec2af 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -12,6 +12,9 @@ const LightbringerQuietDefault = true func ApplyDefaults(v *viper.Viper) { v.SetDefault("lightbringer.quiet", LightbringerQuietDefault) + // The run command resolves this boolean through Viper, so its CLI default + // alone does not apply when the TOML setting is omitted. + v.SetDefault("tuning.use_pool", true) // network.cluster and block.source default in the run command itself // (Alpenglow/turbine, classic/RPC) — NOT here, because the lightbringer // auto-switch needs to distinguish "operator chose a source" from "defaulted". diff --git a/pkg/config/pooling_defaults_test.go b/pkg/config/pooling_defaults_test.go new file mode 100644 index 00000000..84fb367d --- /dev/null +++ b/pkg/config/pooling_defaults_test.go @@ -0,0 +1,28 @@ +package config + +import ( + "strings" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" +) + +func TestVMMemoryPoolingDefaultAndExplicitOverride(t *testing.T) { + for _, tc := range []struct { + name, toml string + want bool + }{ + {"omitted", "[tuning]\ntxpar = 8\n", true}, + {"enabled", "[tuning]\nuse_pool = true\n", true}, + {"disabled", "[tuning]\nuse_pool = false\n", false}, + } { + t.Run(tc.name, func(t *testing.T) { + v := viper.New() + ApplyDefaults(v) + v.SetConfigType("toml") + require.NoError(t, v.ReadConfig(strings.NewReader(tc.toml))) + require.Equal(t, tc.want, v.GetBool("tuning.use_pool")) + }) + } +} diff --git a/pkg/sbpf/pooling_test.go b/pkg/sbpf/pooling_test.go new file mode 100644 index 00000000..38d1028f --- /dev/null +++ b/pkg/sbpf/pooling_test.go @@ -0,0 +1,75 @@ +package sbpf + +import ( + "bytes" + "sync" + "testing" + + "github.com/Overclock-Validator/mithril/pkg/cu" + "github.com/stretchr/testify/require" +) + +func poolingInterpreter(heap int) *Interpreter { + meter := cu.NewComputeMeter(100) + return NewInterpreter(testV3Program([]Slot{testSlot(OpExit, 0, 0, 0, 0)}, nil), + &VMOpts{HeapMax: heap, ComputeMeter: &meter}) +} + +func TestPooledVMIsolationAcrossNestedAndConcurrentExecutions(t *testing.T) { + old := UsePool + UsePool = true + t.Cleanup(func() { UsePool = old }) + var wg sync.WaitGroup + for worker := 0; worker < 8; worker++ { + wg.Add(1) + go func(worker int) { + defer wg.Done() + for i := 0; i < 30; i++ { + parent := poolingInterpreter(32 * 1024) + if !bytes.Equal(parent.heap, make([]byte, len(parent.heap))) || + !bytes.Equal(parent.stack.mem, make([]byte, len(parent.stack.mem))) { + t.Error("pooled VM exposed data from an earlier execution") + } + parent.heap[0] = byte(worker + 1) + parent.stack.mem[0] = byte(worker + 1) + child := poolingInterpreter(256 * 1024) + for j := range child.heap { + child.heap[j] = 0xab + } + for j := range child.stack.mem { + child.stack.mem[j] = 0xcd + } + child.Finish() + if parent.heap[0] != byte(worker+1) || parent.stack.mem[0] != byte(worker+1) { + t.Error("nested VM storage aliased its active parent") + } + parent.Finish() + } + }(worker) + } + wg.Wait() + ip := poolingInterpreter(32 * 1024) + defer ip.Finish() + require.Len(t, ip.heap, 32*1024) + _, err := ip.Translate(VaddrHeap+32*1024, 1, false) + require.Error(t, err, "pool capacity must not widen the requested heap mapping") +} + +func BenchmarkVMCreateAndFinish(b *testing.B) { + old := UsePool + b.Cleanup(func() { UsePool = old }) + for _, pooled := range []bool{false, true} { + name := "fresh" + if pooled { + name = "pooled" + } + b.Run(name, func(b *testing.B) { + UsePool = pooled + b.ReportAllocs() + for i := 0; i < b.N; i++ { + ip := poolingInterpreter(32 * 1024) + ip.Finish() + } + }) + } +} From 7fef571f568eb78c283bc1e2fdb8c96f041d031c Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 12:17:19 -0500 Subject: [PATCH 12/22] Fix Alpenglow Votor certificate compatibility with Firedancer --- pkg/alpenglow/testdata/README.md | 9 ++++ .../testdata/agave_votor_certificate.der | Bin 0 -> 249 bytes pkg/alpenglow/tls_identity.go | 51 +++++++++++------- pkg/alpenglow/tls_identity_test.go | 46 ++++++++++++++++ 4 files changed, 87 insertions(+), 19 deletions(-) create mode 100644 pkg/alpenglow/testdata/agave_votor_certificate.der diff --git a/pkg/alpenglow/testdata/README.md b/pkg/alpenglow/testdata/README.md index 92b75d4e..10997bf7 100644 --- a/pkg/alpenglow/testdata/README.md +++ b/pkg/alpenglow/testdata/README.md @@ -12,3 +12,12 @@ The v4.3 vote wire message intentionally excludes validator rank and stake; the authenticated Votor transport identity supplies those values after decode. The shred version is the final little-endian `u16`. Certificate bitmap vectors use wincode's default bincode-compatible little-endian `u64` length. + +`agave_votor_certificate.der` is the 249-byte certificate constructed by +`anza-xyz/agave` commit `8fe3f1201abc5b0244540aed0c7bf8c6bcafb3f5`, +`tls-utils/src/tls_certificates.rs::new_dummy_x509_certificate`, with public +key bytes `00..1f` at offsets 100–131. It also matches Firedancer commit +`de039cd7fc9f4714782ec7e3d47db3728903abc6`, +`src/ballet/x509/fd_x509_mock.c::fd_x509_mock_pubkey_v2`. Its fixed dummy +X.509 signature is intentional; TLS CertificateVerify proves possession of +the identity key. This fixture contains no private key. diff --git a/pkg/alpenglow/testdata/agave_votor_certificate.der b/pkg/alpenglow/testdata/agave_votor_certificate.der new file mode 100644 index 0000000000000000000000000000000000000000..e23bc0bfbe3e299e5b8ed04f44aad740f841d225 GIT binary patch literal 249 zcmXqL{ASR&ase|FBNGz`BNQ00vN3C?78r;biWms7F^94+^Kb{}=OpGOD&*y-q#7uQ z^O_qN7y=;}L`m?Q7+9Ji2^cUKXh98OR%BpcWMXDvWn<^yMC+6cQE@6%&_` zl#-T_m6KnrX`pT(4zx#Bkdg5}3$Fop6K76-a$-(KesPHb4@g27B*6qU7UD8yM~43t F0syv*UupmV literal 0 HcmV?d00001 diff --git a/pkg/alpenglow/tls_identity.go b/pkg/alpenglow/tls_identity.go index 1e8b91d9..3c18e0cf 100644 --- a/pkg/alpenglow/tls_identity.go +++ b/pkg/alpenglow/tls_identity.go @@ -5,9 +5,7 @@ import ( "crypto/rand" "crypto/tls" "crypto/x509" - "crypto/x509/pkix" "fmt" - "math/big" "time" "github.com/gagliardetto/solana-go" @@ -27,6 +25,36 @@ func newVotorQUICConfig() *quic.Config { } } +// votorCertificateTemplate is Agave's dummy X.509 certificate from +// tls-utils/src/tls_certificates.rs (8fe3f1201abc5b0244540aed0c7bf8c6bcafb3f5). +// Firedancer's fd_x509_mock_pubkey_v2 requires this exact encoding except for +// the 32-byte SubjectPublicKeyInfo at offset 100. The X.509 signature is +// deliberately invalid: TLS 1.3 CertificateVerify authenticates the identity. +// Keep the template unchanged and copy it before inserting a public key. +var votorCertificateTemplate = [...]byte{ + 0x30, 0x81, 0xf6, 0x30, 0x81, 0xa9, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, + 0x08, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x05, 0x06, + 0x03, 0x2b, 0x65, 0x70, 0x30, 0x16, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x04, 0x03, 0x0c, 0x0b, 0x53, 0x6f, 0x6c, 0x61, 0x6e, 0x61, 0x20, + 0x6e, 0x6f, 0x64, 0x65, 0x30, 0x20, 0x17, 0x0d, 0x37, 0x30, 0x30, 0x31, + 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x34, + 0x30, 0x39, 0x36, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x5a, 0x30, 0x00, 0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, + 0x70, 0x03, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa3, 0x29, 0x30, 0x27, 0x30, 0x17, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x01, + 0x01, 0xff, 0x04, 0x0d, 0x30, 0x0b, 0x82, 0x09, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, + 0x65, 0x70, 0x03, 0x41, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +} + // newVotorQUICCertificate creates the single-certificate Ed25519 identity // chain used by the Agave Votor transport. Peers recover the validator identity // directly from the leaf certificate's SubjectPublicKeyInfo; TLS 1.3's @@ -47,23 +75,8 @@ func newVotorQUICCertificate(identity ed25519.PrivateKey) (tls.Certificate, erro pub = priv.Public().(ed25519.PublicKey) } - template := &x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - CommonName: "Mithril Alpenglow observer", - }, - NotBefore: time.Unix(0, 0), - NotAfter: time.Date(4096, 1, 1, 0, 0, 0, 0, time.UTC), - KeyUsage: x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - DNSNames: []string{"localhost"}, - BasicConstraintsValid: true, - } - - certDER, err := x509.CreateCertificate(rand.Reader, template, template, pub, priv) - if err != nil { - return tls.Certificate{}, err - } + certDER := append([]byte(nil), votorCertificateTemplate[:]...) + copy(certDER[100:100+ed25519.PublicKeySize], pub) cert, err := x509.ParseCertificate(certDER) if err != nil { return tls.Certificate{}, err diff --git a/pkg/alpenglow/tls_identity_test.go b/pkg/alpenglow/tls_identity_test.go index 7efaa412..cb591e0f 100644 --- a/pkg/alpenglow/tls_identity_test.go +++ b/pkg/alpenglow/tls_identity_test.go @@ -1,15 +1,61 @@ package alpenglow import ( + "context" "crypto/ed25519" "crypto/tls" "crypto/x509" + "net" + "os" "testing" "time" "github.com/stretchr/testify/require" ) +func TestVotorCertificateMatchesAgaveAndFiredancerTemplate(t *testing.T) { + // Independent fixture from Agave's constructor, also accepted by + // Firedancer's fd_x509_mock_pubkey_v2 byte-pattern parser. + fixture, err := os.ReadFile("testdata/agave_votor_certificate.der") + require.NoError(t, err) + require.Len(t, fixture, 249) + for _, seed := range []byte{61, 62} { + identity := ed25519.NewKeyFromSeed(bytesOf(seed, ed25519.SeedSize)) + certificate, err := newVotorQUICCertificate(identity) + require.NoError(t, err) + expected := append([]byte(nil), fixture...) + copy(expected[100:132], identity.Public().(ed25519.PublicKey)) + require.Equal(t, expected, certificate.Certificate[0]) + require.Equal(t, identity.Public(), certificate.Leaf.PublicKey) + // Do not replace the dummy signature with a real one: Firedancer's + // parser matches it too. Authentication happens in CertificateVerify. + require.Error(t, certificate.Leaf.CheckSignature(certificate.Leaf.SignatureAlgorithm, + certificate.Leaf.RawTBSCertificate, certificate.Leaf.Signature)) + } +} + +func TestVotorCertificateStillRequiresIdentityKeyPossession(t *testing.T) { + identity := ed25519.NewKeyFromSeed(bytesOf(63, ed25519.SeedSize)) + certificate, err := newVotorQUICCertificate(identity) + require.NoError(t, err) + certificate.PrivateKey = ed25519.NewKeyFromSeed(bytesOf(64, ed25519.SeedSize)) + serverConn, clientConn := net.Pipe() + t.Cleanup(func() { _ = serverConn.Close(); _ = clientConn.Close() }) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + server := tls.Server(serverConn, &tls.Config{ + Certificates: []tls.Certificate{certificate}, MinVersion: tls.VersionTLS13, + }) + client := tls.Client(clientConn, &tls.Config{ + InsecureSkipVerify: true, MinVersion: tls.VersionTLS13, + }) + serverDone := make(chan error, 1) + go func() { serverDone <- server.HandshakeContext(ctx) }() + // Skipping the dummy X.509 signature does not bypass CertificateVerify. + require.ErrorContains(t, client.HandshakeContext(ctx), "invalid signature") + require.Error(t, <-serverDone) +} + func TestVotorPeerIdentityRequiresOneEd25519Certificate(t *testing.T) { identity := ed25519.NewKeyFromSeed(bytesOf(61, ed25519.SeedSize)) certificate, err := newVotorQUICCertificate(identity) From fd12b5630e8b947efe4b426e95de01816e3a4048 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 23:12:41 -0500 Subject: [PATCH 13/22] Add opt-in durable signing reservations and restart voting bounds --- cmd/mithril/node/node.go | 38 +- cmd/mithril/node/vote_startup.go | 40 +++ cmd/mithril/node/vote_startup_test.go | 73 ++++ config.example.toml | 10 + docs/reserved-vote-history.md | 66 ++++ pkg/alpenglow/vote_history.go | 40 ++- pkg/alpenglow/vote_reservation.go | 99 ++++++ pkg/alpenglow/vote_reservation_test.go | 71 ++++ pkg/blockprod/leader.go | 6 + .../leader_signing_reservation_test.go | 17 + pkg/config/config.go | 2 + pkg/consensus/engine.go | 2 +- pkg/consensus/vote_reservation.go | 269 ++++++++++++++ pkg/consensus/vote_reservation_test.go | 327 ++++++++++++++++++ pkg/consensus/voter.go | 217 +++++++++--- pkg/consensus/voter_wait_slot_test.go | 91 +++++ 16 files changed, 1290 insertions(+), 78 deletions(-) create mode 100644 cmd/mithril/node/vote_startup.go create mode 100644 cmd/mithril/node/vote_startup_test.go create mode 100644 docs/reserved-vote-history.md create mode 100644 pkg/alpenglow/vote_reservation.go create mode 100644 pkg/alpenglow/vote_reservation_test.go create mode 100644 pkg/blockprod/leader_signing_reservation_test.go create mode 100644 pkg/consensus/vote_reservation.go create mode 100644 pkg/consensus/vote_reservation_test.go create mode 100644 pkg/consensus/voter_wait_slot_test.go diff --git a/cmd/mithril/node/node.go b/cmd/mithril/node/node.go index a7c95f9e..95afb631 100644 --- a/cmd/mithril/node/node.go +++ b/cmd/mithril/node/node.go @@ -103,6 +103,9 @@ var ( validatorTPUQUICBind string validatorAdvertisedIP string validatorSigverifyWorkers int + validatorWaitToVoteSlot uint64 + validatorReservedHistory bool + validatorInitializeReservation bool validatorCompletionReserveMs int validatorMaxBufferedTransactions int @@ -549,6 +552,9 @@ func init() { Run.Flags().StringVar(&validatorTPUQUICBind, "tpu-quic-bind-addr", "", "Validator TPU QUIC listen address (default 0.0.0.0:8004)") Run.Flags().StringVar(&validatorAdvertisedIP, "validator-advertised-ip", "", "Public IP advertised for validator TPU QUIC") Run.Flags().IntVar(&validatorSigverifyWorkers, "tpu-sigverify-workers", 0, "TPU signature verification workers (0 = GOMAXPROCS)") + Run.Flags().BoolVar(&validatorReservedHistory, "reserved-vote-history", false, "Use durable signing reservations with unsynchronized per-vote history writes") + Run.Flags().BoolVar(&validatorInitializeReservation, "initialize-vote-reservation", false, "Enroll complete synchronous vote history in reserved mode (one-time migration)") + Run.Flags().Uint64Var(&validatorWaitToVoteSlot, "wait-to-vote-slot", 0, "Do not cast new votes below this slot; the automatic startup cutoff still applies (0 = automatic only)") Run.Flags().IntVar(&validatorCompletionReserveMs, "leader-completion-reserve-ms", 0, "Time reserved for leader finalization and broadcast (0 = 75ms default; tune from measured completion times)") Run.Flags().IntVar(&validatorMaxBufferedTransactions, "tpu-max-buffered-transactions", 0, "Maximum queued TPU transactions (0 = 131072 default)") @@ -649,6 +655,11 @@ func initConfigAndBindFlags(cmd *cobra.Command) error { if err := config.InitConfig(); err != nil { return err } + if slot, err := configuredWaitToVoteSlot(cmd); err != nil { + return err + } else { + validatorWaitToVoteSlot = slot + } // Check if a CLI flag was explicitly set by the user flagChanged := func(name string) bool { @@ -2667,23 +2678,21 @@ postBootstrap: } global.SeedWallClockSlot(wallClockSeed) startupWallSlot := global.WallClockSlot() - waitToVoteSlot := startupWallSlot - startupWallSlot%alpenglow.LeaderWindowSlots - if waitToVoteSlot <= math.MaxUint64-2*alpenglow.LeaderWindowSlots { - waitToVoteSlot += 2 * alpenglow.LeaderWindowSlots - } else { - waitToVoteSlot = math.MaxUint64 - } - mlog.Log.Infof("ALPENGLOW voting startup watermark: wall_clock=%d wait_to_vote=%d", startupWallSlot, waitToVoteSlot) + waitToVoteSlot := effectiveWaitToVoteSlot(startupWallSlot, validatorWaitToVoteSlot) + mlog.Log.Infof("ALPENGLOW voting startup watermark: wall_clock=%d configured_wait_to_vote=%d wait_to_vote=%d", startupWallSlot, validatorWaitToVoteSlot, waitToVoteSlot) identityPubkey := solana.PrivateKey(validatorIdentity).PublicKey() if err := consensusEngine.EnableVoting(consensusengine.VotingConfig{ - Identity: validatorIdentity, - AuthorizedVoter: validatorAuthorizedVoter, - VoteAccount: validatorVoteAccount, - HistoryDir: blockstorePath, - EpochForSlot: epochSchedule.GetEpoch, - SlotDuration: blockprod.AlpenglowSlotDuration, - WaitToVoteSlot: waitToVoteSlot, + Identity: validatorIdentity, + AuthorizedVoter: validatorAuthorizedVoter, + VoteAccount: validatorVoteAccount, + HistoryDir: blockstorePath, + ReservedHistory: validatorReservedHistory, + InitializeVoteReservation: validatorInitializeReservation, + Genesis: solana.MustHashFromBase58(networkGenesisHash), + EpochForSlot: epochSchedule.GetEpoch, + SlotDuration: blockprod.AlpenglowSlotDuration, + WaitToVoteSlot: waitToVoteSlot, ReadyToVote: func(slot uint64) bool { wallSlot := global.WallClockSlot() if liveSlot, ok := consensusEngine.AlpenglowLiveSlot(); ok { @@ -2824,6 +2833,7 @@ postBootstrap: } }, ProductionParent: consensusEngine.AlpenglowBlockProductionParent, + CanSignSlot: consensusEngine.AlpenglowCanSignLeaderSlot, CurrentSlot: func() uint64 { if slot, ok := consensusEngine.AlpenglowLiveSlot(); ok { return slot diff --git a/cmd/mithril/node/vote_startup.go b/cmd/mithril/node/vote_startup.go new file mode 100644 index 00000000..547e5519 --- /dev/null +++ b/cmd/mithril/node/vote_startup.go @@ -0,0 +1,40 @@ +package node + +import ( + "fmt" + "math" + "strconv" + + "github.com/Overclock-Validator/mithril/pkg/alpenglow" + "github.com/Overclock-Validator/mithril/pkg/config" + "github.com/spf13/cobra" +) + +func configuredWaitToVoteSlot(cmd *cobra.Command) (uint64, error) { + if flag := cmd.Flags().Lookup("wait-to-vote-slot"); flag != nil && flag.Changed { + return cmd.Flags().GetUint64("wait-to-vote-slot") + } + const key = "validator.wait_to_vote_slot" + if !config.IsSet(key) { + return 0, nil + } + // Unlike GetUint64, parsing explicitly must not turn an invalid operator + // cutoff into zero and silently remove the requested voting restriction. + slot, err := strconv.ParseUint(config.GetString(key), 10, 64) + if err != nil { + return 0, fmt.Errorf("%s must be an unsigned 64-bit slot: %w", key, err) + } + return slot, nil +} + +// The operator cutoff can postpone voting but cannot weaken the existing +// startup guard. Equality permits voting, subject to all other Votor checks. +func effectiveWaitToVoteSlot(startupWallSlot, configured uint64) uint64 { + automatic := startupWallSlot - startupWallSlot%alpenglow.LeaderWindowSlots + if automatic <= math.MaxUint64-2*alpenglow.LeaderWindowSlots { + automatic += 2 * alpenglow.LeaderWindowSlots + } else { + automatic = math.MaxUint64 + } + return max(automatic, configured) +} diff --git a/cmd/mithril/node/vote_startup_test.go b/cmd/mithril/node/vote_startup_test.go new file mode 100644 index 00000000..d86a0252 --- /dev/null +++ b/cmd/mithril/node/vote_startup_test.go @@ -0,0 +1,73 @@ +package node + +import ( + "math" + "strings" + "testing" + + "github.com/Overclock-Validator/mithril/pkg/config" + "github.com/spf13/cobra" + "github.com/spf13/viper" + "github.com/stretchr/testify/require" +) + +func TestConfiguredWaitToVoteSlot(t *testing.T) { + for _, tc := range []struct { + name, toml, cli string + want uint64 + invalid bool + }{ + {name: "default"}, + {name: "toml", toml: "wait_to_vote_slot = 1234", want: 1234}, + {name: "cli wins", toml: "wait_to_vote_slot = 1234", cli: "5678", want: 5678}, + {name: "explicit zero wins", toml: "wait_to_vote_slot = 1234", cli: "0"}, + {name: "maximum CLI", cli: "18446744073709551615", want: math.MaxUint64}, + {name: "negative TOML", toml: "wait_to_vote_slot = -1", invalid: true}, + {name: "fractional TOML", toml: "wait_to_vote_slot = 1.5", invalid: true}, + {name: "malformed TOML value", toml: `wait_to_vote_slot = "oops"`, invalid: true}, + {name: "empty TOML value", toml: `wait_to_vote_slot = ""`, invalid: true}, + {name: "overflow TOML value", toml: `wait_to_vote_slot = "18446744073709551616"`, invalid: true}, + {name: "negative CLI", cli: "-1", invalid: true}, + {name: "overflow CLI", cli: "18446744073709551616", invalid: true}, + } { + t.Run(tc.name, func(t *testing.T) { + viper.Reset() + t.Cleanup(viper.Reset) + config.ApplyDefaults(viper.GetViper()) + viper.SetConfigType("toml") + require.NoError(t, viper.ReadConfig(strings.NewReader("[validator]\n"+tc.toml))) + cmd := &cobra.Command{} + cmd.Flags().Uint64("wait-to-vote-slot", 0, "") + var err error + if tc.cli != "" { + err = cmd.Flags().Set("wait-to-vote-slot", tc.cli) + } + var got uint64 + if err == nil { + got, err = configuredWaitToVoteSlot(cmd) + } + if tc.invalid { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, tc.want, got) + }) + } + require.NotNil(t, Run.Flags().Lookup("wait-to-vote-slot")) +} + +func TestEffectiveWaitToVoteSlot(t *testing.T) { + for _, tc := range []struct{ startup, configured, want uint64 }{ + {100, 0, 108}, + {103, 0, 108}, + {103, 104, 108}, + {103, 108, 108}, + {103, 123, 123}, // Operator cutoff need not align with a leader window. + {103, math.MaxUint64, math.MaxUint64}, + {math.MaxUint64 - 7, 0, math.MaxUint64}, + {math.MaxUint64, 0, math.MaxUint64}, + } { + require.Equal(t, tc.want, effectiveWaitToVoteSlot(tc.startup, tc.configured), "%+v", tc) + } +} diff --git a/config.example.toml b/config.example.toml index 2cf1affb..d214103a 100644 --- a/config.example.toml +++ b/config.example.toml @@ -328,6 +328,16 @@ name = "mithril" # Signature-verification workers (0 = GOMAXPROCS). tpu_sigverify_workers = 0 + # Optional minimum slot for NEW votes (inclusive), also --wait-to-vote-slot. + # Useful when rejoining after recovery. CLI overrides this setting. + # Zero adds no operator cutoff; the automatic startup cutoff and normal + # consensus checks still apply. A lower value cannot bypass those checks. + # Replay/repair continue while waiting. Previously recorded, authenticated + # votes can still be restored/rebroadcast under the existing recovery rules. + # This does not coordinate a cluster restart or wait for supermajority, + # and does not allow resetting a corrupt vote-history file. + wait_to_vote_slot = 0 + # Bounded cross-slot TPU queue. Zero keeps the 131,072-transaction default. # Larger queues can prefill four busy leader slots, using additional memory. tpu_max_buffered_transactions = 0 diff --git a/docs/reserved-vote-history.md b/docs/reserved-vote-history.md new file mode 100644 index 00000000..92c5e3b7 --- /dev/null +++ b/docs/reserved-vote-history.md @@ -0,0 +1,66 @@ +# Reserved vote history (experimental, opt-in) + +Use `--reserved-vote-history` to keep detailed per-vote history with atomic +write-and-rename, without explicitly syncing each vote. An independent signed +reservation is synced before its slot range can be used. The default remains +synchronous history. `--wait-to-vote-slot N` is an additional inclusive minimum; +it cannot override recovery, finality, execution or ParentReady checks. + +For the first enrollment, stop the previous validator normally and start with +`--reserved-vote-history --initialize-vote-reservation`. Enrollment requires the +complete history from the synchronous writer and exclusive ownership of the +identity. Remove the initialization flag on subsequent starts. It cannot reset +missing reservations for history already marked as enrolled. Initialization on +an empty directory is only appropriate for a previously unused identity. + +The first durable baseline is established before any unsynchronized writes. +History format version 2 deliberately prevents older binaries from opening it. +Disabling the flag, deleting safety files, or rolling back to a binary that does +not understand reservations is not a supported downgrade procedure. Preserve +both `vote_history-.mithril.json` and +`vote_reservation-.mithril.json` in the ledger directory, independently +of AccountsDB snapshot restoration. Do not copy older safety files back during +an application rollback. Keys, vote account, genesis and shred-version changes +fail closed and require an explicit domain migration, not automatic re-enrollment. + +The reservation worker grants up to 32 slots beyond the requested slot and +renews when 16 or fewer remain. At the nominal 200 ms cadence those correspond +to about 6.4 seconds of reserve and 3.2 seconds of renewal slack. They are slot +counts, not time guarantees. Sync runs on the worker; only its successful +acknowledgement publishes new permission. Exhaustion pauses signing while +replay/verification continue. Failed writes retain the old permission and retry; +uncertain writes prevent a clean shutdown marker until a later successful +monotonic write resolves the uncertainty. Vote events waiting on renewal are +retained in a bounded queue and re-evaluated against current consensus state. + +On an unclean restart, the bound loaded at startup is fixed: all vote types and +historical local-vote restoration are disabled in that uncertain range. Voting +above it requires verified finality/checkpoint state to have reached it, plus +normal live joining and protocol checks. RPC wall-clock estimates cannot release +this recovery gate. Repeated crashes without new permission do not advance it. +The conservative finality wait can be indefinite on a halted cluster. + +Clean shutdown first stops the voter, joins the reservation worker, syncs exact +history (including the verified finality floor), then syncs a marker containing +its digest. Startup checks the digest and durably consumes the marker before +any new signature or history mutation. A crash in that interval therefore falls +back to the reservation. A shutdown before uncertain recovery completes cannot +create a clean marker. Clean vote-history recovery permits normal new voting, +but leader production always skips the previous reservation because vote +history is not a complete record of blocks the leader may have signed. +Every leader slot is checked before building, including trailing window slots. + +The directory lock prevents two instances of this implementation owning one +history directory. It does not fence copies on other machines/directories. +The storage contract assumes successful file and directory sync are honored; +media loss, intentional rollback or compromised signing keys require operator +recovery. Format signatures establish integrity, not freshness against rollback. + +Validation includes race tests for incomplete history, repeated crashes, clean +marker consumption/mismatch, missing/corrupt records, incompatible domains, +unacknowledged and uncertain writes, H/H+1 for all five vote types and restoration, +renewal retry with finality advancement, window-spanning skips, and leader gates. +These are deterministic software tests, not a host power-loss qualification or +formal proof of the full consensus protocol. `BenchmarkVoteHistoryPersistence` +compares the actual serialization/write paths with 32 recorded notarizations; +it does not measure block replay or full validator FAST participation. diff --git a/pkg/alpenglow/vote_history.go b/pkg/alpenglow/vote_history.go index d227bbe1..5ff7e1fa 100644 --- a/pkg/alpenglow/vote_history.go +++ b/pkg/alpenglow/vote_history.go @@ -15,6 +15,7 @@ import ( ) const voteHistoryVersion = 1 +const reservedVoteHistoryVersion = 2 var ErrVoteHistoryNotFound = errors.New("alpenglow vote history not found") @@ -23,6 +24,7 @@ var ErrVoteHistoryNotFound = errors.New("alpenglow vote history not found") // Agave when resuming (notarized blocks and ParentReady edges), in addition to // the anti-equivocation vote sets. type VoteHistory struct { + ReservationRequired bool `json:"reservation_required,omitempty"` Version uint32 `json:"version"` NodePubkey solana.PublicKey `json:"node_pubkey"` Root uint64 `json:"root"` @@ -424,6 +426,19 @@ func VoteHistoryFilename(dir string, node solana.PublicKey) string { // identity and atomically replaces the previous file before a vote can be // admitted to consensus or sent to the network. func SaveVoteHistory(dir string, h *VoteHistory, identity ed25519.PrivateKey) error { + return saveVoteHistory(dir, h, identity, true) +} + +// SaveReservedVoteHistory writes and renames without per-vote sync. The caller +// must enforce an independently durable signing reservation before using it. +func SaveReservedVoteHistory(dir string, h *VoteHistory, identity ed25519.PrivateKey) error { + if h == nil || !h.ReservationRequired { + return fmt.Errorf("reserved history requires durable reservation enrollment") + } + return saveVoteHistory(dir, h, identity, false) +} + +func saveVoteHistory(dir string, h *VoteHistory, identity ed25519.PrivateKey, durable bool) error { if h == nil { return fmt.Errorf("save vote history: nil history") } @@ -435,6 +450,9 @@ func SaveVoteHistory(dir string, h *VoteHistory, identity ed25519.PrivateKey) er return fmt.Errorf("save vote history: identity %s does not match history %s", node, h.NodePubkey) } h.Version = voteHistoryVersion + if h.ReservationRequired { + h.Version = reservedVoteHistoryVersion + } if err := h.preparePersistedViews(); err != nil { return fmt.Errorf("save vote history: %w", err) } @@ -450,7 +468,7 @@ func SaveVoteHistory(dir string, h *VoteHistory, identity ed25519.PrivateKey) er return fmt.Errorf("serialize vote history: %w", err) } envelope := savedVoteHistory{ - Version: voteHistoryVersion, + Version: h.Version, Node: node, Data: data, Signature: ed25519.Sign(identity, data), @@ -463,7 +481,7 @@ func SaveVoteHistory(dir string, h *VoteHistory, identity ed25519.PrivateKey) er return err } filename := VoteHistoryFilename(dir, node) - return persistVoteHistoryFile(dir, filename, encoded) + return replaceVoteHistoryFile(dir, filename, encoded, durable) } // ensureDurableVoteHistoryDirectory creates each missing path component and @@ -515,6 +533,10 @@ func ensureDurableVoteHistoryDirectory(dir string) error { // alone does not guarantee that either the bytes or the new directory entry // survives a crash. func persistVoteHistoryFile(dir, filename string, encoded []byte) error { + return replaceVoteHistoryFile(dir, filename, encoded, true) +} + +func replaceVoteHistoryFile(dir, filename string, encoded []byte, durable bool) error { temporary, err := os.CreateTemp(dir, "."+filepath.Base(filename)+".tmp-") if err != nil { return fmt.Errorf("create temporary vote history: %w", err) @@ -538,8 +560,10 @@ func persistVoteHistoryFile(dir, filename string, encoded []byte) error { if n != len(encoded) { return fmt.Errorf("write temporary vote history: %w", io.ErrShortWrite) } - if err := temporary.Sync(); err != nil { - return fmt.Errorf("sync temporary vote history: %w", err) + if durable { + if err := temporary.Sync(); err != nil { + return fmt.Errorf("sync temporary vote history: %w", err) + } } closeErr := temporary.Close() closed = true @@ -551,8 +575,8 @@ func persistVoteHistoryFile(dir, filename string, encoded []byte) error { } renamed = true - if err := syncVoteHistoryDirectory(dir); err != nil { - return err + if durable { + return syncVoteHistoryDirectory(dir) } return nil } @@ -586,7 +610,7 @@ func LoadVoteHistory(dir string, node solana.PublicKey) (*VoteHistory, error) { if err := json.Unmarshal(encoded, &envelope); err != nil { return nil, fmt.Errorf("decode saved vote history: %w", err) } - if envelope.Version != voteHistoryVersion || envelope.Node != node { + if (envelope.Version != voteHistoryVersion && envelope.Version != reservedVoteHistoryVersion) || envelope.Node != node { return nil, fmt.Errorf("saved vote history identity/version mismatch") } if !ed25519.Verify(ed25519.PublicKey(node[:]), envelope.Data, envelope.Signature) { @@ -596,7 +620,7 @@ func LoadVoteHistory(dir string, node solana.PublicKey) (*VoteHistory, error) { if err := json.Unmarshal(envelope.Data, &h); err != nil { return nil, fmt.Errorf("decode vote history: %w", err) } - if h.Version != voteHistoryVersion || h.NodePubkey != node { + if h.Version != envelope.Version || h.NodePubkey != node || h.ReservationRequired != (h.Version == reservedVoteHistoryVersion) { return nil, fmt.Errorf("vote history identity/version mismatch") } if err := h.validatePersistedState(); err != nil { diff --git a/pkg/alpenglow/vote_reservation.go b/pkg/alpenglow/vote_reservation.go new file mode 100644 index 00000000..e8aa0342 --- /dev/null +++ b/pkg/alpenglow/vote_reservation.go @@ -0,0 +1,99 @@ +package alpenglow + +import ( + "crypto/ed25519" + "crypto/sha256" + "encoding/json" + "errors" + "fmt" + "os" + "path/filepath" + + "github.com/gagliardetto/solana-go" + "golang.org/x/sys/unix" +) + +// VoteReservation is independent of AccountsDB checkpoints. Never restore an +// older copy of this record when restoring a snapshot. A clean digest is valid +// only until a durably written dirty successor consumes it before signing. +type VoteReservation struct { + Version uint32 `json:"version"` + Node solana.PublicKey `json:"node"` + VoteAccount solana.PublicKey `json:"vote_account"` + AuthorizedVoter solana.PublicKey `json:"authorized_voter"` + Genesis solana.Hash `json:"genesis"` + ShredVersion uint16 `json:"shred_version"` + Generation uint64 `json:"generation"` + Through uint64 `json:"through"` + CleanHistoryDigest []byte `json:"clean_history_digest,omitempty"` +} + +func VoteReservationFilename(dir string, node solana.PublicKey) string { + return filepath.Join(dir, fmt.Sprintf("vote_reservation-%s.mithril.json", node)) +} + +// LockVoteHistory excludes concurrent owners of this directory. Operators must +// still fence copies of the same identity on other hosts or in other paths. +func LockVoteHistory(dir string, node solana.PublicKey) (*os.File, error) { + if err := ensureDurableVoteHistoryDirectory(dir); err != nil { + return nil, err + } + f, err := os.OpenFile(filepath.Join(dir, ".vote_history-"+node.String()+".lock"), os.O_CREATE|os.O_RDWR, 0600) + if err != nil { + return nil, err + } + if err := unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB); err != nil { + f.Close() + return nil, fmt.Errorf("vote history already owned: %w", err) + } + return f, nil +} + +func LoadVoteReservation(dir string, node solana.PublicKey) (VoteReservation, error) { + var r VoteReservation + encoded, err := os.ReadFile(VoteReservationFilename(dir, node)) + if err != nil { + return r, err + } + var envelope savedVoteHistory + if err := json.Unmarshal(encoded, &envelope); err != nil { + return r, err + } + if envelope.Version != 1 || envelope.Node != node || !ed25519.Verify(ed25519.PublicKey(node[:]), envelope.Data, envelope.Signature) { + return r, errors.New("invalid vote reservation signature/version/identity") + } + if err := json.Unmarshal(envelope.Data, &r); err != nil { + return r, err + } + if r.Version != 1 || r.Node != node || r.Generation == 0 || (len(r.CleanHistoryDigest) != 0 && len(r.CleanHistoryDigest) != sha256.Size) { + return r, errors.New("invalid vote reservation record") + } + return r, nil +} + +func SaveVoteReservation(dir string, r VoteReservation, identity ed25519.PrivateKey) error { + if len(identity) != ed25519.PrivateKeySize || solana.PublicKey(identity.Public().(ed25519.PublicKey)) != r.Node || r.Version != 1 || r.Generation == 0 { + return errors.New("invalid vote reservation signer/record") + } + data, err := json.Marshal(r) + if err != nil { + return err + } + encoded, err := json.Marshal(savedVoteHistory{Version: 1, Node: r.Node, Data: data, Signature: ed25519.Sign(identity, data)}) + if err != nil { + return err + } + if err := ensureDurableVoteHistoryDirectory(dir); err != nil { + return err + } + return persistVoteHistoryFile(dir, VoteReservationFilename(dir, r.Node), encoded) +} + +func VoteHistoryDigest(dir string, node solana.PublicKey) ([]byte, error) { + data, err := os.ReadFile(VoteHistoryFilename(dir, node)) + if err != nil { + return nil, err + } + digest := sha256.Sum256(data) + return digest[:], nil +} diff --git a/pkg/alpenglow/vote_reservation_test.go b/pkg/alpenglow/vote_reservation_test.go new file mode 100644 index 00000000..9e1840ad --- /dev/null +++ b/pkg/alpenglow/vote_reservation_test.go @@ -0,0 +1,71 @@ +package alpenglow + +import ( + "crypto/ed25519" + "encoding/json" + "os" + "testing" + + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func TestReservedHistoryFormatAndIntegrity(t *testing.T) { + key := ed25519.NewKeyFromSeed(make([]byte, 32)) + node := solana.PublicKey(key.Public().(ed25519.PublicKey)) + dir := t.TempDir() + h := NewVoteHistory(node, 39) + require.Error(t, SaveReservedVoteHistory(dir, h, key)) + h.ReservationRequired = true + require.NoError(t, h.AddVote(NewSkipVote(44))) + require.NoError(t, SaveReservedVoteHistory(dir, h, key)) + raw, err := os.ReadFile(VoteHistoryFilename(dir, node)) + require.NoError(t, err) + var envelope savedVoteHistory + require.NoError(t, json.Unmarshal(raw, &envelope)) + require.Equal(t, uint32(2), envelope.Version, "legacy reader must reject hybrid history") + loaded, err := LoadVoteHistory(dir, node) + require.NoError(t, err) + require.True(t, loaded.HasSkipped(44)) + require.True(t, loaded.ReservationRequired) + envelope.Data[10] ^= 1 + raw, err = json.Marshal(envelope) + require.NoError(t, err) + require.NoError(t, os.WriteFile(VoteHistoryFilename(dir, node), raw, 0600)) + _, err = LoadVoteHistory(dir, node) + require.Error(t, err) +} + +func BenchmarkVoteHistoryPersistence(b *testing.B) { + key := ed25519.NewKeyFromSeed(make([]byte, 32)) + node := solana.PublicKey(key.Public().(ed25519.PublicKey)) + for _, reserved := range []bool{false, true} { + name := "synchronous" + if reserved { + name = "reserved-write-rename" + } + b.Run(name, func(b *testing.B) { + dir := b.TempDir() + h := NewVoteHistory(node, 39) + h.ReservationRequired = reserved + for slot := uint64(40); slot < 72; slot++ { + if err := h.AddVote(NewNotarizationVote(slot, solana.Hash{byte(slot)})); err != nil { + b.Fatal(err) + } + } + save := SaveVoteHistory + if reserved { + save = SaveReservedVoteHistory + } + if err := SaveVoteHistory(dir, h, key); err != nil { + b.Fatal(err) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err := save(dir, h, key); err != nil { + b.Fatal(err) + } + } + }) + } +} diff --git a/pkg/blockprod/leader.go b/pkg/blockprod/leader.go index 66bce23e..7dc15096 100644 --- a/pkg/blockprod/leader.go +++ b/pkg/blockprod/leader.go @@ -97,6 +97,7 @@ type LeaderLoop struct { alpenglowClock bool parentContext func(uint64) ParentContext productionParent func(uint64) alpenglow.BlockProductionParent + canSignSlot func(uint64) bool onBlock func(*b.Block) commitLeaderSlot func(replay.CommitLeaderInput) (*sealevel.SlotCtx, error) @@ -146,6 +147,7 @@ type LeaderLoopConfig struct { AlpenglowClock bool ParentContext func(uint64) ParentContext ProductionParent func(slot uint64) alpenglow.BlockProductionParent + CanSignSlot func(slot uint64) bool OnBlock func(*b.Block) CurrentSlot func() uint64 LeaderForSlot func(uint64) (solana.PublicKey, bool) @@ -188,6 +190,7 @@ func NewLeaderLoop(cfg LeaderLoopConfig) *LeaderLoop { alpenglowClock: cfg.AlpenglowClock, parentContext: cfg.ParentContext, productionParent: cfg.ProductionParent, + canSignSlot: cfg.CanSignSlot, onBlock: cfg.OnBlock, currentSlot: cfg.CurrentSlot, leaderForSlot: cfg.LeaderForSlot, @@ -1114,6 +1117,9 @@ func (l *LeaderLoop) revalidateProductionParentForStartLocked(slot uint64, selec } func (l *LeaderLoop) startSlotLocked(slot uint64) error { + if l.canSignSlot != nil && !l.canSignSlot(slot) { + return fmt.Errorf("%w: waiting for durable signing reservation", errParentNotReady) + } selectedParent, parentReadyRequired, err := l.resolveProductionParent(slot) if err != nil { return err diff --git a/pkg/blockprod/leader_signing_reservation_test.go b/pkg/blockprod/leader_signing_reservation_test.go new file mode 100644 index 00000000..4b7e09c1 --- /dev/null +++ b/pkg/blockprod/leader_signing_reservation_test.go @@ -0,0 +1,17 @@ +package blockprod + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +func TestSigningReservationGatesEveryLeaderSlotBeforeBuild(t *testing.T) { + for slot := uint64(40); slot < 44; slot++ { + var checked uint64 + l := &LeaderLoop{canSignSlot: func(s uint64) bool { checked = s; return false }} + require.ErrorIs(t, l.startSlotLocked(slot), errParentNotReady) + require.Equal(t, slot, checked) + // All other builder dependencies are deliberately nil: rejection must happen + // before accessing a working bank, executing or signing any shreds. + } +} diff --git a/pkg/config/config.go b/pkg/config/config.go index ef7ec2af..cf461613 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -28,6 +28,7 @@ func ApplyDefaults(v *viper.Viper) { v.SetDefault("validator.tpu_quic_bind_addr", "") v.SetDefault("validator.advertised_ip", "") v.SetDefault("validator.tpu_sigverify_workers", 0) + v.SetDefault("validator.wait_to_vote_slot", uint64(0)) } // LedgerConfig holds ledger-related configuration (matches Firedancer [ledger] section) @@ -238,6 +239,7 @@ type ValidatorConfig struct { TPUQUICBindAddr string `toml:"tpu_quic_bind_addr" mapstructure:"tpu_quic_bind_addr"` AdvertisedIP string `toml:"advertised_ip" mapstructure:"advertised_ip"` TPUSigverifyWorkers int `toml:"tpu_sigverify_workers" mapstructure:"tpu_sigverify_workers"` + WaitToVoteSlot uint64 `toml:"wait_to_vote_slot" mapstructure:"wait_to_vote_slot"` // Minimum slot for new votes; automatic startup cutoff still applies BlockCompletionReserveMs int `toml:"block_completion_reserve_ms" mapstructure:"block_completion_reserve_ms"` TPUMaxBufferedTransactions int `toml:"tpu_max_buffered_transactions" mapstructure:"tpu_max_buffered_transactions"` } diff --git a/pkg/consensus/engine.go b/pkg/consensus/engine.go index 39723291..23003fd2 100644 --- a/pkg/consensus/engine.go +++ b/pkg/consensus/engine.go @@ -381,7 +381,7 @@ func (e *AlpenglowObserverEngine) EnableVoting(cfg VotingConfig) error { // events, matching Agave's initial_parent_ready selection. if slot, parent, ok := voter.history.HighestParentReadyMatching(func(parent alpenglow.BlockID) bool { return !e.ensureChain().IsObjectivelyInvalidBlock(parent) - }); ok && slot > root.Slot { + }); ok && slot > root.Slot && (voter.reservation == nil || voter.reservation.recoverThrough == 0) { if !e.ensurePool().RestoreParentReady(slot, parent) { mlog.Log.FileOnlyf("ALPENGLOW voting: ignored persisted ParentReady slot=%d parent=%s because newer root/live tracker state is authoritative", slot, parent) } diff --git a/pkg/consensus/vote_reservation.go b/pkg/consensus/vote_reservation.go new file mode 100644 index 00000000..74b26146 --- /dev/null +++ b/pkg/consensus/vote_reservation.go @@ -0,0 +1,269 @@ +package consensus + +import ( + "bytes" + "crypto/ed25519" + "errors" + "fmt" + "math" + "os" + "sync" + "sync/atomic" + "time" + + "github.com/Overclock-Validator/mithril/pkg/alpenglow" + "github.com/Overclock-Validator/mithril/pkg/mlog" + "github.com/gagliardetto/solana-go" +) + +const signingReserveSlots = uint64(32) +const signingRenewRemaining = uint64(16) + +// The worker owns record after startup. Only its acknowledged Through is +// published to signers. Requested slots never grant signing permission. +type signingReservation struct { + uncertain bool // Worker only, read after halt. Failed sync may have reached storage. + record alpenglow.VoteReservation + through atomic.Uint64 + desired atomic.Uint64 + stopped atomic.Bool + recoverThrough uint64 // Immutable bound read at startup, zero after verified clean shutdown. + leaderThrough uint64 // Exact leader production history is not saved: always skip the old range. + wake chan struct{} + changed chan struct{} + stop chan struct{} + done chan struct{} + stopOnce sync.Once + persist func(alpenglow.VoteReservation) error +} + +func openSigningReservation(cfg VotingConfig, node solana.PublicKey, shredVersion uint16, history *alpenglow.VoteHistory) (*signingReservation, error) { + if cfg.Genesis == (solana.Hash{}) { + return nil, errors.New("reserved voting requires the bound genesis hash") + } + expected := alpenglow.VoteReservation{Version: 1, Node: node, VoteAccount: cfg.VoteAccount, AuthorizedVoter: solana.PublicKey(cfg.AuthorizedVoter.Public().(ed25519.PublicKey)), Genesis: cfg.Genesis, ShredVersion: shredVersion} + record, err := alpenglow.LoadVoteReservation(cfg.HistoryDir, node) + initializing := errors.Is(err, os.ErrNotExist) + if initializing { + if !cfg.InitializeVoteReservation || history.ReservationRequired { + return nil, errors.New("missing vote reservation; explicit first enrollment with complete synchronous history is required") + } + record = expected + record.Generation = 1 + record.Through = history.Root + for slot := range history.VotesCast { + record.Through = max(record.Through, slot) + } + // The baseline is made durable before the first reservation is created. + if err := alpenglow.SaveVoteHistory(cfg.HistoryDir, history, cfg.Identity); err != nil { + return nil, err + } + } else if err != nil { + return nil, fmt.Errorf("refuse unsafe reservation reset: %w", err) + } + if record.Node != expected.Node || record.VoteAccount != expected.VoteAccount || record.AuthorizedVoter != expected.AuthorizedVoter || record.Genesis != expected.Genesis || record.ShredVersion != expected.ShredVersion { + return nil, errors.New("vote reservation cluster or signing identity mismatch; explicit domain migration is required") + } + if record.Through == math.MaxUint64 || record.Generation == math.MaxUint64 { + return nil, errors.New("vote reservation exhausted") + } + for slot := range history.VotesCast { + if slot > record.Through { + return nil, fmt.Errorf("history slot %d exceeds durable reservation %d", slot, record.Through) + } + } + r := &signingReservation{record: record, recoverThrough: record.Through, leaderThrough: record.Through, wake: make(chan struct{}, 1), changed: make(chan struct{}, 1), stop: make(chan struct{}), done: make(chan struct{})} + r.persist = func(next alpenglow.VoteReservation) error { + return alpenglow.SaveVoteReservation(cfg.HistoryDir, next, cfg.Identity) + } + if initializing { + r.recoverThrough = 0 + } else if len(record.CleanHistoryDigest) != 0 { + digest, err := alpenglow.VoteHistoryDigest(cfg.HistoryDir, node) + if err != nil { + return nil, err + } + if bytes.Equal(digest, record.CleanHistoryDigest) { + r.recoverThrough = 0 + } + } + // Consume the clean marker before any new signature or history mutation. + r.record.CleanHistoryDigest = nil + r.record.Generation++ + if err := r.persist(r.record); err != nil { + return nil, fmt.Errorf("consume vote reservation session: %w", err) + } + history.ReservationRequired = true + // Version 2 is deliberately rejected by older binaries that do not enforce H. + if err := alpenglow.SaveVoteHistory(cfg.HistoryDir, history, cfg.Identity); err != nil { + return nil, err + } + r.through.Store(r.record.Through) + mlog.Log.Infof("ALPENGLOW signing reservation: through=%d recovery_through=%d leader_recovery_through=%d", r.record.Through, r.recoverThrough, r.leaderThrough) + go r.run() + return r, nil +} + +// allow is nonblocking and can also be called by the leader loop. Finality +// comes from verified consensus/checkpoint state, never an RPC wall-clock tip. +func (r *signingReservation) allow(slot, finalized uint64, leader bool) bool { + if r.stopped.Load() { + return false + } + floor := r.recoverThrough + if leader { + floor = r.leaderThrough + } + if floor != 0 && (slot <= floor || finalized < floor) { + return false + } + r.request(slot) + return slot <= r.through.Load() +} + +func (r *signingReservation) request(slot uint64) { + for { + old := r.desired.Load() + if slot <= old || r.desired.CompareAndSwap(old, slot) { + break + } + } + through := r.through.Load() + if slot > through || through-slot <= signingRenewRemaining { + select { + case r.wake <- struct{}{}: + default: + } + } +} + +func (r *signingReservation) run() { + defer close(r.done) + retry := time.NewTicker(250 * time.Millisecond) + defer retry.Stop() + var warned bool + for { + select { + case <-r.stop: + return + case <-r.wake: + case <-retry.C: + } + if r.stopped.Load() { + return + } + slot := r.desired.Load() + if slot == 0 || (slot <= r.record.Through && r.record.Through-slot > signingRenewRemaining) { + continue + } + if slot > math.MaxUint64-signingReserveSlots || r.record.Generation == math.MaxUint64 { + continue + } // Never wrap or grant permission. + next := r.record + next.Through = max(next.Through, slot+signingReserveSlots) + next.Generation++ + next.CleanHistoryDigest = nil + if err := r.persist(next); err != nil { + r.uncertain = true + if !warned { + mlog.Log.Errorf("ALPENGLOW signing reservation renewal failed; permission remains through %d: %v", r.through.Load(), err) + warned = true + } + continue // Retry the same or a greater bound; an uncertain sync never grants permission. + } + warned = false + r.uncertain = false + r.record = next + r.through.Store(next.Through) + select { + case r.changed <- struct{}{}: + default: + } + } +} + +func (r *signingReservation) halt() { + r.stopOnce.Do(func() { r.stopped.Store(true); close(r.stop) }) + <-r.done +} + +// Called only after the voter loop and leader producer have stopped. A failure +// leaves dirty recovery in force; history must be synced before its digest. +func (r *signingReservation) seal(dir string, history *alpenglow.VoteHistory, identity ed25519.PrivateKey) error { + r.halt() + if r.uncertain { + return errors.New("uncertain reservation write; retaining unclean recovery") + } + if err := alpenglow.SaveVoteHistory(dir, history, identity); err != nil { + return err + } + digest, err := alpenglow.VoteHistoryDigest(dir, history.NodePubkey) + if err != nil { + return err + } + if r.record.Generation == math.MaxUint64 { + return errors.New("vote reservation generation exhausted") + } + next := r.record + next.Generation++ + next.CleanHistoryDigest = digest + return r.persist(next) +} + +// Retain only events blocked on renewal, not historical catch-up traffic. +// Replay the original event after acknowledgement so normal finality, parent, +// execution and invalidation checks still decide whether to vote. +func (v *alpenglowVoter) retainReservationEvent(event voterEvent) { + r := v.reservation + if r == nil { + return + } + var slot uint64 + switch event.kind { + case voterEventBlock: + slot = event.block.Block.Slot + case voterEventBlockTimeout, voterEventCrashedLeaderTimeout: + slot = event.slot | (alpenglow.LeaderWindowSlots - 1) + case voterEventConsensus: + switch event.consensus.Kind { + case alpenglow.ConsensusEventBlockNotarized, alpenglow.ConsensusEventParentReady, alpenglow.ConsensusEventSafeToNotar, alpenglow.ConsensusEventSafeToSkip: + slot = event.consensus.Slot + if event.consensus.Kind == alpenglow.ConsensusEventSafeToNotar || event.consensus.Kind == alpenglow.ConsensusEventSafeToSkip { + slot |= alpenglow.LeaderWindowSlots - 1 + } + default: + return + } + default: + return + } + if slot <= r.through.Load() || slot <= v.admissionFloor() || slot < v.waitToVoteSlot || v.engine.alpenglowVoteActionFloor() < r.recoverThrough || slot <= r.recoverThrough { + return + } + if !v.votingStarted && v.readyToVote != nil && !v.readyToVote(slot) { + return + } + r.request(slot) + if len(v.reservationEvents) < votorEventQueueSize { + v.reservationEvents = append(v.reservationEvents, event) + } +} + +// AlpenglowCanSignLeaderSlot protects every produced slot, including the +// trailing slots of a leader window. A clean vote-history marker is not a +// complete leader-block history, so leaders always skip the old reservation. +func (e *AlpenglowObserverEngine) AlpenglowCanSignLeaderSlot(slot uint64) bool { + if e.safetyError() != nil { + return false + } + e.voterMu.RLock() + defer e.voterMu.RUnlock() + v := e.voter + if v == nil { + return false + } + if v.reservation == nil { + return true + } + return v.reservation.allow(slot, e.alpenglowVoteActionFloor(), true) +} diff --git a/pkg/consensus/vote_reservation_test.go b/pkg/consensus/vote_reservation_test.go new file mode 100644 index 00000000..a89393d1 --- /dev/null +++ b/pkg/consensus/vote_reservation_test.go @@ -0,0 +1,327 @@ +package consensus + +import ( + "crypto/ed25519" + "errors" + "math" + "os" + "sync/atomic" + "testing" + "time" + + "github.com/Overclock-Validator/mithril/pkg/alpenglow" + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func reservedTestConfig(dir string) VotingConfig { + return VotingConfig{Identity: voterTestKey(11), AuthorizedVoter: voterTestKey(12), VoteAccount: solana.PublicKey(voterTestKey(13).Public().(ed25519.PublicKey)), HistoryDir: dir, Genesis: solana.Hash{1}, ReservedHistory: true, InitializeVoteReservation: true, WaitToVoteSlot: 40, ReadyToVote: func(uint64) bool { return true }, EpochForSlot: func(uint64) uint64 { return 7 }, Peers: func([]alpenglow.ValidatorStake) []alpenglow.VotorPeer { return nil }} +} + +func openReservedTestVoter(t *testing.T, cfg VotingConfig, root uint64) (*alpenglowVoter, error) { + t.Helper() + e, err := NewEngine(Config{AlpenglowIdentity: cfg.Identity, AlpenglowShredVersion: 0x1234}) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, e.Close()) }) + set := voterTestValidatorSet(t, cfg.Identity, cfg.AuthorizedVoter, cfg.VoteAccount) + e.SetAlpenglowEpochLookup(cfg.EpochForSlot) + require.NoError(t, e.SetAlpenglowValidatorSet(set)) + block := alpenglow.BlockID{Slot: root, Hash: solana.Hash{byte(root)}} + e.SetAlpenglowRoot(block) + v, err := newAlpenglowVoterUnstarted(e, cfg, block, []alpenglow.ValidatorSet{set}) + if err == nil { + t.Cleanup(func() { require.NoError(t, v.close()) }) + } + return v, err +} + +func reserveThrough(t *testing.T, r *signingReservation, slot uint64) { + t.Helper() + r.request(slot) + require.Eventually(t, func() bool { return r.through.Load() >= slot }, time.Second, time.Millisecond) +} + +// Simulate loss of this process without executing the clean shutdown protocol. +func crashReservedTestVoter(t *testing.T, v *alpenglowVoter) { + t.Helper() + v.shutdownOnce.Do(func() { + v.closeOnce.Do(func() { close(v.done) }) + v.wg.Wait() + v.reservation.halt() + require.NoError(t, v.broadcaster.Close()) + require.NoError(t, v.historyLock.Close()) + }) +} + +func TestReservedVotingLostHistorySuffixAndRepeatedCrash(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + reserveThrough(t, v.reservation, 60) + baseline, err := os.ReadFile(alpenglow.VoteHistoryFilename(cfg.HistoryDir, v.node)) + require.NoError(t, err) + voted, err := v.cast(alpenglow.NewNotarizationVote(60, solana.Hash{1}), false) + require.NoError(t, err) + require.True(t, voted) + oldH := v.reservation.through.Load() + crashReservedTestVoter(t, v) + // Reproduce a host crash retaining a valid older version of detailed history. + require.NoError(t, os.WriteFile(alpenglow.VoteHistoryFilename(cfg.HistoryDir, v.node), baseline, 0600)) + cfg.InitializeVoteReservation = false + resumed, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + require.Equal(t, oldH, resumed.reservation.recoverThrough) + for _, vote := range []alpenglow.Vote{alpenglow.NewNotarizationVote(60, solana.Hash{2}), alpenglow.NewSkipVote(60), alpenglow.NewFinalizationVote(60), alpenglow.NewNotarizationFallbackVote(60, solana.Hash{2}), alpenglow.NewSkipFallbackVote(60), alpenglow.NewSkipVote(oldH + 1)} { + // Check at the signing boundary, including the restoration bypass. + for _, normal := range []bool{false, true} { + _, _, err := resumed.sign(vote, normal) + require.ErrorIs(t, err, errVoterNotReady) + } + } + require.Equal(t, oldH, resumed.reservation.through.Load(), "recovery must not keep moving its target") + crashReservedTestVoter(t, resumed) + resumed, err = openReservedTestVoter(t, cfg, oldH) + require.NoError(t, err) + require.Equal(t, oldH, resumed.reservation.recoverThrough) + reserveThrough(t, resumed.reservation, oldH+1) + voted, err = resumed.cast(alpenglow.NewSkipVote(oldH+1), false) + require.NoError(t, err) + require.True(t, voted) + voted, err = resumed.cast(alpenglow.NewSkipVote(oldH), false) + require.NoError(t, err) + require.False(t, voted) +} + +func TestReservedVotingCleanMarkerConsumedBeforeSigning(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + reserveThrough(t, v.reservation, 44) + voted, err := v.cast(alpenglow.NewSkipVote(44), false) + require.NoError(t, err) + require.True(t, voted) + require.NoError(t, v.close()) + r, err := alpenglow.LoadVoteReservation(cfg.HistoryDir, v.node) + require.NoError(t, err) + require.NotEmpty(t, r.CleanHistoryDigest) + cfg.InitializeVoteReservation = false + resumed, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + require.Zero(t, resumed.reservation.recoverThrough) + require.True(t, resumed.history.HasSkipped(44)) + r, err = alpenglow.LoadVoteReservation(cfg.HistoryDir, v.node) + require.NoError(t, err) + require.Empty(t, r.CleanHistoryDigest) + voted, err = resumed.cast(alpenglow.NewSkipVote(45), false) + require.NoError(t, err) + require.True(t, voted) + require.False(t, resumed.reservation.allow(45, 39, true), "clean vote history does not authorize repeating leader blocks") + crashReservedTestVoter(t, resumed) + again, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + require.Equal(t, r.Through, again.reservation.recoverThrough) + // A shutdown before recovering the uncertain range must not mark it clean. + require.NoError(t, again.close()) + r, err = alpenglow.LoadVoteReservation(cfg.HistoryDir, v.node) + require.NoError(t, err) + require.Empty(t, r.CleanHistoryDigest) +} + +func TestReservedVotingRejectsMissingCorruptOrWrongDomain(t *testing.T) { + for _, which := range []string{"missing_history", "missing_bound", "corrupt_bound", "genesis", "authorized", "vote_account", "synchronous"} { + t.Run(which, func(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + reserveThrough(t, v.reservation, 44) + crashReservedTestVoter(t, v) + cfg.InitializeVoteReservation = false + switch which { + case "missing_history": + require.NoError(t, os.Remove(alpenglow.VoteHistoryFilename(cfg.HistoryDir, v.node))) + case "missing_bound": + require.NoError(t, os.Remove(alpenglow.VoteReservationFilename(cfg.HistoryDir, v.node))) + cfg.InitializeVoteReservation = true + case "corrupt_bound": + require.NoError(t, os.WriteFile(alpenglow.VoteReservationFilename(cfg.HistoryDir, v.node), []byte("{"), 0600)) + case "genesis": + cfg.Genesis = solana.Hash{2} + case "authorized": + cfg.AuthorizedVoter = voterTestKey(19) + case "vote_account": + cfg.VoteAccount = solana.PublicKey{9} + case "synchronous": + cfg.ReservedHistory = false + } + _, err = openReservedTestVoter(t, cfg, 39) + require.Error(t, err) + }) + } +} + +func TestReservedVotingRequiresEnrollmentAndExclusiveOwner(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + cfg.InitializeVoteReservation = false + _, err := openReservedTestVoter(t, cfg, 39) + require.Error(t, err) + cfg.InitializeVoteReservation = true + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + _, err = openReservedTestVoter(t, cfg, 39) + require.ErrorContains(t, err, "already owned") + require.NoError(t, v.close()) +} + +func TestSigningReservationUnacknowledgedSyncCannotAuthorize(t *testing.T) { + entered, release := make(chan struct{}), make(chan struct{}) + r := &signingReservation{record: alpenglow.VoteReservation{Through: 64, Generation: 1}, wake: make(chan struct{}, 1), changed: make(chan struct{}, 1), stop: make(chan struct{}), done: make(chan struct{})} + r.through.Store(64) + var calls atomic.Uint64 + r.persist = func(next alpenglow.VoteReservation) error { + calls.Add(1) + close(entered) + <-release + return nil + } + go r.run() + require.True(t, r.allow(64, 64, false)) + <-entered + require.Equal(t, uint64(64), r.through.Load()) + require.False(t, r.allow(65, 64, false)) + close(release) + require.Eventually(t, func() bool { return r.through.Load() > 64 }, time.Second, time.Millisecond) + require.True(t, r.allow(65, 64, false)) + r.halt() + require.Equal(t, uint64(1), calls.Load()) +} + +func TestSigningReservationUncertainWriteSurvivesRestart(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + // Stop the original worker, then exercise a fresh worker against the real record. + v.reservation.halt() + record, err := alpenglow.LoadVoteReservation(cfg.HistoryDir, v.node) + require.NoError(t, err) + r := &signingReservation{record: record, wake: make(chan struct{}, 1), changed: make(chan struct{}, 1), stop: make(chan struct{}), done: make(chan struct{})} + r.through.Store(record.Through) + wrote := make(chan struct{}, 1) + r.persist = func(next alpenglow.VoteReservation) error { + err := alpenglow.SaveVoteReservation(cfg.HistoryDir, next, cfg.Identity) + select { + case wrote <- struct{}{}: + default: + } + if err != nil { + return err + } + return errors.New("injected lost sync acknowledgement") + } + v.reservation = r + go r.run() + require.False(t, r.allow(60, 39, false)) + <-wrote + r.halt() + require.Equal(t, record.Through, r.through.Load()) + durable, err := alpenglow.LoadVoteReservation(cfg.HistoryDir, v.node) + require.NoError(t, err) + require.Greater(t, durable.Through, record.Through) + require.ErrorContains(t, r.seal(cfg.HistoryDir, v.history, cfg.Identity), "uncertain") + crashReservedTestVoter(t, v) + cfg.InitializeVoteReservation = false + resumed, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + require.Equal(t, durable.Through, resumed.reservation.recoverThrough) +} + +func TestSigningReservationNeverWraps(t *testing.T) { + r := &signingReservation{record: alpenglow.VoteReservation{Through: 64, Generation: 1}, wake: make(chan struct{}, 1), changed: make(chan struct{}, 1), stop: make(chan struct{}), done: make(chan struct{}), persist: func(alpenglow.VoteReservation) error { t.Error("overflow attempted persistence"); return nil }} + r.through.Store(64) + go r.run() + require.False(t, r.allow(math.MaxUint64, 64, false)) + r.halt() + require.Equal(t, uint64(64), r.through.Load()) +} + +func TestReservationRetryRechecksFinality(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + event := voterEvent{kind: voterEventBlockTimeout, slot: 44} + require.NoError(t, v.handle(event)) + require.NotEmpty(t, v.reservationEvents) + reserveThrough(t, v.reservation, 44) + v.engine.SetAlpenglowRoot(alpenglow.BlockID{Slot: 47, Hash: solana.Hash{47}}) + pending := v.reservationEvents + v.reservationEvents = nil + for _, e := range pending { + require.NoError(t, v.handle(e)) + } + require.False(t, v.history.HasSkipped(44), "finalized work must not be signed after a delayed ack") +} + +func TestReservedVotingEverySignatureTypeAtBound(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + reserveThrough(t, v.reservation, 44) + h := v.reservation.through.Load() + // Freeze acknowledgement while leaving the signing guard active. + v.reservation.halt() + v.reservation.stopped.Store(false) + defer v.reservation.stopped.Store(true) + for _, slot := range []uint64{h, h + 1} { + votes := []alpenglow.Vote{alpenglow.NewNotarizationVote(slot, solana.Hash{2}), alpenglow.NewSkipVote(slot), alpenglow.NewFinalizationVote(slot), alpenglow.NewNotarizationFallbackVote(slot, solana.Hash{2}), alpenglow.NewSkipFallbackVote(slot)} + for _, vote := range votes { + for _, normal := range []bool{false, true} { + _, _, err := v.sign(vote, normal) + if slot == h { + require.NoError(t, err) + } else { + require.ErrorIs(t, err, errVoterNotReady) + } + } + } + } +} + +func TestReservedCleanDigestMismatchUsesCrashRecovery(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + baseline, err := os.ReadFile(alpenglow.VoteHistoryFilename(cfg.HistoryDir, v.node)) + require.NoError(t, err) + reserveThrough(t, v.reservation, 44) + voted, err := v.cast(alpenglow.NewSkipVote(44), false) + require.NoError(t, err) + require.True(t, voted) + require.NoError(t, v.close()) + require.NoError(t, os.WriteFile(alpenglow.VoteHistoryFilename(cfg.HistoryDir, v.node), baseline, 0600)) + resumed, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + require.NotZero(t, resumed.reservation.recoverThrough) +} + +func TestReservationLoopRetriesAfterAcknowledgement(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + v.start() + require.NoError(t, v.enqueue(voterEvent{kind: voterEventBlockTimeout, slot: 44})) + // Read via stats, not mutable voter history, while its loop is running. + require.Eventually(t, func() bool { v.landingMu.RLock(); defer v.landingMu.RUnlock(); return v.stats.VotesCastThisRun == 4 }, time.Second, time.Millisecond) + require.NoError(t, v.close()) + for slot := uint64(44); slot <= 47; slot++ { + require.True(t, v.history.HasSkipped(slot)) + } +} + +func TestReservationRetainsWindowCrossingBound(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + reserveThrough(t, v.reservation, 44) + h := v.reservation.through.Load() // 76: window ends at 79. + v.retainReservationEvent(voterEvent{kind: voterEventBlockTimeout, slot: h}) + require.Len(t, v.reservationEvents, 1, "trailing skip slots require retry even if the first slot fits") +} diff --git a/pkg/consensus/voter.go b/pkg/consensus/voter.go index b77a7689..2225cd89 100644 --- a/pkg/consensus/voter.go +++ b/pkg/consensus/voter.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "math" + "os" "sort" "sync" "time" @@ -38,21 +39,27 @@ type VotingPeerSource func(validators []alpenglow.ValidatorStake) []alpenglow.Vo // account address; AuthorizedVoter is the Ed25519 signer from which its BLS key // was registered. type VotingConfig struct { - Identity ed25519.PrivateKey - AuthorizedVoter ed25519.PrivateKey - VoteAccount solana.PublicKey - HistoryDir string - EpochForSlot func(slot uint64) uint64 - Peers VotingPeerSource - SlotDuration time.Duration - WaitToVoteSlot uint64 - ReadyToVote func(slot uint64) bool + Identity ed25519.PrivateKey + AuthorizedVoter ed25519.PrivateKey + VoteAccount solana.PublicKey + HistoryDir string + ReservedHistory bool + InitializeVoteReservation bool + Genesis solana.Hash + EpochForSlot func(slot uint64) uint64 + Peers VotingPeerSource + SlotDuration time.Duration + WaitToVoteSlot uint64 // Inclusive minimum for new votes; authenticated history restoration is separate + ReadyToVote func(slot uint64) bool } // VotingStats exposes positive network evidence separately from local casting. // NetworkLandedVotes counts unique persisted votes whose rank appeared in the // exact BLS-verified certificate proof received over Votor QUIC. type VotingStats struct { + ReservedHistory bool `json:"reserved_history"` + SigningReservedThrough uint64 `json:"signing_reserved_through,omitempty"` + RecoveryThrough uint64 `json:"recovery_through,omitempty"` Enabled bool `json:"enabled"` VotesCastThisRun uint64 `json:"votes_cast_this_run"` NetworkLandedVotes uint64 `json:"network_landed_votes"` @@ -109,43 +116,48 @@ type pendingVotorBlock struct { // history decisions run on loop; validator-set snapshots are protected only so // the outbound peer callback can read them from broadcast workers. type alpenglowVoter struct { - engine *AlpenglowObserverEngine - identity ed25519.PrivateKey - node solana.PublicKey - voteAccount solana.PublicKey - signer *alpenglow.BLSSigner - historyDir string - history *alpenglow.VoteHistory - epochForSlot func(uint64) uint64 - peerSource VotingPeerSource - slotDuration time.Duration - waitToVoteSlot uint64 - readyToVote func(slot uint64) bool - broadcaster *alpenglow.VotorBroadcaster - events chan voterEvent - done chan struct{} - startOnce sync.Once - closeOnce sync.Once - wg sync.WaitGroup - setsMu sync.RWMutex - sets map[uint64]alpenglow.ValidatorSet - restored map[alpenglow.VoteMessageKey]bool - pending map[uint64][]pendingVotorBlock - receivedShred map[uint64]bool - timeoutsSet map[uint64]bool - executedBlocks map[alpenglow.BlockID]bool - highestFinal uint64 - lastFinalizedAt time.Time - votingStarted bool - latestLiveSlot uint64 - standstillSlot *uint64 - refreshQueue []alpenglow.Message - refreshCursor int - lastWarn map[uint64]time.Time - landingMu sync.RWMutex - landed map[alpenglow.VoteMessageKey]struct{} - stats VotingStats - lastStatsLog time.Time + engine *AlpenglowObserverEngine + identity ed25519.PrivateKey + node solana.PublicKey + voteAccount solana.PublicKey + signer *alpenglow.BLSSigner + historyDir string + historyLock *os.File + reservation *signingReservation + reservationEvents []voterEvent + shutdownOnce sync.Once + shutdownErr error + history *alpenglow.VoteHistory + epochForSlot func(uint64) uint64 + peerSource VotingPeerSource + slotDuration time.Duration + waitToVoteSlot uint64 + readyToVote func(slot uint64) bool + broadcaster *alpenglow.VotorBroadcaster + events chan voterEvent + done chan struct{} + startOnce sync.Once + closeOnce sync.Once + wg sync.WaitGroup + setsMu sync.RWMutex + sets map[uint64]alpenglow.ValidatorSet + restored map[alpenglow.VoteMessageKey]bool + pending map[uint64][]pendingVotorBlock + receivedShred map[uint64]bool + timeoutsSet map[uint64]bool + executedBlocks map[alpenglow.BlockID]bool + highestFinal uint64 + lastFinalizedAt time.Time + votingStarted bool + latestLiveSlot uint64 + standstillSlot *uint64 + refreshQueue []alpenglow.Message + refreshCursor int + lastWarn map[uint64]time.Time + landingMu sync.RWMutex + landed map[alpenglow.VoteMessageKey]struct{} + stats VotingStats + lastStatsLog time.Time // beforeVoteGuard is a deterministic test seam for invalidation races. It // is nil in production. beforeVoteGuard func(alpenglow.BlockID) @@ -163,6 +175,9 @@ func newAlpenglowVoterUnstarted(engine *AlpenglowObserverEngine, cfg VotingConfi } func newAlpenglowVoterWithStart(engine *AlpenglowObserverEngine, cfg VotingConfig, root alpenglow.BlockID, start bool, sets []alpenglow.ValidatorSet) (*alpenglowVoter, error) { + if cfg.InitializeVoteReservation && !cfg.ReservedHistory { + return nil, errors.New("initialize-vote-reservation requires reserved-vote-history") + } if engine == nil { return nil, fmt.Errorf("enable Alpenglow voting: nil consensus engine") } @@ -192,17 +207,52 @@ func newAlpenglowVoterWithStart(engine *AlpenglowObserverEngine, cfg VotingConfi if node != engineNode { return nil, fmt.Errorf("enable Alpenglow voting: identity %s does not match consensus transport identity %s", node, engineNode) } + historyLock, err := alpenglow.LockVoteHistory(cfg.HistoryDir, node) + if err != nil { + return nil, err + } + keepLock := false + defer func() { + if !keepLock { + historyLock.Close() + } + }() + if !cfg.ReservedHistory { + if _, err := os.Stat(alpenglow.VoteReservationFilename(cfg.HistoryDir, node)); !errors.Is(err, os.ErrNotExist) { + return nil, fmt.Errorf("existing or unreadable vote reservation requires reserved history mode") + } + } history, err := alpenglow.LoadVoteHistory(cfg.HistoryDir, node) if err != nil { if !errors.Is(err, alpenglow.ErrVoteHistoryNotFound) { return nil, fmt.Errorf("enable Alpenglow voting: refuse unsafe vote-history reset: %w", err) } + if cfg.ReservedHistory { + if _, err := os.Stat(alpenglow.VoteReservationFilename(cfg.HistoryDir, node)); !errors.Is(err, os.ErrNotExist) || !cfg.InitializeVoteReservation { + return nil, fmt.Errorf("missing history for reserved voter; refusing automatic reset") + } + } history = alpenglow.NewVoteHistory(node, root.Slot) if err := alpenglow.SaveVoteHistory(cfg.HistoryDir, history, cfg.Identity); err != nil { return nil, fmt.Errorf("initialize Alpenglow vote history: %w", err) } mlog.Log.FileOnlyf("ALPENGLOW voting: created new vote history for %s at root %d; do not reuse this vote account on another validator", node, root.Slot) } + if history.ReservationRequired && !cfg.ReservedHistory { + return nil, errors.New("reserved vote history cannot be opened in synchronous mode") + } + var reservation *signingReservation + if cfg.ReservedHistory { + reservation, err = openSigningReservation(cfg, node, engine.shredVersion, history) + if err != nil { + return nil, err + } + defer func() { + if !keepLock { + reservation.halt() + } + }() + } if history.Root < root.Slot { history.SetRoot(root.Slot) if err := alpenglow.SaveVoteHistory(cfg.HistoryDir, history, cfg.Identity); err != nil { @@ -220,6 +270,8 @@ func newAlpenglowVoterWithStart(engine *AlpenglowObserverEngine, cfg VotingConfi voteAccount: cfg.VoteAccount, signer: signer, historyDir: cfg.HistoryDir, + historyLock: historyLock, + reservation: reservation, history: history, epochForSlot: cfg.EpochForSlot, peerSource: cfg.Peers, @@ -261,6 +313,7 @@ func newAlpenglowVoterWithStart(engine *AlpenglowObserverEngine, cfg VotingConfi return nil, err } v.broadcaster = broadcaster + keepLock = true if start { v.start() } @@ -300,12 +353,25 @@ func (v *alpenglowVoter) loop() { v.closeOnce.Do(func() { close(v.done) }) v.wg.Done() }() + var reservationChanged <-chan struct{} + if v.reservation != nil { + reservationChanged = v.reservation.changed + } ticker := time.NewTicker(time.Second) defer ticker.Stop() for { select { case <-v.done: return + case <-reservationChanged: + pending := v.reservationEvents + v.reservationEvents = nil + for _, event := range pending { + if err := v.handle(event); err != nil { + v.engine.latchSafetyError(fmt.Errorf("reservation retry: %w", err)) + return + } + } case event := <-v.events: if err := v.handle(event); err != nil { v.engine.latchSafetyError(fmt.Errorf("voting engine: %w", err)) @@ -324,6 +390,11 @@ func (v *alpenglowVoter) loop() { } func (v *alpenglowVoter) handle(event voterEvent) error { + v.retainReservationEvent(event) + return v.handleEvent(event) +} + +func (v *alpenglowVoter) handleEvent(event voterEvent) error { floor := v.admissionFloor() if event.kind == voterEventBlock && v.engine.ensureChain().IsObjectivelyInvalidBlock(event.block.Block) { return nil @@ -691,9 +762,9 @@ func (v *alpenglowVoter) castTarget(vote alpenglow.Vote, restoring bool, guarded if err := v.history.AddVote(vote); err != nil { return false, fmt.Errorf("record %s vote at slot %d: %w", vote.Type, vote.Slot, err) } - // Pool admission may synchronously assemble and publish a certificate. - // Persist the anti-equivocation record first so no externally visible - // proof can survive a crash without its signed local history. + // Pool admission can publish a certificate. In reserved mode the durable + // upper bound covers loss of this unsynchronized history replacement; + // synchronous mode still persists the exact history before admission. if err := v.saveHistory(); err != nil { return false, err } @@ -728,6 +799,9 @@ func (v *alpenglowVoter) castTarget(vote alpenglow.Vote, restoring bool, guarded } func (v *alpenglowVoter) sign(vote alpenglow.Vote, respectVotingGate bool) (alpenglow.VoteMessage, alpenglow.VoteVerifyResult, error) { + if v.reservation != nil && !v.reservation.allow(vote.Slot, v.engine.alpenglowVoteActionFloor(), false) { + return alpenglow.VoteMessage{}, alpenglow.VoteVerifyResult{}, fmt.Errorf("%w: waiting for verified recovery or durable signing reservation", errVoterNotReady) + } if respectVotingGate { if err := v.votingGateError(vote.Slot); err != nil { return alpenglow.VoteMessage{}, alpenglow.VoteVerifyResult{}, err @@ -768,7 +842,7 @@ func (v *alpenglowVoter) votingGateError(slot uint64) error { return fmt.Errorf("%w: slot %d is at or below consensus action floor %d", errVoterNotReady, slot, floor) } if slot < v.waitToVoteSlot { - return fmt.Errorf("%w: waiting for startup watermark slot %d", errVoterNotReady, v.waitToVoteSlot) + return fmt.Errorf("%w: waiting for voting cutoff slot %d", errVoterNotReady, v.waitToVoteSlot) } // ReadyToVote is a startup join guard, not a perpetual clock check. Once an // accepted live block or vote joins Votor, verified ParentReady and timeout @@ -777,6 +851,9 @@ func (v *alpenglowVoter) votingGateError(slot uint64) error { if !v.votingStarted && v.readyToVote != nil && !v.readyToVote(slot) { return fmt.Errorf("%w: slot is still behind the startup live voting window", errVoterNotReady) } + if v.reservation != nil && !v.reservation.allow(slot, v.engine.alpenglowVoteActionFloor(), false) { + return fmt.Errorf("%w: waiting for verified recovery or durable signing reservation", errVoterNotReady) + } return nil } @@ -837,6 +914,9 @@ func (v *alpenglowVoter) votorTransportValidators() []alpenglow.ValidatorStake { } func (v *alpenglowVoter) restoreVotesForEpoch(epoch uint64) error { + if v.reservation != nil && v.reservation.recoverThrough != 0 { + return nil + } floor := v.admissionFloor() for _, vote := range v.history.VotesAfter(v.history.Root - minU64(v.history.Root, 1)) { // Keep every signed history entry for anti-equivocation, but do not @@ -884,6 +964,9 @@ func (v *alpenglowVoter) restoreVotesForEpoch(epoch uint64) error { // persist-before-admission crash window without trusting process-local invalid // block state across a restart. func (v *alpenglowVoter) restoreVotesForBlock(block alpenglow.BlockID) (bool, error) { + if v.reservation != nil && v.reservation.recoverThrough != 0 { + return false, nil + } if block.Slot <= v.admissionFloor() { return false, nil } @@ -1087,7 +1170,11 @@ func (v *alpenglowVoter) isIdentityStaked(slot uint64) bool { } func (v *alpenglowVoter) saveHistory() error { - if err := alpenglow.SaveVoteHistory(v.historyDir, v.history, v.identity); err != nil { + save := alpenglow.SaveVoteHistory + if v.reservation != nil { + save = alpenglow.SaveReservedVoteHistory + } + if err := save(v.historyDir, v.history, v.identity); err != nil { return fmt.Errorf("persist vote history before consensus publication: %w", err) } return nil @@ -1201,6 +1288,11 @@ func (v *alpenglowVoter) snapshot() VotingStats { stats := v.stats v.landingMu.RUnlock() stats.Enabled = true + if v.reservation != nil { + stats.ReservedHistory = true + stats.SigningReservedThrough = v.reservation.through.Load() + stats.RecoveryThrough = v.reservation.recoverThrough + } if v.broadcaster != nil { broadcast := v.broadcaster.Stats() stats.BroadcastMessagesQueued = broadcast.MessagesQueued @@ -1228,7 +1320,7 @@ func (v *alpenglowVoter) maybeLogStats() { } v.lastStatsLog = time.Now() stats := v.snapshot() - mlog.Log.FileOnlyf("alpenglow voting stats: votes_cast_this_run=%d network_landed=%d last_landed_slot=%d broadcast_queued=%d broadcast_dropped=%d peer_sends=%d peer_sends_skipped=%d peer_send_errors=%d desired_peers=%d active_connections=%d pending_connections=%d connection_attempts=%d connection_errors=%d connection_jobs_dropped=%d", + mlog.Log.FileOnlyf("alpenglow voting stats: votes_cast_this_run=%d network_landed=%d last_landed_slot=%d broadcast_queued=%d broadcast_dropped=%d peer_sends=%d peer_sends_skipped=%d peer_send_errors=%d desired_peers=%d active_connections=%d pending_connections=%d connection_attempts=%d connection_errors=%d connection_jobs_dropped=%d reserved_history=%t signing_through=%d recovery_through=%d", stats.VotesCastThisRun, stats.NetworkLandedVotes, stats.LastNetworkLandedSlot, @@ -1243,6 +1335,7 @@ func (v *alpenglowVoter) maybeLogStats() { stats.BroadcastConnectionAttempts, stats.BroadcastConnectionErrors, stats.BroadcastConnectionJobsDropped, + stats.ReservedHistory, stats.SigningReservedThrough, stats.RecoveryThrough, ) } @@ -1274,9 +1367,23 @@ func (v *alpenglowVoter) close() error { if v == nil { return nil } - v.closeOnce.Do(func() { close(v.done) }) - v.wg.Wait() - return v.broadcaster.Close() + v.shutdownOnce.Do(func() { + v.closeOnce.Do(func() { close(v.done) }) + v.wg.Wait() + if v.reservation != nil { + v.reservation.halt() + floor := v.engine.alpenglowVoteActionFloor() + if v.engine.safetyError() == nil && floor >= v.reservation.recoverThrough { + v.history.SetRoot(floor) + v.shutdownErr = v.reservation.seal(v.historyDir, v.history, v.identity) + if v.shutdownErr == nil { + mlog.Log.Infof("ALPENGLOW signing reservation: clean history sealed at root=%d through=%d", v.history.Root, v.reservation.through.Load()) + } + } + } + v.shutdownErr = errors.Join(v.shutdownErr, v.broadcaster.Close(), v.historyLock.Close()) + }) + return v.shutdownErr } func pendingContains(blocks []pendingVotorBlock, candidate pendingVotorBlock) bool { diff --git a/pkg/consensus/voter_wait_slot_test.go b/pkg/consensus/voter_wait_slot_test.go new file mode 100644 index 00000000..b06a3581 --- /dev/null +++ b/pkg/consensus/voter_wait_slot_test.go @@ -0,0 +1,91 @@ +package consensus + +import ( + "crypto/ed25519" + "testing" + + "github.com/Overclock-Validator/mithril/pkg/alpenglow" + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func waitSlotTestVoter(t *testing.T, cutoff uint64) *alpenglowVoter { + t.Helper() + identity, authorized := voterTestKey(11), voterTestKey(12) + voteAccount := solana.PublicKey(voterTestKey(13).Public().(ed25519.PublicKey)) + set := voterTestValidatorSet(t, identity, authorized, voteAccount) + root := alpenglow.BlockID{Slot: 39, Hash: solana.Hash{0x39}} + engine, err := NewEngine(Config{AlpenglowShredVersion: 0x1234, AlpenglowIdentity: identity}) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, engine.Close()) }) + engine.SetAlpenglowEpochLookup(func(uint64) uint64 { return set.Epoch }) + require.NoError(t, engine.SetAlpenglowValidatorSet(set)) + engine.SetAlpenglowRoot(root) + voter, err := newAlpenglowVoterUnstarted(engine, VotingConfig{ + Identity: identity, AuthorizedVoter: authorized, VoteAccount: voteAccount, + HistoryDir: t.TempDir(), EpochForSlot: func(uint64) uint64 { return set.Epoch }, + Peers: func([]alpenglow.ValidatorStake) []alpenglow.VotorPeer { return nil }, + WaitToVoteSlot: cutoff, ReadyToVote: func(uint64) bool { return true }, + }, root, []alpenglow.ValidatorSet{set}) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, voter.close()) }) + return voter +} + +func TestWaitToVoteSlotGatesEveryNewVoteType(t *testing.T) { + voter := waitSlotTestVoter(t, 44) + constructors := []func(uint64) alpenglow.Vote{ + func(slot uint64) alpenglow.Vote { return alpenglow.NewNotarizationVote(slot, solana.Hash{1}) }, + alpenglow.NewFinalizationVote, + alpenglow.NewSkipVote, + func(slot uint64) alpenglow.Vote { return alpenglow.NewNotarizationFallbackVote(slot, solana.Hash{1}) }, + alpenglow.NewSkipFallbackVote, + } + for _, started := range []bool{false, true} { + voter.votingStarted = started + for _, voteAt := range constructors { + vote := voteAt(43) + _, _, err := voter.sign(vote, true) + require.ErrorIs(t, err, errVoterNotReady, "%s started=%t", vote.Type, started) + for _, slot := range []uint64{44, 45} { + message, _, err := voter.sign(voteAt(slot), true) + require.NoError(t, err, "%s slot=%d started=%t", vote.Type, slot, started) + require.Equal(t, voteAt(slot), message.Vote) + } + } + } +} + +func TestWaitToVoteSlotSplitsSkipWindowAndPersistsOnlyAllowedVotes(t *testing.T) { + voter := waitSlotTestVoter(t, 42) + require.NoError(t, voter.trySkipWindow(40)) + for _, slot := range []uint64{40, 41} { + require.False(t, voter.history.VotedAt(slot)) + } + for _, slot := range []uint64{42, 43} { + require.True(t, voter.history.HasSkipped(slot)) + } + restored, err := alpenglow.LoadVoteHistory(voter.historyDir, voter.node) + require.NoError(t, err) + require.Equal(t, voter.history.VotesCast, restored.VotesCast) + require.EqualValues(t, 2, voter.engine.ensurePool().Snapshot().VerifiedVotes) + // Joining live voting must not make older slots eligible afterward. + require.True(t, voter.votingStarted) + voted, err := voter.cast(alpenglow.NewSkipVote(41), false) + require.NoError(t, err) + require.False(t, voted) + require.False(t, voter.history.VotedAt(41)) +} + +func TestWaitToVoteSlotPreservesAuthenticatedHistoryRestoration(t *testing.T) { + voter := waitSlotTestVoter(t, 44) + require.NoError(t, voter.history.AddVote(alpenglow.NewSkipVote(40))) + require.NoError(t, voter.saveHistory()) + restored, err := alpenglow.LoadVoteHistory(voter.historyDir, voter.node) + require.NoError(t, err) + voter.history = restored + require.NoError(t, voter.restoreVotesForEpoch(voter.epochForSlot(40))) + require.EqualValues(t, 1, voter.engine.ensurePool().Snapshot().VerifiedVotes) + require.False(t, voter.votingStarted, "restoring a recorded vote must not bypass startup readiness") + require.ErrorIs(t, voter.votingGateError(41), errVoterNotReady) +} From 6f92f8f15cef671b054e615dff60e4329afb1574 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 23:12:41 -0500 Subject: [PATCH 14/22] Persist reserved vote history with one ordered background writer --- docs/reserved-vote-history.md | 29 ++- pkg/alpenglow/vote_history.go | 63 ++++-- pkg/alpenglow/vote_history_snapshot_test.go | 49 +++++ pkg/consensus/vote_history_writer.go | 120 ++++++++++++ pkg/consensus/vote_history_writer_test.go | 201 ++++++++++++++++++++ pkg/consensus/vote_reservation_test.go | 3 + pkg/consensus/voter.go | 40 +++- 7 files changed, 482 insertions(+), 23 deletions(-) create mode 100644 pkg/alpenglow/vote_history_snapshot_test.go create mode 100644 pkg/consensus/vote_history_writer.go create mode 100644 pkg/consensus/vote_history_writer_test.go diff --git a/docs/reserved-vote-history.md b/docs/reserved-vote-history.md index 92c5e3b7..fcb9d885 100644 --- a/docs/reserved-vote-history.md +++ b/docs/reserved-vote-history.md @@ -1,7 +1,7 @@ # Reserved vote history (experimental, opt-in) -Use `--reserved-vote-history` to keep detailed per-vote history with atomic -write-and-rename, without explicitly syncing each vote. An independent signed +Use `--reserved-vote-history` to keep detailed history with atomic +write-and-rename on an ordered background writer, without explicitly syncing each vote. An independent signed reservation is synced before its slot range can be used. The default remains synchronous history. `--wait-to-vote-slot N` is an additional inclusive minimum; it cannot override recovery, finality, execution or ParentReady checks. @@ -23,6 +23,17 @@ of AccountsDB snapshot restoration. Do not copy older safety files back during an application rollback. Keys, vote account, genesis and shred-version changes fail closed and require an explicit domain migration, not automatic re-enrollment. +Each history update validates and signs an immutable, complete snapshot on the +voter goroutine, then submits it without waiting for filesystem I/O. One worker +per voter owns all history replacements; it retains at most one in-flight and +one newer pending snapshot. While a write is blocked, the newest complete +snapshot supersedes an older pending snapshot. There is no intentional batching +delay. Complete in-memory voting decisions remain authoritative while running, +and the normal verified-root pruning rules still apply. Snapshot preparation +keeps serialization/signing CPU work on the voter but performs no filesystem +operations. Worker errors latch the engine safety fault and stop voting; an +already in-flight vote remains protected by the durable reservation. + The reservation worker grants up to 32 slots beyond the requested slot and renews when 16 or fewer remain. At the nominal 200 ms cadence those correspond to about 6.4 seconds of reserve and 3.2 seconds of renewal slack. They are slot @@ -40,8 +51,9 @@ normal live joining and protocol checks. RPC wall-clock estimates cannot release this recovery gate. Repeated crashes without new permission do not advance it. The conservative finality wait can be indefinite on a halted cluster. -Clean shutdown first stops the voter, joins the reservation worker, syncs exact -history (including the verified finality floor), then syncs a marker containing +Clean shutdown first stops the voter, joins the reservation worker, drains and +joins the history writer, syncs exact history (including the verified finality +floor), then syncs a marker containing its digest. Startup checks the digest and durably consumes the marker before any new signature or history mutation. A crash in that interval therefore falls back to the reservation. A shutdown before uncertain recovery completes cannot @@ -60,7 +72,14 @@ Validation includes race tests for incomplete history, repeated crashes, clean marker consumption/mismatch, missing/corrupt records, incompatible domains, unacknowledged and uncertain writes, H/H+1 for all five vote types and restoration, renewal retry with finality advancement, window-spanning skips, and leader gates. -These are deterministic software tests, not a host power-loss qualification or +Writer tests additionally cover snapshot isolation from mutable state, continued +voting while disk I/O is blocked, coalescing with complete retained decisions, +shutdown ordering, terminal write failures and killing a subprocess with both +an in-flight and pending unwritten snapshot. The reservation refuses voting in +the uncertain range after restart. The process-kill test does not simulate +power loss to the disk or a filesystem rollback. + +These are software tests, not a host power-loss qualification or formal proof of the full consensus protocol. `BenchmarkVoteHistoryPersistence` compares the actual serialization/write paths with 32 recorded notarizations; it does not measure block replay or full validator FAST participation. diff --git a/pkg/alpenglow/vote_history.go b/pkg/alpenglow/vote_history.go index 5ff7e1fa..d2a10f74 100644 --- a/pkg/alpenglow/vote_history.go +++ b/pkg/alpenglow/vote_history.go @@ -438,34 +438,77 @@ func SaveReservedVoteHistory(dir string, h *VoteHistory, identity ed25519.Privat return saveVoteHistory(dir, h, identity, false) } +// VoteHistorySnapshot owns signed, immutable bytes. It retains no reference to +// the voter's mutable maps or signing key and can be saved by a worker. +type VoteHistorySnapshot struct { + node solana.PublicKey + encoded []byte +} + +// PrepareReservedVoteHistory validates and signs the complete current history +// without filesystem access. Only the owner of h may call this while mutating h. +func PrepareReservedVoteHistory(h *VoteHistory, identity ed25519.PrivateKey) (*VoteHistorySnapshot, error) { + if h == nil || !h.ReservationRequired { + return nil, fmt.Errorf("reserved history requires durable reservation enrollment") + } + encoded, err := encodeVoteHistory(h, identity) + if err != nil { + return nil, err + } + return &VoteHistorySnapshot{node: h.NodePubkey, encoded: encoded}, nil +} + +// SaveReservedVoteHistorySnapshot replaces history without an explicit sync. +// Callers must serialize writes and enforce the independent durable reservation. +func SaveReservedVoteHistorySnapshot(dir string, snapshot *VoteHistorySnapshot) error { + if snapshot == nil || len(snapshot.encoded) == 0 { + return fmt.Errorf("save reserved history: empty snapshot") + } + if err := ensureDurableVoteHistoryDirectory(dir); err != nil { + return err + } + return replaceVoteHistoryFile(dir, VoteHistoryFilename(dir, snapshot.node), snapshot.encoded, false) +} + func saveVoteHistory(dir string, h *VoteHistory, identity ed25519.PrivateKey, durable bool) error { + encoded, err := encodeVoteHistory(h, identity) + if err != nil { + return err + } + if err := ensureDurableVoteHistoryDirectory(dir); err != nil { + return err + } + return replaceVoteHistoryFile(dir, VoteHistoryFilename(dir, h.NodePubkey), encoded, durable) +} + +func encodeVoteHistory(h *VoteHistory, identity ed25519.PrivateKey) ([]byte, error) { if h == nil { - return fmt.Errorf("save vote history: nil history") + return nil, fmt.Errorf("save vote history: nil history") } if len(identity) != ed25519.PrivateKeySize { - return fmt.Errorf("save vote history: invalid identity key size %d", len(identity)) + return nil, fmt.Errorf("save vote history: invalid identity key size %d", len(identity)) } node := solana.PublicKey(identity.Public().(ed25519.PublicKey)) if node != h.NodePubkey { - return fmt.Errorf("save vote history: identity %s does not match history %s", node, h.NodePubkey) + return nil, fmt.Errorf("save vote history: identity %s does not match history %s", node, h.NodePubkey) } h.Version = voteHistoryVersion if h.ReservationRequired { h.Version = reservedVoteHistoryVersion } if err := h.preparePersistedViews(); err != nil { - return fmt.Errorf("save vote history: %w", err) + return nil, fmt.Errorf("save vote history: %w", err) } defer func() { h.PersistedNotarized = nil h.PersistedParentReady = nil }() if err := h.validatePersistedState(); err != nil { - return fmt.Errorf("save vote history: %w", err) + return nil, fmt.Errorf("save vote history: %w", err) } data, err := json.Marshal(h) if err != nil { - return fmt.Errorf("serialize vote history: %w", err) + return nil, fmt.Errorf("serialize vote history: %w", err) } envelope := savedVoteHistory{ Version: h.Version, @@ -475,13 +518,9 @@ func saveVoteHistory(dir string, h *VoteHistory, identity ed25519.PrivateKey, du } encoded, err := json.Marshal(envelope) if err != nil { - return fmt.Errorf("serialize saved vote history: %w", err) - } - if err := ensureDurableVoteHistoryDirectory(dir); err != nil { - return err + return nil, fmt.Errorf("serialize saved vote history: %w", err) } - filename := VoteHistoryFilename(dir, node) - return replaceVoteHistoryFile(dir, filename, encoded, durable) + return encoded, nil } // ensureDurableVoteHistoryDirectory creates each missing path component and diff --git a/pkg/alpenglow/vote_history_snapshot_test.go b/pkg/alpenglow/vote_history_snapshot_test.go new file mode 100644 index 00000000..540e6fa1 --- /dev/null +++ b/pkg/alpenglow/vote_history_snapshot_test.go @@ -0,0 +1,49 @@ +package alpenglow + +import ( + "crypto/ed25519" + "testing" + + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func TestReservedHistorySnapshotIsImmutable(t *testing.T) { + identity := ed25519.NewKeyFromSeed(make([]byte, ed25519.SeedSize)) + node := solana.PublicKey(identity.Public().(ed25519.PublicKey)) + h := NewVoteHistory(node, 10) + h.ReservationRequired = true + block := BlockID{Slot: 11, Hash: solana.Hash{11}} + require.NoError(t, h.AddVote(NewNotarizationVote(11, block.Hash))) + h.NotarizedBlocks[block] = true + h.AddParentReady(12, block) + snapshot, err := PrepareReservedVoteHistory(h, identity) + require.NoError(t, err) + // Mutate/prune every transport-backed collection after taking the snapshot. + h.SetRoot(20) + require.NoError(t, h.AddVote(NewSkipVote(21))) + for i := range identity { + identity[i] = 0 + } + dir := t.TempDir() + require.NoError(t, SaveReservedVoteHistorySnapshot(dir, snapshot)) + loaded, err := LoadVoteHistory(dir, node) + require.NoError(t, err) + require.Equal(t, uint64(10), loaded.Root) + require.True(t, loaded.VotedAt(11)) + require.True(t, loaded.IsBlockNotarized(block)) + require.True(t, loaded.IsParentReady(12, block)) + require.False(t, loaded.HasSkipped(21)) +} + +func TestReservedHistorySnapshotRequiresEnrollmentAndValidHistory(t *testing.T) { + identity := ed25519.NewKeyFromSeed(make([]byte, ed25519.SeedSize)) + h := NewVoteHistory(solana.PublicKey(identity.Public().(ed25519.PublicKey)), 10) + _, err := PrepareReservedVoteHistory(h, identity) + require.Error(t, err) + h.ReservationRequired = true + h.Voted[11] = true // Inconsistent with canonical VotesCast. + _, err = PrepareReservedVoteHistory(h, identity) + require.Error(t, err) + require.Error(t, SaveReservedVoteHistorySnapshot(t.TempDir(), &VoteHistorySnapshot{})) +} diff --git a/pkg/consensus/vote_history_writer.go b/pkg/consensus/vote_history_writer.go new file mode 100644 index 00000000..dd732913 --- /dev/null +++ b/pkg/consensus/vote_history_writer.go @@ -0,0 +1,120 @@ +package consensus + +import ( + "errors" + "fmt" + "sync" + + "github.com/Overclock-Validator/mithril/pkg/alpenglow" +) + +// One serial writer, one in-flight snapshot and at most one newer pending +// snapshot. A newer complete history supersedes an unwritten snapshot; every +// retained, unrooted voting decision is still present in that newer history. +// The independently durable reservation, not this queue, authorizes signing. +type voteHistoryWriter struct { + mu sync.Mutex + pending *alpenglow.VoteHistorySnapshot + closing bool + failure error + submitted uint64 + written uint64 + coalesced uint64 + wake chan struct{} + done chan struct{} + persist func(*alpenglow.VoteHistorySnapshot) error + onError func(error) +} + +func newVoteHistoryWriter(persist func(*alpenglow.VoteHistorySnapshot) error, onError func(error)) *voteHistoryWriter { + w := &voteHistoryWriter{wake: make(chan struct{}, 1), done: make(chan struct{}), persist: persist, onError: onError} + go w.run() + return w +} + +// submit does no I/O and never waits for the writer. The mutex only protects +// pointer/counter changes; neither persistence nor error callbacks hold it. +func (w *voteHistoryWriter) submit(snapshot *alpenglow.VoteHistorySnapshot) error { + if snapshot == nil { + return errors.New("nil vote-history snapshot") + } + w.mu.Lock() + if w.failure != nil { + err := w.failure + w.mu.Unlock() + return err + } + if w.closing { + w.mu.Unlock() + return errors.New("vote-history writer is closed") + } + if w.pending != nil { + w.coalesced++ + } + w.pending = snapshot + w.submitted++ + w.mu.Unlock() + w.notify() + return nil +} + +func (w *voteHistoryWriter) notify() { + select { + case w.wake <- struct{}{}: + default: + } +} + +func (w *voteHistoryWriter) run() { + defer close(w.done) + for range w.wake { + for { + w.mu.Lock() + snapshot := w.pending + w.pending = nil + closing := w.closing + w.mu.Unlock() + if snapshot == nil { + if closing { + return + } + break + } + if err := w.persist(snapshot); err != nil { + err = fmt.Errorf("background vote-history write: %w", err) + w.mu.Lock() + w.failure = err + w.pending = nil + w.closing = true + w.mu.Unlock() + if w.onError != nil { + w.onError(err) + } + return + } + w.mu.Lock() + w.written++ + w.mu.Unlock() + } + } +} + +// close rejects new submissions and drains every retained snapshot. The voter +// must join this worker before writing and syncing its final clean history, +// otherwise an older in-flight rename could overwrite the sealed history. +func (w *voteHistoryWriter) close() error { + w.mu.Lock() + w.closing = true + w.mu.Unlock() + w.notify() + <-w.done + w.mu.Lock() + defer w.mu.Unlock() + return w.failure +} + +func (w *voteHistoryWriter) counters() (submitted, written, coalesced uint64) { + w.mu.Lock() + defer w.mu.Unlock() + return w.submitted, w.written, w.coalesced +} diff --git a/pkg/consensus/vote_history_writer_test.go b/pkg/consensus/vote_history_writer_test.go new file mode 100644 index 00000000..74c7bbfc --- /dev/null +++ b/pkg/consensus/vote_history_writer_test.go @@ -0,0 +1,201 @@ +package consensus + +import ( + "errors" + "os" + "os/exec" + "path/filepath" + "sync" + "testing" + "time" + + "github.com/Overclock-Validator/mithril/pkg/alpenglow" + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func TestAsyncHistoryBlockedWriteDoesNotDelayVotesAndCleanCloseDrains(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + reserveThrough(t, v.reservation, 44) + require.NoError(t, v.historyWriter.close()) + entered, release := make(chan struct{}), make(chan struct{}) + var once sync.Once + unblock := func() { once.Do(func() { close(release) }) } + t.Cleanup(unblock) + first := true // Owned only by the serial writer. + v.historyWriter = newVoteHistoryWriter(func(s *alpenglow.VoteHistorySnapshot) error { + if first { + first = false + close(entered) + <-release + } + return alpenglow.SaveReservedVoteHistorySnapshot(cfg.HistoryDir, s) + }, nil) + voted, err := v.cast(alpenglow.NewSkipVote(44), false) + require.NoError(t, err) + require.True(t, voted) + <-entered + castDone := make(chan error, 1) + go func() { + for _, slot := range []uint64{45, 46} { + ok, err := v.cast(alpenglow.NewSkipVote(slot), false) + if err != nil || !ok { + castDone <- errors.New("vote failed while history writer was blocked") + return + } + } + castDone <- nil + }() + select { + case err := <-castDone: + require.NoError(t, err) + case <-time.After(time.Second): + t.Fatal("disk writer blocked voting") + } + submitted, written, coalesced := v.historyWriter.counters() + require.Equal(t, uint64(3), submitted) + require.Zero(t, written) + require.Equal(t, uint64(1), coalesced) + onDisk, err := alpenglow.LoadVoteHistory(cfg.HistoryDir, v.node) + require.NoError(t, err) + require.False(t, onDisk.HasSkipped(44), "I/O should still be blocked") + // Even when disk history lags, complete in-memory decisions forbid conflict. + voted, err = v.cast(alpenglow.NewNotarizationVote(44, solana.Hash{7}), false) + require.NoError(t, err) + require.False(t, voted) + closed := make(chan error, 1) + go func() { closed <- v.close() }() + require.Eventually(t, func() bool { + v.historyWriter.mu.Lock() + defer v.historyWriter.mu.Unlock() + return v.historyWriter.closing + }, time.Second, time.Millisecond) + select { + case err := <-closed: + t.Fatalf("close returned before draining its writer: %v", err) + default: + } + r, err := alpenglow.LoadVoteReservation(cfg.HistoryDir, v.node) + require.NoError(t, err) + require.Empty(t, r.CleanHistoryDigest) + unblock() + require.NoError(t, <-closed) + onDisk, err = alpenglow.LoadVoteHistory(cfg.HistoryDir, v.node) + require.NoError(t, err) + for _, slot := range []uint64{44, 45, 46} { + require.True(t, onDisk.HasSkipped(slot)) + } + r, err = alpenglow.LoadVoteReservation(cfg.HistoryDir, v.node) + require.NoError(t, err) + digest, err := alpenglow.VoteHistoryDigest(cfg.HistoryDir, v.node) + require.NoError(t, err) + require.Equal(t, digest, r.CleanHistoryDigest) + _, written, _ = v.historyWriter.counters() + require.Equal(t, uint64(2), written, "old in-flight snapshot must finish before newest complete snapshot") +} + +func TestAsyncHistoryFailureIsStickyAndReportedWithoutMoreSubmissions(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + h := alpenglow.NewVoteHistory(voterTestValidatorSet(t, cfg.Identity, cfg.AuthorizedVoter, cfg.VoteAccount).Validators[0].NodePubkey, 39) + h.ReservationRequired = true + snapshot, err := alpenglow.PrepareReservedVoteHistory(h, cfg.Identity) + require.NoError(t, err) + diskErr := errors.New("injected disk failure") + reported := make(chan error, 1) + w := newVoteHistoryWriter(func(*alpenglow.VoteHistorySnapshot) error { return diskErr }, func(err error) { reported <- err }) + require.NoError(t, w.submit(snapshot)) + select { + case err := <-reported: + require.ErrorIs(t, err, diskErr) + case <-time.After(time.Second): + t.Fatal("background failure was not reported") + } + require.ErrorIs(t, w.submit(snapshot), diskErr) + require.ErrorIs(t, w.close(), diskErr) + require.ErrorIs(t, w.close(), diskErr) +} + +func TestAsyncHistoryFailureStopsVoterAndPreventsCleanMarker(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + e, err := NewEngine(Config{AlpenglowIdentity: cfg.Identity, AlpenglowShredVersion: 0x1234}) + require.NoError(t, err) + t.Cleanup(func() { _ = e.Close() }) + set := voterTestValidatorSet(t, cfg.Identity, cfg.AuthorizedVoter, cfg.VoteAccount) + e.SetAlpenglowEpochLookup(cfg.EpochForSlot) + require.NoError(t, e.SetAlpenglowValidatorSet(set)) + root := alpenglow.BlockID{Slot: 39, Hash: solana.Hash{39}} + e.SetAlpenglowRoot(root) + v, err := newAlpenglowVoterUnstarted(e, cfg, root, []alpenglow.ValidatorSet{set}) + require.NoError(t, err) + t.Cleanup(func() { _ = v.close() }) + reserveThrough(t, v.reservation, 44) + require.NoError(t, v.historyWriter.close()) + diskErr := errors.New("injected background disk failure") + v.historyWriter = newVoteHistoryWriter(func(*alpenglow.VoteHistorySnapshot) error { return diskErr }, v.failHistoryWrite) + require.NoError(t, v.history.AddVote(alpenglow.NewSkipVote(44))) + require.NoError(t, v.saveHistory()) + select { + case <-v.done: + case <-time.After(time.Second): + t.Fatal("disk failure did not stop the voter") + } + require.ErrorIs(t, e.safetyError(), diskErr) + _, _, err = v.sign(alpenglow.NewSkipVote(45), false) + require.ErrorIs(t, err, diskErr) + require.Error(t, v.enqueue(voterEvent{kind: voterEventBlockTimeout, slot: 45})) + require.ErrorIs(t, v.close(), diskErr) + r, err := alpenglow.LoadVoteReservation(cfg.HistoryDir, v.node) + require.NoError(t, err) + require.Empty(t, r.CleanHistoryDigest) +} + +// Kill a subprocess while its first history write is blocked and a newer +// snapshot is pending. Successful earlier reservation syncs survive this +// process crash; this is deliberately not a host power-loss test. +func TestAsyncHistoryProcessCrashLosesPendingSnapshots(t *testing.T) { + const childEnv = "MITHRIL_ASYNC_HISTORY_TEST_DIR" + if dir := os.Getenv(childEnv); dir != "" { + cfg := reservedTestConfig(dir) + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + reserveThrough(t, v.reservation, 44) + require.NoError(t, v.historyWriter.close()) + entered := make(chan struct{}) + v.historyWriter = newVoteHistoryWriter(func(*alpenglow.VoteHistorySnapshot) error { + close(entered) + select {} + }, nil) + for _, slot := range []uint64{44, 45} { + voted, err := v.cast(alpenglow.NewSkipVote(slot), false) + require.NoError(t, err) + require.True(t, voted) + if slot == 44 { + <-entered + } + } + require.NoError(t, os.WriteFile(filepath.Join(dir, "ready"), []byte("ready"), 0600)) + select {} + } + dir := t.TempDir() + cmd := exec.Command(os.Args[0], "-test.run=^TestAsyncHistoryProcessCrashLosesPendingSnapshots$", "-test.count=1") + cmd.Env = append(os.Environ(), childEnv+"="+dir) + require.NoError(t, cmd.Start()) + t.Cleanup(func() { _ = cmd.Process.Kill() }) + require.Eventually(t, func() bool { _, err := os.Stat(filepath.Join(dir, "ready")); return err == nil }, 10*time.Second, time.Millisecond) + require.NoError(t, cmd.Process.Kill()) + require.Error(t, cmd.Wait()) + cfg := reservedTestConfig(dir) + cfg.InitializeVoteReservation = false + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + require.False(t, v.history.HasSkipped(44)) + require.False(t, v.history.HasSkipped(45)) + h := v.reservation.recoverThrough + require.GreaterOrEqual(t, h, uint64(45)) + _, _, err = v.sign(alpenglow.NewSkipVote(44), false) + require.ErrorIs(t, err, errVoterNotReady) + _, _, err = v.sign(alpenglow.NewSkipVote(h+1), false) + require.ErrorIs(t, err, errVoterNotReady, "verified finality must reach the lost history's bound") +} diff --git a/pkg/consensus/vote_reservation_test.go b/pkg/consensus/vote_reservation_test.go index a89393d1..f4e5c418 100644 --- a/pkg/consensus/vote_reservation_test.go +++ b/pkg/consensus/vote_reservation_test.go @@ -48,6 +48,9 @@ func crashReservedTestVoter(t *testing.T, v *alpenglowVoter) { v.closeOnce.Do(func() { close(v.done) }) v.wg.Wait() v.reservation.halt() + if v.historyWriter != nil { + require.NoError(t, v.historyWriter.close()) + } require.NoError(t, v.broadcaster.Close()) require.NoError(t, v.historyLock.Close()) }) diff --git a/pkg/consensus/voter.go b/pkg/consensus/voter.go index 2225cd89..8557bc90 100644 --- a/pkg/consensus/voter.go +++ b/pkg/consensus/voter.go @@ -57,6 +57,9 @@ type VotingConfig struct { // NetworkLandedVotes counts unique persisted votes whose rank appeared in the // exact BLS-verified certificate proof received over Votor QUIC. type VotingStats struct { + HistorySnapshotsSubmitted uint64 `json:"history_snapshots_submitted,omitempty"` + HistorySnapshotsWritten uint64 `json:"history_snapshots_written,omitempty"` + HistorySnapshotsCoalesced uint64 `json:"history_snapshots_coalesced,omitempty"` ReservedHistory bool `json:"reserved_history"` SigningReservedThrough uint64 `json:"signing_reserved_through,omitempty"` RecoveryThrough uint64 `json:"recovery_through,omitempty"` @@ -124,6 +127,7 @@ type alpenglowVoter struct { historyDir string historyLock *os.File reservation *signingReservation + historyWriter *voteHistoryWriter reservationEvents []voterEvent shutdownOnce sync.Once shutdownErr error @@ -313,6 +317,11 @@ func newAlpenglowVoterWithStart(engine *AlpenglowObserverEngine, cfg VotingConfi return nil, err } v.broadcaster = broadcaster + if reservation != nil { + v.historyWriter = newVoteHistoryWriter(func(snapshot *alpenglow.VoteHistorySnapshot) error { + return alpenglow.SaveReservedVoteHistorySnapshot(v.historyDir, snapshot) + }, v.failHistoryWrite) + } keepLock = true if start { v.start() @@ -799,6 +808,9 @@ func (v *alpenglowVoter) castTarget(vote alpenglow.Vote, restoring bool, guarded } func (v *alpenglowVoter) sign(vote alpenglow.Vote, respectVotingGate bool) (alpenglow.VoteMessage, alpenglow.VoteVerifyResult, error) { + if err := v.engine.safetyError(); err != nil { + return alpenglow.VoteMessage{}, alpenglow.VoteVerifyResult{}, err + } if v.reservation != nil && !v.reservation.allow(vote.Slot, v.engine.alpenglowVoteActionFloor(), false) { return alpenglow.VoteMessage{}, alpenglow.VoteVerifyResult{}, fmt.Errorf("%w: waiting for verified recovery or durable signing reservation", errVoterNotReady) } @@ -1170,16 +1182,25 @@ func (v *alpenglowVoter) isIdentityStaked(slot uint64) bool { } func (v *alpenglowVoter) saveHistory() error { - save := alpenglow.SaveVoteHistory - if v.reservation != nil { - save = alpenglow.SaveReservedVoteHistory + if v.historyWriter != nil { + snapshot, err := alpenglow.PrepareReservedVoteHistory(v.history, v.identity) + if err != nil { + return err + } + return v.historyWriter.submit(snapshot) } - if err := save(v.historyDir, v.history, v.identity); err != nil { + if err := alpenglow.SaveVoteHistory(v.historyDir, v.history, v.identity); err != nil { return fmt.Errorf("persist vote history before consensus publication: %w", err) } return nil } +func (v *alpenglowVoter) failHistoryWrite(err error) { + v.engine.latchSafetyError(err) + mlog.Log.Errorf("ALPENGLOW VOTING SAFETY: %v", err) + v.closeOnce.Do(func() { close(v.done) }) +} + func (v *alpenglowVoter) recordNetworkCertificate(cert alpenglow.Certificate) { set, ok := v.validatorSet(cert.Slot) if !ok { @@ -1288,6 +1309,9 @@ func (v *alpenglowVoter) snapshot() VotingStats { stats := v.stats v.landingMu.RUnlock() stats.Enabled = true + if v.historyWriter != nil { + stats.HistorySnapshotsSubmitted, stats.HistorySnapshotsWritten, stats.HistorySnapshotsCoalesced = v.historyWriter.counters() + } if v.reservation != nil { stats.ReservedHistory = true stats.SigningReservedThrough = v.reservation.through.Load() @@ -1320,7 +1344,7 @@ func (v *alpenglowVoter) maybeLogStats() { } v.lastStatsLog = time.Now() stats := v.snapshot() - mlog.Log.FileOnlyf("alpenglow voting stats: votes_cast_this_run=%d network_landed=%d last_landed_slot=%d broadcast_queued=%d broadcast_dropped=%d peer_sends=%d peer_sends_skipped=%d peer_send_errors=%d desired_peers=%d active_connections=%d pending_connections=%d connection_attempts=%d connection_errors=%d connection_jobs_dropped=%d reserved_history=%t signing_through=%d recovery_through=%d", + mlog.Log.FileOnlyf("alpenglow voting stats: votes_cast_this_run=%d network_landed=%d last_landed_slot=%d broadcast_queued=%d broadcast_dropped=%d peer_sends=%d peer_sends_skipped=%d peer_send_errors=%d desired_peers=%d active_connections=%d pending_connections=%d connection_attempts=%d connection_errors=%d connection_jobs_dropped=%d reserved_history=%t signing_through=%d recovery_through=%d history_submitted=%d history_written=%d history_coalesced=%d", stats.VotesCastThisRun, stats.NetworkLandedVotes, stats.LastNetworkLandedSlot, @@ -1336,6 +1360,7 @@ func (v *alpenglowVoter) maybeLogStats() { stats.BroadcastConnectionErrors, stats.BroadcastConnectionJobsDropped, stats.ReservedHistory, stats.SigningReservedThrough, stats.RecoveryThrough, + stats.HistorySnapshotsSubmitted, stats.HistorySnapshotsWritten, stats.HistorySnapshotsCoalesced, ) } @@ -1372,8 +1397,11 @@ func (v *alpenglowVoter) close() error { v.wg.Wait() if v.reservation != nil { v.reservation.halt() + if v.historyWriter != nil { + v.shutdownErr = v.historyWriter.close() + } floor := v.engine.alpenglowVoteActionFloor() - if v.engine.safetyError() == nil && floor >= v.reservation.recoverThrough { + if v.shutdownErr == nil && v.engine.safetyError() == nil && floor >= v.reservation.recoverThrough { v.history.SetRoot(floor) v.shutdownErr = v.reservation.seal(v.historyDir, v.history, v.identity) if v.shutdownErr == nil { From 622053c76f4049c02ca4e6a6cf10db291634f6d8 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 23:12:41 -0500 Subject: [PATCH 15/22] Order live vote admission and durable pruning behind replay --- docs/reserved-vote-history.md | 14 +- pkg/consensus/engine.go | 38 +++- pkg/consensus/vote_reservation.go | 4 +- pkg/consensus/voter.go | 22 +- pkg/consensus/voter_finality_ordering_test.go | 192 ++++++++++++++++++ 5 files changed, 251 insertions(+), 19 deletions(-) create mode 100644 pkg/consensus/voter_finality_ordering_test.go diff --git a/docs/reserved-vote-history.md b/docs/reserved-vote-history.md index fcb9d885..483c82db 100644 --- a/docs/reserved-vote-history.md +++ b/docs/reserved-vote-history.md @@ -4,7 +4,19 @@ Use `--reserved-vote-history` to keep detailed history with atomic write-and-rename on an ordered background writer, without explicitly syncing each vote. An independent signed reservation is synced before its slot range can be used. The default remains synchronous history. `--wait-to-vote-slot N` is an additional inclusive minimum; -it cannot override recovery, finality, execution or ParentReady checks. +it cannot override recovery, retained-root, execution or ParentReady checks. + +Live vote admission uses the retained consensus-pool root and the persisted +vote-history root. Receiving a finalization certificate does not itself retire +that slot for voting: successful replay may still supply a valid notarization +before a later fast or slot+8 reward certificate is collected. The pool keeps +its existing bounded 16-slot finality tail; expired catch-up work is still +discarded. Exact execution, ParentReady, parent-vote and anti-equivocation rules +are unchanged. Durable checkpoint pruning is queued behind completed replay +events on the voter, so it cannot remove a block's voting state before its +earlier replay event is processed. Observer-only pruning remains synchronous. +Crash recovery uses a separate verified-finality/checkpoint floor, not this +live admission window. For the first enrollment, stop the previous validator normally and start with `--reserved-vote-history --initialize-vote-reservation`. Enrollment requires the diff --git a/pkg/consensus/engine.go b/pkg/consensus/engine.go index 23003fd2..57667810 100644 --- a/pkg/consensus/engine.go +++ b/pkg/consensus/engine.go @@ -967,11 +967,18 @@ func (e *AlpenglowObserverEngine) injectLocalVote(message alpenglow.VoteMessage, } } -// alpenglowVoteActionFloor is the highest slot on which this validator must -// not initiate a new vote. The pool root is its strict admission boundary; -// direct finality is included because the pool deliberately retains a short -// reward-accounting tail behind finality where network votes remain useful. +// alpenglowVoteActionFloor is the retained pool's strict admission boundary. +// Network finality is not a voting root: a replayed block may still contribute +// a notarization to a later fast certificate and its slot+8 reward certificate. +// The voter also checks its own persisted history root before signing. func (e *AlpenglowObserverEngine) alpenglowVoteActionFloor() uint64 { + return e.ensurePool().Snapshot().RootSlot +} + +// alpenglowVerifiedFinalityFloor releases crash-recovery reservations. Keep this +// independent of live vote admission: a retained reward window must not weaken +// the requirement to pass every slot that may have been signed before a crash. +func (e *AlpenglowObserverEngine) alpenglowVerifiedFinalityFloor() uint64 { floor := e.ensurePool().Snapshot().RootSlot if finalized := e.ensureChain().Snapshot().LatestDirectFinalizedBlock.Slot; finalized > floor { floor = finalized @@ -1528,6 +1535,25 @@ func (e *AlpenglowObserverEngine) PruneAlpenglowBefore(slot uint64) { if slot == 0 { return } + // Replay enqueues its completed-block event before publishing a durable + // promotion. Retire the pool, execution proof and history on that same + // ordered voter stream, so a fast checkpoint cannot overtake the vote. + e.voterMu.RLock() + voter := e.voter + e.voterMu.RUnlock() + if voter != nil { + if err := voter.enqueue(voterEvent{kind: voterEventDurableRoot, slot: slot}); err != nil { + e.latchSafetyError(err) + } + return + } + e.applyAlpenglowDurableRoot(slot) +} + +// applyAlpenglowDurableRoot is called by the voter after earlier replay events, +// or synchronously by an observer without a voting loop. Startup root restore +// remains a separate, immediate barrier in SetAlpenglowRoot. +func (e *AlpenglowObserverEngine) applyAlpenglowDurableRoot(slot uint64) alpenglow.BlockID { e.poolOutputMu.Lock() defer e.poolOutputMu.Unlock() @@ -1543,13 +1569,11 @@ func (e *AlpenglowObserverEngine) PruneAlpenglowBefore(slot uint64) { if e.certPool != nil { e.certPool.ObserveFloor(slot) } - if err := e.enqueueVoter(voterEvent{kind: voterEventRoot, root: root}); err != nil { - e.latchSafetyError(err) - } // Replay calls this only after the fold through slot is durably committed. // Keep the transport peer window tied to that local root, not to speculative // certificate finality or the pool's reward-retention floor. e.advanceVotorPeerRoot(slot) + return root } func (e *AlpenglowObserverEngine) pruneInvalidBlockIDsBefore(slot uint64) { diff --git a/pkg/consensus/vote_reservation.go b/pkg/consensus/vote_reservation.go index 74b26146..fbac102d 100644 --- a/pkg/consensus/vote_reservation.go +++ b/pkg/consensus/vote_reservation.go @@ -237,7 +237,7 @@ func (v *alpenglowVoter) retainReservationEvent(event voterEvent) { default: return } - if slot <= r.through.Load() || slot <= v.admissionFloor() || slot < v.waitToVoteSlot || v.engine.alpenglowVoteActionFloor() < r.recoverThrough || slot <= r.recoverThrough { + if slot <= r.through.Load() || slot <= v.admissionFloor() || slot < v.waitToVoteSlot || v.engine.alpenglowVerifiedFinalityFloor() < r.recoverThrough || slot <= r.recoverThrough { return } if !v.votingStarted && v.readyToVote != nil && !v.readyToVote(slot) { @@ -265,5 +265,5 @@ func (e *AlpenglowObserverEngine) AlpenglowCanSignLeaderSlot(slot uint64) bool { if v.reservation == nil { return true } - return v.reservation.allow(slot, e.alpenglowVoteActionFloor(), true) + return v.reservation.allow(slot, e.alpenglowVerifiedFinalityFloor(), true) } diff --git a/pkg/consensus/voter.go b/pkg/consensus/voter.go index 8557bc90..33f9d3a9 100644 --- a/pkg/consensus/voter.go +++ b/pkg/consensus/voter.go @@ -98,6 +98,7 @@ const ( voterEventValidatorSet voterEventRoot voterEventNetworkCertificate + voterEventDurableRoot ) type voterEvent struct { @@ -443,6 +444,9 @@ func (v *alpenglowVoter) handleEvent(event voterEvent) error { return nil } return v.saveHistory() + case voterEventDurableRoot: + root := v.engine.applyAlpenglowDurableRoot(event.slot) + return v.handleEvent(voterEvent{kind: voterEventRoot, root: root}) case voterEventNetworkCertificate: v.recordNetworkCertificate(event.certificate) return nil @@ -557,9 +561,9 @@ func (v *alpenglowVoter) handleConsensus(event alpenglow.ConsensusEvent) error { func (v *alpenglowVoter) admissionFloor() uint64 { floor := v.history.Root - if v.highestFinal > floor { - floor = v.highestFinal - } + // highestFinal tracks network progress and standstill, not retirement of + // our own decisions. Keep ParentReady, pending replay and exact vote history + // available until the retained pool or an ordered durable root retires them. if engineFloor := v.engine.alpenglowVoteActionFloor(); engineFloor > floor { floor = engineFloor } @@ -762,9 +766,9 @@ func (v *alpenglowVoter) castTarget(vote alpenglow.Vote, restoring bool, guarded return false, nil } if !restoring { - // Finality can advance while the BLS signature is computed. Avoid a - // durable stale record when that race is already visible here; if it - // advances later, atomic admission below classifies it benignly. + // Retention/root pruning can advance while the BLS signature is computed. + // Avoid an expired record if that race is already visible here; atomic + // admission below classifies a later pruning race benignly. if vote.Slot <= v.admissionFloor() { return false, nil } @@ -811,7 +815,7 @@ func (v *alpenglowVoter) sign(vote alpenglow.Vote, respectVotingGate bool) (alpe if err := v.engine.safetyError(); err != nil { return alpenglow.VoteMessage{}, alpenglow.VoteVerifyResult{}, err } - if v.reservation != nil && !v.reservation.allow(vote.Slot, v.engine.alpenglowVoteActionFloor(), false) { + if v.reservation != nil && !v.reservation.allow(vote.Slot, v.engine.alpenglowVerifiedFinalityFloor(), false) { return alpenglow.VoteMessage{}, alpenglow.VoteVerifyResult{}, fmt.Errorf("%w: waiting for verified recovery or durable signing reservation", errVoterNotReady) } if respectVotingGate { @@ -863,7 +867,7 @@ func (v *alpenglowVoter) votingGateError(slot uint64) error { if !v.votingStarted && v.readyToVote != nil && !v.readyToVote(slot) { return fmt.Errorf("%w: slot is still behind the startup live voting window", errVoterNotReady) } - if v.reservation != nil && !v.reservation.allow(slot, v.engine.alpenglowVoteActionFloor(), false) { + if v.reservation != nil && !v.reservation.allow(slot, v.engine.alpenglowVerifiedFinalityFloor(), false) { return fmt.Errorf("%w: waiting for verified recovery or durable signing reservation", errVoterNotReady) } return nil @@ -1400,7 +1404,7 @@ func (v *alpenglowVoter) close() error { if v.historyWriter != nil { v.shutdownErr = v.historyWriter.close() } - floor := v.engine.alpenglowVoteActionFloor() + floor := v.engine.alpenglowVerifiedFinalityFloor() if v.shutdownErr == nil && v.engine.safetyError() == nil && floor >= v.reservation.recoverThrough { v.history.SetRoot(floor) v.shutdownErr = v.reservation.seal(v.historyDir, v.history, v.identity) diff --git a/pkg/consensus/voter_finality_ordering_test.go b/pkg/consensus/voter_finality_ordering_test.go new file mode 100644 index 00000000..a53abb64 --- /dev/null +++ b/pkg/consensus/voter_finality_ordering_test.go @@ -0,0 +1,192 @@ +package consensus + +import ( + "context" + "crypto/ed25519" + "testing" + + "github.com/Overclock-Validator/mithril/pkg/alpenglow" + "github.com/Overclock-Validator/mithril/pkg/block" + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +// Keep the actor unstarted so tests can place finality, replay and durable +// promotion in a deterministic order, using the real engine event queue. +func newOrderingTestVoter(t *testing.T, reserved bool) (*AlpenglowObserverEngine, *alpenglowVoter) { + t.Helper() + cfg := reservedTestConfig(t.TempDir()) + cfg.ReservedHistory = reserved + cfg.InitializeVoteReservation = reserved + root := alpenglow.BlockID{Slot: 39, Hash: solana.Hash{39}} + e, err := NewEngine(Config{AlpenglowIdentity: cfg.Identity, AlpenglowShredVersion: 0x1234}) + require.NoError(t, err) + t.Cleanup(func() { require.NoError(t, e.Close()) }) + set := voterTestValidatorSet(t, cfg.Identity, cfg.AuthorizedVoter, cfg.VoteAccount) + e.SetAlpenglowEpochLookup(cfg.EpochForSlot) + require.NoError(t, e.SetAlpenglowValidatorSet(set)) + e.SetAlpenglowRoot(root) + v, err := newAlpenglowVoterUnstarted(e, cfg, root, []alpenglow.ValidatorSet{set}) + require.NoError(t, err) + e.voter = v + if reserved { + reserveThrough(t, v.reservation, 44) + } + // Equivalent to startup's trusted-root ParentReady seed. + require.True(t, v.history.AddParentReady(40, root)) + return e, v +} + +func drainOrderingEvents(t *testing.T, v *alpenglowVoter) { + t.Helper() + for i := 0; i < 1000; i++ { + select { + case event := <-v.events: + require.NoError(t, v.handle(event)) + default: + return + } + } + t.Fatal("voter event queue did not drain") +} + +func observeOrderingBlock(t *testing.T, e *AlpenglowObserverEngine, slot uint64) alpenglow.BlockID { + t.Helper() + id := alpenglow.BlockID{Slot: slot, Hash: solana.Hash{byte(slot)}} + require.NoError(t, e.ObserveBlock(context.Background(), BlockObservation{Source: "ordering-test", Block: &block.Block{ + Slot: slot, ParentSlot: slot - 1, + AlpenglowBlockID: [32]byte(id.Hash), HasAlpenglowBlockID: true, + AlpenglowParentBlockID: [32]byte{byte(slot - 1)}, HasAlpenglowParentBlockID: true, + }})) + return id +} + +func finalizeOrderingBlock(t *testing.T, e *AlpenglowObserverEngine, v *alpenglowVoter, id alpenglow.BlockID) { + t.Helper() + // The two peers supply the 60% slow-finality quorum without our vote; + // adding our 30% notarization later can produce a fast certificate. + for _, vote := range []alpenglow.Vote{alpenglow.NewNotarizationVote(id.Slot, id.Hash), alpenglow.NewFinalizationVote(id.Slot)} { + for rank, key := range []ed25519.PrivateKey{voterTestKey(21), voterTestKey(22)} { + peer := signedVerifiedVoterPeerVote(t, e, v.sets[7], key, uint16(rank+1), vote) + _, err := e.acceptVerifiedVoteResult(peer) + require.NoError(t, err) + } + } + require.Equal(t, id.Slot, e.ensureChain().Snapshot().LatestDirectFinalizedBlock.Slot) +} + +func TestAlpenglowVoterReplaysFourBlocksAfterNetworkFinality(t *testing.T) { + for _, reserved := range []bool{false, true} { + name := "synchronous" + if reserved { + name = "reserved" + } + t.Run(name, func(t *testing.T) { + e, v := newOrderingTestVoter(t, reserved) + for slot := uint64(40); slot <= 43; slot++ { + id := observeOrderingBlock(t, e, slot) + finalizeOrderingBlock(t, e, v, id) + drainOrderingEvents(t, v) + require.Equal(t, slot, v.highestFinal) + require.Less(t, v.admissionFloor(), slot) + require.False(t, v.history.VotedAt(slot), "network finality does not prove local execution") + before := v.snapshot() + require.NoError(t, e.OnReplayResult(context.Background(), SlotReplayResult{Slot: slot, Source: "ordering-test"})) + drainOrderingEvents(t, v) + hash, ok := v.history.NotarizedVote(slot) + require.True(t, ok, "replay must still notarize after slow finality") + require.Equal(t, id.Hash, hash) + require.Greater(t, v.snapshot().BroadcastMessagesQueued, before.BroadcastMessagesQueued) + require.NoError(t, e.AlpenglowSafetyError()) + } + }) + } +} + +func TestAlpenglowVoterNetworkFinalityDuringLocalAdmission(t *testing.T) { + e, v := newOrderingTestVoter(t, false) + id := observeOrderingBlock(t, e, 40) + called := false + v.beforeLocalVoteInject = func(vote alpenglow.Vote) { + if called { + return + } + called = true + require.Equal(t, alpenglow.NewNotarizationVote(id.Slot, id.Hash), vote) + finalizeOrderingBlock(t, e, v, id) + } + require.NoError(t, e.OnReplayResult(context.Background(), SlotReplayResult{Slot: id.Slot})) + drainOrderingEvents(t, v) + require.True(t, called) + require.Positive(t, v.snapshot().VotesCastThisRun) + message, _, err := v.sign(alpenglow.NewNotarizationVote(id.Slot, id.Hash), false) + require.NoError(t, err) + require.True(t, e.ensurePool().HasVerifiedVote(message)) + require.NoError(t, e.AlpenglowSafetyError()) +} + +func TestAlpenglowVoterDurableRootCannotOvertakeQueuedReplay(t *testing.T) { + e, v := newOrderingTestVoter(t, true) + id := observeOrderingBlock(t, e, 40) + finalizeOrderingBlock(t, e, v, id) + drainOrderingEvents(t, v) + require.NoError(t, e.OnReplayResult(context.Background(), SlotReplayResult{Slot: id.Slot})) + e.PruneAlpenglowBefore(id.Slot) + require.Equal(t, uint64(39), e.ensurePool().Snapshot().RootSlot) + require.Contains(t, e.executedReplayBlocks, id, "queued replay must retain its execution proof") + queuedBefore := v.snapshot().BroadcastMessagesQueued + drainOrderingEvents(t, v) + require.Greater(t, v.snapshot().BroadcastMessagesQueued, queuedBefore) + require.Equal(t, id.Slot, v.history.Root) + require.Equal(t, id.Slot, e.ensurePool().Snapshot().RootSlot) + require.NotContains(t, e.executedReplayBlocks, id) + _, ok := v.history.NotarizedVote(id.Slot) + require.True(t, ok, "root vote must remain available to its intra-window child") + child := observeOrderingBlock(t, e, 41) + finalizeOrderingBlock(t, e, v, child) + require.NoError(t, e.OnReplayResult(context.Background(), SlotReplayResult{Slot: child.Slot})) + drainOrderingEvents(t, v) + hash, ok := v.history.NotarizedVote(child.Slot) + require.True(t, ok) + require.Equal(t, child.Hash, hash) + require.NoError(t, e.AlpenglowSafetyError()) +} + +func TestAlpenglowVoterFinalityPreservesEarlierSkipDecision(t *testing.T) { + e, v := newOrderingTestVoter(t, true) + require.NoError(t, v.history.AddVote(alpenglow.NewSkipVote(40))) + id := observeOrderingBlock(t, e, 40) + finalizeOrderingBlock(t, e, v, id) + drainOrderingEvents(t, v) + require.NoError(t, e.OnReplayResult(context.Background(), SlotReplayResult{Slot: 40})) + drainOrderingEvents(t, v) + require.True(t, v.history.HasSkipped(40)) + _, ok := v.history.NotarizedVote(40) + require.False(t, ok, "late replay must never replace an earlier round-one decision") + require.NoError(t, e.AlpenglowSafetyError()) +} + +func TestReservedRecoveryUsesVerifiedFinalityNotLiveAdmissionFloor(t *testing.T) { + cfg := reservedTestConfig(t.TempDir()) + v, err := openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + reserveThrough(t, v.reservation, 40) + h := v.reservation.through.Load() + crashReservedTestVoter(t, v) + cfg.InitializeVoteReservation = false + v, err = openReservedTestVoter(t, cfg, 39) + require.NoError(t, err) + _, _, err = v.sign(alpenglow.NewSkipVote(h+1), false) + require.ErrorIs(t, err, errVoterNotReady) + id := observeOrderingBlock(t, v.engine, h) + finalizeOrderingBlock(t, v.engine, v, id) + require.Less(t, v.admissionFloor(), h) + require.Equal(t, h, v.engine.alpenglowVerifiedFinalityFloor()) + reserveThrough(t, v.reservation, h+1) + for _, normal := range []bool{false, true} { + _, _, err = v.sign(alpenglow.NewSkipVote(h), normal) + require.ErrorIs(t, err, errVoterNotReady) + _, _, err = v.sign(alpenglow.NewSkipVote(h+1), normal) + require.NoError(t, err) + } +} From 55ed3b90b1f8d22ed76fed972fa6bade6beb5042 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 23:12:41 -0500 Subject: [PATCH 16/22] Advance replay progress without waiting for certificate verification --- pkg/alpenglow/certpool.go | 22 +++-- pkg/alpenglow/certpool_progress_test.go | 112 ++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 pkg/alpenglow/certpool_progress_test.go diff --git a/pkg/alpenglow/certpool.go b/pkg/alpenglow/certpool.go index 60b1882e..50a5c595 100644 --- a/pkg/alpenglow/certpool.go +++ b/pkg/alpenglow/certpool.go @@ -6,6 +6,7 @@ import ( "fmt" "math/big" "sync" + "sync/atomic" bls12381 "github.com/Overclock-Validator/gnark-crypto/ecc/bls12-381" blsfr "github.com/Overclock-Validator/gnark-crypto/ecc/bls12-381/fr" @@ -171,8 +172,8 @@ type CertPool struct { slots map[uint64]*poolSlot emitted map[CertificateKey]struct{} floor uint64 - liveSlot uint64 // trusted replay/observed watermark (NOT advanced by raw votes) - highestSlot uint64 // observability only: highest vote slot seen + liveSlot atomic.Uint64 // trusted replay/observed watermark (NOT advanced by raw votes) + highestSlot uint64 // observability only: highest vote slot seen totalPending int equivocation []EquivocationEvidence snap CertPoolSnapshot @@ -236,21 +237,24 @@ func (p *CertPool) SetEpochLookup(fn func(slot uint64) uint64) { // (from replay progress / observed finality — never from raw votes, so an // attacker cannot slide the window forward). Monotonic. func (p *CertPool) NoteLiveSlot(slot uint64) { - p.mu.Lock() - if slot > p.liveSlot { - p.liveSlot = slot + // Replay must not wait for BLS verification under p.mu merely to announce + // progress. Concurrent trusted updates may arrive out of order. + for current := p.liveSlot.Load(); slot > current; current = p.liveSlot.Load() { + if p.liveSlot.CompareAndSwap(current, slot) { + return + } } - p.mu.Unlock() } // windowAnchorLocked is the trusted upper anchor of the live vote window: the // higher of the finalized floor and the replay-observed live slot. It is NOT // derived from raw votes, so ingest cannot advance it. func (p *CertPool) windowAnchorLocked() uint64 { - if p.floor > p.liveSlot { + liveSlot := p.liveSlot.Load() + if p.floor > liveSlot { return p.floor } - return p.liveSlot + return liveSlot } // setForSlotLocked resolves the validator set covering slot. Returns nil (votes @@ -560,7 +564,7 @@ func (p *CertPool) Snapshot() CertPoolSnapshot { snap.Slots = len(p.slots) snap.Floor = p.floor snap.HighestSlot = p.highestSlot - snap.LiveSlot = p.liveSlot + snap.LiveSlot = p.liveSlot.Load() snap.PendingTotal = p.totalPending return snap } diff --git a/pkg/alpenglow/certpool_progress_test.go b/pkg/alpenglow/certpool_progress_test.go new file mode 100644 index 00000000..6732dce4 --- /dev/null +++ b/pkg/alpenglow/certpool_progress_test.go @@ -0,0 +1,112 @@ +package alpenglow + +import ( + "sync" + "testing" + "time" +) + +func TestCertPoolProgressDoesNotWaitForVerificationLock(t *testing.T) { + pool := NewCertPool(DefaultCertPoolConfig(), NewCertificateVerifier(), nil) + pool.mu.Lock() // The same lock held while verifying incoming BLS votes. + done := make(chan struct{}) + go func() { + pool.NoteLiveSlot(123) + close(done) + }() + completed := false + select { + case <-done: + completed = true + case <-time.After(time.Second): + } + pool.mu.Unlock() + <-done + if !completed { + t.Fatal("trusted replay progress waited for the verification lock") + } + if got := pool.Snapshot().LiveSlot; got != 123 { + t.Fatalf("live slot = %d, want 123", got) + } +} + +func TestCertPoolConcurrentProgressRemainsMonotonic(t *testing.T) { + pool := NewCertPool(DefaultCertPoolConfig(), NewCertificateVerifier(), nil) + const workers, updates = 16, 256 + start := make(chan struct{}) + var wg sync.WaitGroup + for worker := range workers { + wg.Go(func() { + <-start + for n := range updates { + slot := uint64(n*workers + worker + 1) + pool.NoteLiveSlot(slot) + pool.NoteLiveSlot(slot / 2) // Stale updates race newer progress. + } + }) + } + finished := make(chan struct{}) + go func() { + wg.Wait() + close(finished) + }() + close(start) + var previous uint64 + for { + live := pool.Snapshot().LiveSlot + pool.mu.Lock() + anchor := pool.windowAnchorLocked() + pool.mu.Unlock() + if live < previous || anchor < live { + t.Errorf("progress regressed: previous=%d snapshot=%d anchor=%d", previous, live, anchor) + } + previous = anchor + select { + case <-finished: + pool.NoteLiveSlot(0) + pool.NoteLiveSlot(1) + if got := pool.Snapshot().LiveSlot; got != workers*updates { + t.Fatalf("live slot = %d, want %d", got, workers*updates) + } + return + default: + } + } +} + +func TestCertPoolProgressPreservesTrustedVoteWindow(t *testing.T) { + set, keys := testBLSValidatorSet(100, 40, 30, 15, 10, 5) + verifier := NewCertificateVerifier() + if err := verifier.SetValidatorSet(set); err != nil { + t.Fatal(err) + } + pool := NewCertPool(CertPoolConfig{MaxSlotsAhead: 10}, verifier, nil) + pool.SetEpochLookup(func(uint64) uint64 { return set.Epoch }) + pool.NoteLiveSlot(100) + checkVote := func(slot uint64, rejected bool, live uint64) { + t.Helper() + before := pool.Snapshot() + addVote(t, pool, NewSkipVote(slot), 4, keys[4]) + after := pool.Snapshot() + wantRejected := before.VotesRejected + if rejected { + wantRejected++ + } + if after.VotesRejected != wantRejected { + t.Fatalf("slot %d: rejected=%d, want %d", slot, after.VotesRejected, wantRejected) + } + if after.LiveSlot != live { + t.Fatalf("raw vote at %d moved trusted progress to %d, want %d", slot, after.LiveSlot, live) + } + } + checkVote(110, false, 100) // Inclusive upper edge. + checkVote(111, true, 100) // Accepted raw votes cannot slide the window. + pool.NoteLiveSlot(90) + checkVote(111, true, 100) + pool.NoteLiveSlot(101) + checkVote(111, false, 101) + pool.ObserveFloor(120) + checkVote(120, true, 101) // Finalized floor still rejects old votes. + checkVote(130, false, 101) // Floor can anchor the window above replay. + checkVote(131, true, 101) +} From 4a5a9761847a4fd8e7d60c72810082cf91fb73d5 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 23:12:41 -0500 Subject: [PATCH 17/22] Move transaction-status checkpoint encoding off the replay loop --- pkg/replay/async_checkpoint_capture_test.go | 172 ++++++++++++++++++ pkg/replay/async_promotion_test.go | 18 +- pkg/replay/block.go | 6 +- pkg/replay/promotion.go | 92 +++++++--- pkg/replay/transaction_status_cache.go | 54 +++++- .../transaction_status_capture_bench_test.go | 66 +++++++ pkg/replay/transaction_status_capture_test.go | 162 +++++++++++++++++ 7 files changed, 524 insertions(+), 46 deletions(-) create mode 100644 pkg/replay/async_checkpoint_capture_test.go create mode 100644 pkg/replay/transaction_status_capture_bench_test.go create mode 100644 pkg/replay/transaction_status_capture_test.go diff --git a/pkg/replay/async_checkpoint_capture_test.go b/pkg/replay/async_checkpoint_capture_test.go new file mode 100644 index 00000000..a556c5a3 --- /dev/null +++ b/pkg/replay/async_checkpoint_capture_test.go @@ -0,0 +1,172 @@ +package replay + +import ( + "encoding/json" + "errors" + "sync" + "testing" + "time" + + "github.com/Overclock-Validator/mithril/pkg/accounts" + "github.com/Overclock-Validator/mithril/pkg/state" + "github.com/stretchr/testify/require" +) + +type testCheckpointEncoder func() ([]byte, error) + +func (f testCheckpointEncoder) MarshalBinary() ([]byte, error) { return f() } + +func testCheckpointBytes(payload []byte) TransactionStatusSnapshot { + owned := append([]byte(nil), payload...) + return testCheckpointEncoder(func() ([]byte, error) { return append([]byte(nil), owned...), nil }) +} + +func TestAsyncCheckpointEncodingDoesNotRunDuringJobBuild(t *testing.T) { + fc := &fakeCommitter{durable: accounts.NewMemAccounts()} + tail := asyncTestTail(fc, 5, 6) + started, release := make(chan struct{}), make(chan struct{}) + blockEncoding := false + rootDir := t.TempDir() + require.NoError(t, tail.SetTransactionStatusCheckpointHooks(TransactionStatusCheckpointHooks{ + Capture: func(through uint64) (TransactionStatusSnapshot, error) { + require.Equal(t, uint64(6), through) + return testCheckpointEncoder(func() ([]byte, error) { + if !blockEncoding { + return nil, errors.New("encoder ran during job construction") + } + close(started) + <-release + return []byte("encoded-on-worker"), nil + }), nil + }, + Install: func(through uint64, payload []byte) (*state.TransactionStatusCheckpointRef, error) { + return PrepareTransactionStatusCheckpoint(rootDir, through, payload) + }, + })) + job, err := tail.buildFoldJob(6, false) + require.NoError(t, err) + select { + case <-started: + t.Fatal("job construction ran the encoder") + default: + } + promoter := newAsyncPromoter(fc) + // Always unblock the worker before draining it, including a failed assertion. + var releaseOnce sync.Once + unblock := func() { releaseOnce.Do(func() { close(release) }) } + defer promoter.stop() + defer unblock() + blockEncoding = true + promoter.enqueue(job) + select { + case <-started: + case <-time.After(2 * time.Second): + t.Fatal("checkpoint worker did not start encoding") + } + // Replay can publish a later bank while the worker's encoder is blocked. + tail.Add(7, []*accounts.Account{testAccount(3, 7)}, testHashBytes(7)) + tail.SetContext(7, &state.ResumeContext{Slot: 7}) + require.Equal(t, 3, tail.overlay.HeldSlots()) + require.Nil(t, promoter.poll()) + + unblock() + result := promoter.drain() + require.NotNil(t, result) + require.NoError(t, result.err) + require.Nil(t, result.job.transactionStatusSnapshot) + tail.applyFoldJob(result.job) + require.Equal(t, 1, tail.overlay.HeldSlots()) +} + +func TestCheckpointCaptureFailureOrdering(t *testing.T) { + cases := []struct { + name string + capture func(uint64) (TransactionStatusSnapshot, error) + want string + buildFails bool + }{ + {"nil capture", func(uint64) (TransactionStatusSnapshot, error) { return nil, nil }, "capture is nil", true}, + {"capture error", func(uint64) (TransactionStatusSnapshot, error) { return nil, errors.New("capture failed") }, "capture failed", true}, + {"encode error", func(uint64) (TransactionStatusSnapshot, error) { + return testCheckpointEncoder(func() ([]byte, error) { return nil, errors.New("encode failed") }), nil + }, "encode failed", false}, + {"empty encoding", func(uint64) (TransactionStatusSnapshot, error) { return testCheckpointBytes(nil), nil }, "snapshot is empty", false}, + } + for _, tc := range cases { + for _, forced := range []bool{false, true} { + name := tc.name + "/async" + if forced { + name = tc.name + "/forced" + } + t.Run(name, func(t *testing.T) { + fc := &fakeCommitter{durable: accounts.NewMemAccounts()} + tail := asyncTestTail(fc, 5, 6) + installed := false + require.NoError(t, tail.SetTransactionStatusCheckpointHooks(TransactionStatusCheckpointHooks{ + Capture: tc.capture, + Install: func(uint64, []byte) (*state.TransactionStatusCheckpointRef, error) { + installed = true + return nil, errors.New("unexpected install") + }, + })) + if forced { + through, _, err := tail.flush(6) + require.ErrorContains(t, err, tc.want) + require.Zero(t, through) + } else { + job, err := tail.buildFoldJob(6, false) + if tc.buildFails { + require.ErrorContains(t, err, tc.want) + require.Nil(t, job) + } else { + require.NoError(t, err) + require.ErrorContains(t, runFoldJob(fc, job), tc.want) + require.Nil(t, job.transactionStatusSnapshot, "failed result retained its captured deltas") + } + } + require.False(t, installed) + require.Empty(t, fc.throughs) + require.Equal(t, 2, tail.overlay.HeldSlots()) + }) + } + } +} + +func TestFoldCheckpointKeepsCapturedRootAfterLiveCacheAdvances(t *testing.T) { + c := importedStatusCacheForTest(t) + for slot := uint64(301); slot <= 350; slot++ { + require.NoError(t, c.CommitBlock(captureTestBlock(slot, 1))) + } + want, err := legacyStatusSnapshotForTest(c, 320) + require.NoError(t, err) + fc := &fakeCommitter{durable: accounts.NewMemAccounts()} + tail := asyncTestTail(fc, 319, 320, 321) + rootDir := t.TempDir() + require.NoError(t, tail.SetTransactionStatusCheckpointHooks(TransactionStatusCheckpointHooks{ + Capture: c.CaptureSnapshotThrough, + Install: func(through uint64, payload []byte) (*state.TransactionStatusCheckpointRef, error) { + return PrepareTransactionStatusCheckpoint(rootDir, through, payload) + }, + })) + job, err := tail.buildFoldJob(321, false) + require.NoError(t, err) + for slot := uint64(351); slot <= 660; slot++ { + require.NoError(t, c.CommitBlock(captureTestBlock(slot, 1))) + c.Root(slot - 20) + } + require.NoError(t, runFoldJob(fc, job)) + require.Nil(t, job.transactionStatusSnapshot, "completed result retained captured deltas") + require.Positive(t, job.checkpointCaptureTime) + require.Positive(t, job.checkpointEncodeTime) + require.Equal(t, len(want), job.checkpointBytes) + var manifest state.ResumeContext + require.NoError(t, json.Unmarshal(fc.ctxs[320], &manifest)) + require.Equal(t, uint64(320), manifest.TransactionStatusCheckpoint.Root) + got, err := ReadTransactionStatusCheckpoint(rootDir, manifest.TransactionStatusCheckpoint) + require.NoError(t, err) + require.Equal(t, want, got) + restored, err := NewTransactionStatusCacheFromSnapshot(got) + require.NoError(t, err) + require.Equal(t, uint64(320), restored.RootedThrough()) + require.True(t, restored.CoverageComplete()) +} diff --git a/pkg/replay/async_promotion_test.go b/pkg/replay/async_promotion_test.go index 49d47a49..eff0de20 100644 --- a/pkg/replay/async_promotion_test.go +++ b/pkg/replay/async_promotion_test.go @@ -22,7 +22,7 @@ type slowCommitter struct { delay time.Duration } -func TestFoldJobSnapshotsStatusOnLoopAndReferenceRidesManifest(t *testing.T) { +func TestFoldJobCapturesStatusOnLoopAndReferenceRidesManifest(t *testing.T) { rootDir := t.TempDir() fc := &fakeCommitter{durable: accounts.NewMemAccounts()} tail := asyncTestTail(fc, 5, 6, 7) @@ -32,10 +32,10 @@ func TestFoldJobSnapshotsStatusOnLoopAndReferenceRidesManifest(t *testing.T) { installCalled := false afterCommitCalled := false hooks := TransactionStatusCheckpointHooks{ - Snapshot: func(through uint64) ([]byte, error) { + Capture: func(through uint64) (TransactionStatusSnapshot, error) { require.Equal(t, uint64(6), through) snapshotCalled = true - return scratch, nil + return testCheckpointBytes(scratch), nil }, Install: func(through uint64, payload []byte) (*state.TransactionStatusCheckpointRef, error) { require.True(t, snapshotCalled, "worker install ran before loop snapshot") @@ -83,7 +83,7 @@ func TestFoldJobCheckpointFailuresCannotReachCommitBatch(t *testing.T) { fc := &fakeCommitter{durable: accounts.NewMemAccounts()} tail := asyncTestTail(fc, 5, 6) require.NoError(t, tail.SetTransactionStatusCheckpointHooks(TransactionStatusCheckpointHooks{ - Snapshot: func(uint64) ([]byte, error) { return nil, errors.New("snapshot boom") }, + Capture: func(uint64) (TransactionStatusSnapshot, error) { return nil, errors.New("snapshot boom") }, Install: func(uint64, []byte) (*state.TransactionStatusCheckpointRef, error) { t.Fatal("install must not run") return nil, nil @@ -101,7 +101,7 @@ func TestFoldJobCheckpointFailuresCannotReachCommitBatch(t *testing.T) { fc := &fakeCommitter{durable: accounts.NewMemAccounts()} tail := asyncTestTail(fc, 5, 6) require.NoError(t, tail.SetTransactionStatusCheckpointHooks(TransactionStatusCheckpointHooks{ - Snapshot: func(uint64) ([]byte, error) { return []byte("captured"), nil }, + Capture: func(uint64) (TransactionStatusSnapshot, error) { return testCheckpointBytes([]byte("captured")), nil }, Install: func(uint64, []byte) (*state.TransactionStatusCheckpointRef, error) { return nil, errors.New("fsync boom") }, @@ -120,7 +120,7 @@ func TestFoldJobCheckpointFailuresCannotReachCommitBatch(t *testing.T) { tail := asyncTestTail(fc, 5, 6) afterCommitCalled := false require.NoError(t, tail.SetTransactionStatusCheckpointHooks(TransactionStatusCheckpointHooks{ - Snapshot: func(uint64) ([]byte, error) { return []byte("captured"), nil }, + Capture: func(uint64) (TransactionStatusSnapshot, error) { return testCheckpointBytes([]byte("captured")), nil }, Install: func(through uint64, payload []byte) (*state.TransactionStatusCheckpointRef, error) { return PrepareTransactionStatusCheckpoint(rootDir, through, payload) }, @@ -144,9 +144,9 @@ func TestForcedFoldCarriesStatusCheckpointReference(t *testing.T) { tail := asyncTestTail(fc, 5) afterCommitCalled := false require.NoError(t, tail.SetTransactionStatusCheckpointHooks(TransactionStatusCheckpointHooks{ - Snapshot: func(through uint64) ([]byte, error) { + Capture: func(through uint64) (TransactionStatusSnapshot, error) { require.Equal(t, uint64(5), through) - return []byte("forced-partial-status"), nil + return testCheckpointBytes([]byte("forced-partial-status")), nil }, Install: func(through uint64, payload []byte) (*state.TransactionStatusCheckpointRef, error) { return PrepareTransactionStatusCheckpoint(rootDir, through, payload) @@ -175,7 +175,7 @@ func TestCheckpointAfterCommitRequiresDurabilityHooks(t *testing.T) { err := tail.SetTransactionStatusCheckpointHooks(TransactionStatusCheckpointHooks{ AfterCommit: func(*state.TransactionStatusCheckpointRef) error { return nil }, }) - require.ErrorContains(t, err, "requires Snapshot and Install") + require.ErrorContains(t, err, "requires Capture and Install") } func (c *slowCommitter) CommitBatch(deltas []accounts.SlotDelta, throughSlot uint64, bankhashes map[uint64][32]byte, resumeCtx []byte) (accountsdb.BatchCommitResult, error) { diff --git a/pkg/replay/block.go b/pkg/replay/block.go index ce618a4d..e36ecd51 100644 --- a/pkg/replay/block.go +++ b/pkg/replay/block.go @@ -1987,9 +1987,9 @@ func ReplayBlocks( checkpointAfterCommit = consensusOpts.TransactionStatusCheckpointAfterCommit } if hookErr := unrootedTailState.SetTransactionStatusCheckpointHooks(TransactionStatusCheckpointHooks{ - // Snapshot runs here on the replay loop during fold-job construction; - // only its immutable bytes cross to the async worker. - Snapshot: transactionStatuses.SnapshotThrough, + // Pin the exact immutable view on replay. Sorting and encoding run + // on the existing fold worker, after releasing the live cache lock. + Capture: transactionStatuses.CaptureSnapshotThrough, Install: func(through uint64, payload []byte) (*state.TransactionStatusCheckpointRef, error) { return PrepareTransactionStatusCheckpoint(acctsDbPath, through, payload) }, diff --git a/pkg/replay/promotion.go b/pkg/replay/promotion.go index c750009e..752e0cdd 100644 --- a/pkg/replay/promotion.go +++ b/pkg/replay/promotion.go @@ -27,14 +27,13 @@ type batchCommitter interface { CommitBatch(deltas []accounts.SlotDelta, throughSlot uint64, bankhashes map[uint64][32]byte, resumeCtx []byte) (accountsdb.BatchCommitResult, error) } -// TransactionStatusCheckpointHooks deliberately split status-cache capture -// from sidecar I/O. Snapshot runs on the replay loop while its mutable cache is -// coherent; Install runs on the fold worker using only those immutable bytes. -// This makes it impossible for the async worker to traverse concurrently -// changing replay lineage. The later AccountsDB manifest remains the selector. +// TransactionStatusCheckpointHooks split immutable status capture from encoding +// and sidecar I/O. Capture runs on replay; the fold worker serializes the captured +// view and then calls Install. Neither worker operation revisits live lineage. +// The later AccountsDB manifest remains the durable checkpoint selector. type TransactionStatusCheckpointHooks struct { - Snapshot func(through uint64) ([]byte, error) - Install func(through uint64, payload []byte) (*state.TransactionStatusCheckpointRef, error) + Capture func(through uint64) (TransactionStatusSnapshot, error) + Install func(through uint64, payload []byte) (*state.TransactionStatusCheckpointRef, error) // AfterCommit is an advisory retention hook. It runs only after CommitBatch // has durably selected the manifest carrying selected. Its error is logged // and ignored: once CommitBatch succeeds, the fold must remain successful. @@ -378,7 +377,10 @@ type foldJob struct { ctx *state.ResumeContext ctxJSON []byte stakeIdxDir string - transactionStatusCheckpointPayload []byte + transactionStatusSnapshot TransactionStatusSnapshot + checkpointCaptureTime time.Duration + checkpointEncodeTime time.Duration + checkpointBytes int installTransactionStatusCheckpoint func(through uint64, payload []byte) (*state.TransactionStatusCheckpointRef, error) afterTransactionStatusCheckpointCommit func(selected *state.TransactionStatusCheckpointRef) error } @@ -415,18 +417,18 @@ func (t *unrootedTail) buildFoldJob(through uint64, force bool, hookOverrides .. return nil, fmt.Errorf("fold chunk through slot %d: no resume context recorded for chunk-top slot", through) } ctx = cloneResumeContextForFold(ctx) - var checkpointPayload []byte - if hooks.Snapshot != nil { - checkpointPayload, err = hooks.Snapshot(through) + var snapshot TransactionStatusSnapshot + var captureTime time.Duration + if hooks.Capture != nil { + start := time.Now() + snapshot, err = hooks.Capture(through) + captureTime = time.Since(start) if err != nil { - return nil, fmt.Errorf("fold chunk through slot %d: snapshot transaction status checkpoint: %w", through, err) + return nil, fmt.Errorf("fold chunk through slot %d: capture transaction status checkpoint: %w", through, err) } - if len(checkpointPayload) == 0 { - return nil, fmt.Errorf("fold chunk through slot %d: transaction status checkpoint snapshot is empty", through) + if snapshot == nil { + return nil, fmt.Errorf("fold chunk through slot %d: transaction status checkpoint capture is nil", through) } - // The worker owns this immutable copy. Even a future Snapshot - // implementation that reuses a scratch buffer cannot race it. - checkpointPayload = append([]byte(nil), checkpointPayload...) } bankhashes := make(map[uint64][32]byte, len(chunk)) for _, sd := range chunk { @@ -440,7 +442,8 @@ func (t *unrootedTail) buildFoldJob(through uint64, force bool, hookOverrides .. bankhashes: bankhashes, ctx: ctx, stakeIdxDir: t.stakeIdxDir, - transactionStatusCheckpointPayload: checkpointPayload, + transactionStatusSnapshot: snapshot, + checkpointCaptureTime: captureTime, installTransactionStatusCheckpoint: hooks.Install, afterTransactionStatusCheckpointCommit: hooks.AfterCommit, }, nil @@ -450,12 +453,26 @@ func (t *unrootedTail) buildFoldJob(through uint64, force bool, hookOverrides .. // state). Stake-index entries flush (fsync'd) BEFORE the batch commit — see // promoteRootedBatched for why that order is a correctness requirement. func runFoldJob(committer batchCommitter, job *foldJob) error { - if job == nil || job.ctx == nil { + if job == nil { + return errors.New("fold job has no resume context") + } + // Failed folds are rebuilt from the retained tail. Neither a failed result + // nor a completed-but-unapplied job should keep checkpoint deltas alive. + defer func() { job.transactionStatusSnapshot = nil }() + if job.ctx == nil { return errors.New("fold job has no resume context") } var selectedCheckpoint *state.TransactionStatusCheckpointRef if job.installTransactionStatusCheckpoint != nil { - ref, err := job.installTransactionStatusCheckpoint(job.through, job.transactionStatusCheckpointPayload) + start := time.Now() + payload, err := encodeTransactionStatusCheckpoint(job.transactionStatusSnapshot) + job.checkpointEncodeTime = time.Since(start) + job.transactionStatusSnapshot = nil + if err != nil { + return fmt.Errorf("fold chunk through slot %d: encode transaction status checkpoint: %w", job.through, err) + } + job.checkpointBytes = len(payload) + ref, err := job.installTransactionStatusCheckpoint(job.through, payload) if err != nil { return fmt.Errorf("fold chunk through slot %d: prepare transaction status checkpoint: %w", job.through, err) } @@ -539,7 +556,9 @@ func (p *asyncPromoter) run() { start := time.Now() err := runFoldJob(p.committer, job) if err == nil { - mlog.Log.FileOnlyf("async fold: committed %d slots through %d in %s", len(job.chunk), job.through, time.Since(start).Round(time.Millisecond)) + mlog.Log.FileOnlyf("async fold: committed %d slots through %d in %s checkpoint_capture=%s checkpoint_encode=%s checkpoint_bytes=%d", + len(job.chunk), job.through, time.Since(start).Round(time.Millisecond), + job.checkpointCaptureTime, job.checkpointEncodeTime, job.checkpointBytes) } p.results <- foldResult{job: job, err: err} } @@ -713,14 +732,15 @@ func promoteRootedBatched( } ctx = cloneResumeContextForFold(ctx) var selectedCheckpoint *state.TransactionStatusCheckpointRef - if hooks.Snapshot != nil { - payload, serr := hooks.Snapshot(chunkThrough) + if hooks.Capture != nil { + snapshot, serr := hooks.Capture(chunkThrough) if serr != nil { - err = fmt.Errorf("promote chunk through slot %d: snapshot transaction status checkpoint: %w", chunkThrough, serr) + err = fmt.Errorf("promote chunk through slot %d: capture transaction status checkpoint: %w", chunkThrough, serr) break } - if len(payload) == 0 { - err = fmt.Errorf("promote chunk through slot %d: transaction status checkpoint snapshot is empty", chunkThrough) + payload, serr := encodeTransactionStatusCheckpoint(snapshot) + if serr != nil { + err = fmt.Errorf("promote chunk through slot %d: encode transaction status checkpoint: %w", chunkThrough, serr) break } ref, perr := hooks.Install(chunkThrough, payload) @@ -792,15 +812,29 @@ func resolveTransactionStatusCheckpointHooks(configured TransactionStatusCheckpo } func validateTransactionStatusCheckpointHooks(hooks TransactionStatusCheckpointHooks) error { - if (hooks.Snapshot == nil) != (hooks.Install == nil) { - return errors.New("transaction status checkpoint Snapshot and Install hooks must either both be set or both be nil") + if (hooks.Capture == nil) != (hooks.Install == nil) { + return errors.New("transaction status checkpoint Capture and Install hooks must either both be set or both be nil") } if hooks.AfterCommit != nil && hooks.Install == nil { - return errors.New("transaction status checkpoint AfterCommit hook requires Snapshot and Install hooks") + return errors.New("transaction status checkpoint AfterCommit hook requires Capture and Install hooks") } return nil } +func encodeTransactionStatusCheckpoint(snapshot TransactionStatusSnapshot) ([]byte, error) { + if snapshot == nil { + return nil, errors.New("transaction status checkpoint capture is nil") + } + payload, err := snapshot.MarshalBinary() + if err != nil { + return nil, err + } + if len(payload) == 0 { + return nil, errors.New("transaction status checkpoint snapshot is empty") + } + return payload, nil +} + func cloneResumeContextForFold(ctx *state.ResumeContext) *state.ResumeContext { if ctx == nil { return nil diff --git a/pkg/replay/transaction_status_cache.go b/pkg/replay/transaction_status_cache.go index 8320df05..03437ba0 100644 --- a/pkg/replay/transaction_status_cache.go +++ b/pkg/replay/transaction_status_cache.go @@ -681,10 +681,32 @@ func (c *TransactionStatusCache) Root(through uint64) bool { return !wasComplete && c.coverageComplete } -// SnapshotThrough serializes only the rooted lineage needed at through. It is -// called while constructing a fold job, so the blob rides in that exact durable -// manifest without being copied into every speculative ResumeContext. -func (c *TransactionStatusCache) SnapshotThrough(through uint64) ([]byte, error) { +// TransactionStatusSnapshot pins an immutable checkpoint view. MarshalBinary +// must use only captured data, without locking or revisiting the live cache, +// and return an owned payload. It can run on the checkpoint worker while replay +// commits, roots, or unwinds its current lineage. +type TransactionStatusSnapshot interface { + MarshalBinary() ([]byte, error) +} + +type transactionStatusSnapshot struct { + nodes []*transactionStatusNode + rootedSinceSeed uint16 + complete bool + coverageFromGenesis bool +} + +func (s *transactionStatusSnapshot) MarshalBinary() ([]byte, error) { + if s == nil { + return nil, nil + } + return marshalTransactionStatusNodes(s.nodes, s.rootedSinceSeed, s.complete, s.coverageFromGenesis) +} + +// CaptureSnapshotThrough selects the exact checkpoint lineage and coverage on +// replay, but leaves transaction-key sorting and serialization to the worker. +// Published deltas are immutable; only small node headers are copied here. +func (c *TransactionStatusCache) CaptureSnapshotThrough(through uint64) (TransactionStatusSnapshot, error) { if c == nil { return nil, nil } @@ -700,7 +722,29 @@ func (c *TransactionStatusCache) SnapshotThrough(through uint64) ([]byte, error) if rootedSinceSeed > maxTransactionStatusRoots { rootedSinceSeed = maxTransactionStatusRoots } - return marshalTransactionStatusNodes(nodes, uint16(rootedSinceSeed), complete, c.coverageFromGenesis) + owned := make([]transactionStatusNode, len(nodes)) + pinned := make([]*transactionStatusNode, len(nodes)) + for i, node := range nodes { + owned[i] = *node + // The encoder consumes only these node deltas. Do not keep the old + // parent chain, which could retain roots excluded from this snapshot. + owned[i].parent = nil + pinned[i] = &owned[i] + } + return &transactionStatusSnapshot{ + nodes: pinned, rootedSinceSeed: uint16(rootedSinceSeed), complete: complete, + coverageFromGenesis: c.coverageFromGenesis, + }, nil +} + +// SnapshotThrough is the synchronous convenience API. Serialization still +// happens after releasing the cache lock; normal folds use CaptureSnapshotThrough. +func (c *TransactionStatusCache) SnapshotThrough(through uint64) ([]byte, error) { + snapshot, err := c.CaptureSnapshotThrough(through) + if err != nil || snapshot == nil { + return nil, err + } + return snapshot.MarshalBinary() } func (c *TransactionStatusCache) processedSlotLocked(blockhash solana.Hash, key transactionStatusKey) uint64 { diff --git a/pkg/replay/transaction_status_capture_bench_test.go b/pkg/replay/transaction_status_capture_bench_test.go new file mode 100644 index 00000000..64bf7a7e --- /dev/null +++ b/pkg/replay/transaction_status_capture_bench_test.go @@ -0,0 +1,66 @@ +package replay + +import ( + "crypto/sha256" + "encoding/binary" + "testing" + + "github.com/gagliardetto/solana-go" +) + +var checkpointBenchmarkPayload []byte +var checkpointBenchmarkCapture TransactionStatusSnapshot + +func BenchmarkTransactionStatusCheckpointCapture(b *testing.B) { + // A private, not-yet-published fixture with the same complete 300-root + // metadata as an imported cache. 1.5 million keys encode to roughly 30 MB. + c := newTransactionStatusCache(true) + c.coverageFromGenesis = false + c.rootedSinceSeed = maxTransactionStatusRoots + c.rootedThrough = maxTransactionStatusRoots + for slot := uint64(1); slot <= maxTransactionStatusRoots; slot++ { + keys := make(map[transactionStatusKey]struct{}, 5000) + var seed [16]byte + binary.LittleEndian.PutUint64(seed[:8], slot) + for i := uint64(0); i < 5000; i++ { + binary.LittleEndian.PutUint64(seed[8:], i) + hash := sha256.Sum256(seed[:]) + var key transactionStatusKey + copy(key[:], hash[:]) + keys[key] = struct{}{} + } + c.tip = &transactionStatusNode{slot: slot, parent: c.tip, + delta: transactionStatusDelta{solana.Hash{1}: {keyIndex: 7, keys: keys}}} + } + view, err := c.CaptureSnapshotThrough(maxTransactionStatusRoots) + if err != nil { + b.Fatal(err) + } + b.Run("SynchronousBaseline", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + checkpointBenchmarkPayload, err = legacyStatusSnapshotForTest(c, maxTransactionStatusRoots) + if err != nil { + b.Fatal(err) + } + } + }) + b.Run("CaptureOnReplay", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + checkpointBenchmarkCapture, err = c.CaptureSnapshotThrough(maxTransactionStatusRoots) + if err != nil { + b.Fatal(err) + } + } + }) + b.Run("EncodeOnWorker", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + checkpointBenchmarkPayload, err = view.MarshalBinary() + if err != nil { + b.Fatal(err) + } + } + }) +} diff --git a/pkg/replay/transaction_status_capture_test.go b/pkg/replay/transaction_status_capture_test.go new file mode 100644 index 00000000..755fa9df --- /dev/null +++ b/pkg/replay/transaction_status_capture_test.go @@ -0,0 +1,162 @@ +package replay + +import ( + "encoding/binary" + "sync" + "testing" + "time" + + b "github.com/Overclock-Validator/mithril/pkg/block" + "github.com/Overclock-Validator/mithril/pkg/txstatus" + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +// Keep the pre-split selection/metadata calculation as a differential oracle. +// The wire encoder itself did not change. +func legacyStatusSnapshotForTest(c *TransactionStatusCache, through uint64) ([]byte, error) { + c.mu.RLock() + defer c.mu.RUnlock() + nodes := c.nodesThroughLocked(through) + if len(nodes) > maxTransactionStatusRoots { + nodes = nodes[len(nodes)-maxTransactionStatusRoots:] + } + rooted := uint32(c.rootedSinceSeed) + uint32(c.countNodesBetweenLocked(c.rootedThrough, through)) + complete := c.coverageComplete || rooted >= maxTransactionStatusRoots + if rooted > maxTransactionStatusRoots { + rooted = maxTransactionStatusRoots + } + return marshalTransactionStatusNodes(nodes, uint16(rooted), complete, c.coverageFromGenesis) +} + +func importedStatusCacheForTest(t *testing.T) *TransactionStatusCache { + t.Helper() + roots := make([]txstatus.SnapshotSlotDelta, maxTransactionStatusRoots) + for i := range roots { + roots[i] = txstatus.SnapshotSlotDelta{Slot: uint64(i + 1), IsRoot: true} + } + c, err := NewTransactionStatusCacheFromAgaveSnapshot(roots, maxTransactionStatusRoots) + require.NoError(t, err) + return c +} + +func captureTestBlock(slot uint64, branch byte) *b.Block { + tx := statusCacheTestTransaction(1, 2, branch) + data := make([]byte, 9) + binary.LittleEndian.PutUint64(data, slot) + data[8] = branch + tx.Message.Instructions[0].Data = data + return statusCacheTestBlock(slot, tx) +} + +func TestTransactionStatusCaptureSurvivesConcurrentPruneAndUnwind(t *testing.T) { + c := importedStatusCacheForTest(t) + for slot := uint64(301); slot <= 350; slot++ { + require.NoError(t, c.CommitBlock(captureTestBlock(slot, 1))) + } + want, err := legacyStatusSnapshotForTest(c, 320) + require.NoError(t, err) + captured, err := c.CaptureSnapshotThrough(320) + require.NoError(t, err) + view := captured.(*transactionStatusSnapshot) + require.Len(t, view.nodes, maxTransactionStatusRoots) + require.Equal(t, uint64(21), view.nodes[0].slot) + require.Equal(t, uint64(320), view.nodes[len(view.nodes)-1].slot) + for _, node := range view.nodes { + require.Nil(t, node.parent, "capture retained excluded ancestry") + } + + var wg sync.WaitGroup + wg.Add(1) + errs := make(chan error, 1) + go func() { + defer wg.Done() + for slot := uint64(351); slot <= 750; slot++ { + if err := c.CommitBlock(captureTestBlock(slot, 1)); err != nil { + errs <- err + return + } + c.Root(slot - 20) + } + if err := c.Unwind(741); err != nil { + errs <- err + return + } + for slot := uint64(741); slot <= 755; slot++ { + if err := c.CommitBlock(captureTestBlock(slot, 2)); err != nil { + errs <- err + return + } + } + }() + for i := 0; i < 50; i++ { + got, err := captured.MarshalBinary() + if err != nil || string(want) != string(got) { + t.Errorf("captured bytes changed during replay: %v", err) + break + } + } + wg.Wait() + close(errs) + for err := range errs { + require.NoError(t, err) + } + got, err := captured.MarshalBinary() + require.NoError(t, err) + require.Equal(t, want, got) + restored, err := NewTransactionStatusCacheFromSnapshot(got) + require.NoError(t, err) + require.Equal(t, uint64(320), restored.RootedThrough()) + require.True(t, restored.CoverageComplete()) + retry := statusCacheTestBlock(321, captureTestBlock(301, 1).Transactions[0]) + require.Error(t, restored.ValidateBlock(retry), "captured ancestor was forgotten") + // A bank after the capture's through-slot must not leak into recovery. + future := statusCacheTestBlock(321, captureTestBlock(350, 1).Transactions[0]) + require.NoError(t, restored.ValidateBlock(future)) +} + +func TestTransactionStatusCapturePreservesCoverageAndOwnedBytes(t *testing.T) { + for _, complete := range []bool{false, true} { + c := newTransactionStatusCache(complete) + // Exercise metadata selection without changing its pre-existing rules. + for slot := uint64(1); slot <= 310; slot++ { + c.tip = &transactionStatusNode{slot: slot, parent: c.tip, + delta: transactionStatusDelta{solana.Hash{1}: {keyIndex: 7, keys: map[transactionStatusKey]struct{}{{byte(slot), byte(slot >> 8)}: {}}}}} + } + for _, through := range []uint64{0, 1, 299, 300, 310, 400} { + want, err := legacyStatusSnapshotForTest(c, through) + require.NoError(t, err) + view, err := c.CaptureSnapshotThrough(through) + require.NoError(t, err) + got, err := view.MarshalBinary() + require.NoError(t, err) + require.Equal(t, want, got) + got[0] ^= 0xff + again, err := view.MarshalBinary() + require.NoError(t, err) + require.Equal(t, want, again, "caller mutated the captured data through encoded bytes") + } + } + var absent *TransactionStatusCache + view, err := absent.CaptureSnapshotThrough(1) + require.NoError(t, err) + require.Nil(t, view) +} + +func TestTransactionStatusCaptureEncodingDoesNotLockLiveCache(t *testing.T) { + c := importedStatusCacheForTest(t) + require.NoError(t, c.CommitBlock(captureTestBlock(301, 1))) + view, err := c.CaptureSnapshotThrough(301) + require.NoError(t, err) + c.mu.Lock() + done := make(chan error, 1) + go func() { _, err := view.MarshalBinary(); done <- err }() + select { + case err := <-done: + c.mu.Unlock() + require.NoError(t, err) + case <-time.After(2 * time.Second): + c.mu.Unlock() + t.Fatal("checkpoint encoding waited for the live cache lock") + } +} From f03c60ecbad2dea745cc1f138fa8a8b33bf4aeb8 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 23:23:46 -0500 Subject: [PATCH 18/22] Document consolidated validator review, benchmarks and validation --- .gitattributes | 7 + docs/alpenglow_validator_performance.md | 124 ++ .../2026-09-14/README.md | 126 ++ .../baseline-failure-comparison.json | 26 + .../baseline-native-sealevel-race.log | 330 +++++ .../2026-09-14/baseline-sealevel-race.log | 330 +++++ .../2026-09-14/checkpoint-live.json | 118 ++ .../checkpoint-native-benchmark.log | 15 + .../2026-09-14/checkpoint-probe-sanity.json | 1302 +++++++++++++++++ .../2026-09-14/local-race.log | 347 +++++ .../2026-09-14/native-build.log | 0 .../2026-09-14/native-race.log | 347 +++++ .../2026-09-14/native-vet.log | 0 .../post-deploy-voting-summary.json | 91 ++ .../2026-09-14/tested-code-commit.txt | 1 + .../2026-09-14/validate-native.py | 43 + .../2026-09-14/validation.json | 74 + 17 files changed, 3281 insertions(+) create mode 100644 .gitattributes create mode 100644 docs/alpenglow_validator_performance.md create mode 100644 docs/results/validator-performance/2026-09-14/README.md create mode 100644 docs/results/validator-performance/2026-09-14/baseline-failure-comparison.json create mode 100644 docs/results/validator-performance/2026-09-14/baseline-native-sealevel-race.log create mode 100644 docs/results/validator-performance/2026-09-14/baseline-sealevel-race.log create mode 100644 docs/results/validator-performance/2026-09-14/checkpoint-live.json create mode 100644 docs/results/validator-performance/2026-09-14/checkpoint-native-benchmark.log create mode 100644 docs/results/validator-performance/2026-09-14/checkpoint-probe-sanity.json create mode 100644 docs/results/validator-performance/2026-09-14/local-race.log create mode 100644 docs/results/validator-performance/2026-09-14/native-build.log create mode 100644 docs/results/validator-performance/2026-09-14/native-race.log create mode 100644 docs/results/validator-performance/2026-09-14/native-vet.log create mode 100644 docs/results/validator-performance/2026-09-14/post-deploy-voting-summary.json create mode 100644 docs/results/validator-performance/2026-09-14/tested-code-commit.txt create mode 100644 docs/results/validator-performance/2026-09-14/validate-native.py create mode 100644 docs/results/validator-performance/2026-09-14/validation.json diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..d6ca540f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# Keep raw benchmark evidence available without overwhelming the review. +# Go's CPU-identification line may include trailing padding; retain raw bytes. +docs/results/**/*.json linguist-generated=true +docs/results/**/*.jsonl linguist-generated=true +docs/results/**/*.txt linguist-generated=true whitespace=-blank-at-eol +docs/results/**/*.log linguist-generated=true whitespace=-blank-at-eol +docs/results/**/*.tar.gz linguist-generated=true diff --git a/docs/alpenglow_validator_performance.md b/docs/alpenglow_validator_performance.md new file mode 100644 index 00000000..f81b8d4e --- /dev/null +++ b/docs/alpenglow_validator_performance.md @@ -0,0 +1,124 @@ +# Alpenglow validator performance and voting recovery + +This change consolidates streaming signature verification, leader block packing, +certificate compatibility, voting persistence and checkpoint latency work on +`alpenglow-dev` at `33dde4050d9250557583395810799aaac2f54017`. +The aim is to spend less of each slot preparing completed blocks or waiting to +enqueue a vote, while keeping the signing and durable-state boundaries explicit. + +## Review order + +The commits preserve the implementation sequence and can be reviewed by subsystem: + +1. **Streaming verification:** update Narya; verify completed entry components + during shred arrival; dispatch small ready batches immediately and bundle + larger requests; compare cached component bytes directly against shreds; + reuse authenticated FEC-root inputs and avoid repeated ordering work. + [Design and configuration](transaction_sigverify_streaming.md). +2. **Leader packing:** decode and statically prepare owned transactions before + leadership, revalidate bank-dependent inputs, reduce scratch allocation, + compute signature roots without unused proof nodes, and improve scheduler + ordering. Expose queue capacity and completion-reserve settings while keeping + their defaults. Correct slot-duration-dependent resource budgets. + [Design and synthetic block tests](leader_block_packing.md). +3. **Observer and certificate compatibility:** enable existing safe VM pooling + defaults, reduce observer statistics overhead, and use the Votor-compatible + certificate layout accepted by Agave/Firedancer. The regression fixture is + an X.509 certificate, with no private signing key. +4. **Voting persistence and recovery:** optional durable signing reservations, + operator `--wait-to-vote-slot`, one ordered background history writer, clean + shutdown sealing, and conservative recovery after a crash. Default history + persistence remains synchronous. [Storage contract](reserved-vote-history.md). +5. **Vote admission and progress:** retain valid live voting opportunities within + the existing pool/history window, order durable pruning behind replay events, + and update the trusted monotonic replay watermark without taking the + certificate-verification mutex. Verified finality still gates crash recovery. +6. **Status checkpoint capture:** select and pin the exact immutable transaction + status lineage on replay; serialize it on the existing fold worker. Sidecar + installation, checkpoint format, manifest selection and commit ordering are + preserved. Capture/encoding failures cannot publish a new durable checkpoint. + +This PR does not absorb FEC acceleration PR #259, genesis bootstrap PR #276, +or the skipped-parent replay fix in PR #278. The small DATA_COMPLETE boundary +correction shared with #259 is included because the streaming implementation +must identify actual serialized-component boundaries. Tests exercise the public +generator without depending on accelerated FEC APIs. The earlier standalone +streaming and leader branches are represented here rather than opened as +duplicate PRs. + +## Performance evidence + +These measurements have different scopes and must not be combined into a +whole-validator speedup multiplier. + +| Measurement | Reference | Result | +| --- | --- | --- | +| Build a bank of 48,622 single-signature, 198-byte transactions from wire bytes | Current base `33dde405` | 209.64 → 150.30 ms | +| Same bank from decoded transactions | Current base `33dde405` | 189.73 → 132.40 ms | +| Capture a synthetic 300-root / 1.5M-key status checkpoint on replay | Original synchronous snapshot algorithm, unchanged in current base | 202.44–207.35 ms → 5.92–6.06 µs; roughly 201 ms encoding moves to the worker | +| 33,760 maximum-wire-size transactions arriving over 200 ms: final-shred-to-ready | Same extracted implementation, overlap disabled/enabled | 99.89 → 6.504 ms | +| Replay callback to broadcast enqueue, p95 | Sequential live samples before/after atomic watermark | 12.885 → 2.031 ms | + +The bank comparison uses a serial caller on a Ryzen 7 9700X, Go 1.26.4, +GOMAXPROCS=8, with three alternating paired rounds. It includes admission, +execution, account publication, entry construction and signature-root hashing; +it excludes signature verification, real AccountsDB, network broadcast and +consensus. The statically prepared bank phase measured 101.92 ms, but its earlier +preparation work is excluded and is not eliminated from total CPU use. +[Bank benchmark methods and raw evidence](results/leader-block-packing/2026-09-13/README.md). + +The checkpoint benchmark uses GOMAXPROCS=1 on the same Zen 5 model and about 30 MB +of encoded status data. It measures removal of the direct replay wait, not +elimination of encoding CPU or allocations. The original synchronous encoder +is unchanged; the additional old full-payload copy is not in the baseline. +The live deployment's first ten captures took 28.694–34.464 µs for 24.5–43.4 MB +snapshots, while worker encoding took 179–367 ms. +[Checkpoint and voting measurements](results/validator-performance/2026-09-14/README.md). + +The streaming example uses exactly 1,232-byte valid transactions, two workers +and vector target eight. It includes decoding, assembly, identity checks, +signature verification and publication; it excludes transaction execution, +packet/shred authentication, loss recovery, disk and commit. Both sides use +the new batching/Narya/completion code, so it is an overlap comparison rather +than old `alpenglow-dev` versus this PR. Its catch-up case offers every shred +immediately and showed modest overhead from overlap in this short run. +[Standalone verification and allocation measurements](results/streaming-pr-review/2026-09-13/README.md). +Older component-by-component experiments retain their own intermediate baselines. + +## Live scope and remaining work + +The live testnet binary also contains #259's FEC/producer changes and retains a +two-FEC batching configuration. This independently based PR uses the base's +one-FEC configuration and includes the final pointer-view allocation improvement +from the standalone streaming branch. It is therefore not byte-identical to the +live binary. Vote-history, consensus admission/progress, checkpoint, leader +runtime and TLS implementation files match the preserved live source, apart +from explicitly identified FEC/shared-baseline code and configuration comments. + +The first recorded post-checkpoint leader window contained 25,200 / 48,622 / +41,136 / 39,687 transactions. The offline fixture reaches 48,622 under the +tested 50M cost budget and rejects the next transaction; 50,000 is not achieved. +Changing live leader windows and shared-host load are not controlled benchmarks. +Later FAST scores improved, but some improvement preceded the checkpoint change +while load was paused, and a restart also reset transient queue state. No causal +FAST-score improvement is claimed for this PR. + +Reserved voting remains experimental and opt-in. Its signed high-water bound, +history-version migration, clean marker consumption, directory locking, leader +gates and failure handling require consensus/storage review. Software crash +tests and a clean live restart do not qualify host power-loss behavior. A halted +cluster can leave conservative crash recovery waiting indefinitely. Preserve +the reservation and latest history across application or AccountsDB rollback. + +The separately observed scheduler heap retention issue is not fixed here, and +the remaining FAST/Titan reward omissions are not assumed to be solved. Server +loader scheduling, faucet automation, monitoring scripts and validator keys are +not part of the product changes in this PR. + +## Validation + +The combined source is tested separately from the live deployment. Exact commands, +results and the baseline BPF-loader failures are recorded in +[the validation report](results/validator-performance/2026-09-14/README.md). +Archived benchmark output is retained verbatim and marked as generated for +review; the Markdown explanations and executable test harnesses remain visible. diff --git a/docs/results/validator-performance/2026-09-14/README.md b/docs/results/validator-performance/2026-09-14/README.md new file mode 100644 index 00000000..6f169588 --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/README.md @@ -0,0 +1,126 @@ +# Consolidated validator validation and checkpoint measurements + +The combined runtime code was archived and tested at commit +`4a5a9761847a4fd8e7d60c72810082cf91fb73d5` against base +`33dde4050d9250557583395810799aaac2f54017`. Subsequent additions are review +documentation, attributes and evidence. The live validator was not redeployed. +This folder adds the checkpoint/voting evidence to the existing standalone +streaming and leader-packing benchmark reports. + +## Combined branch validation + +Go 1.26.4, Apple M4 Pro locally and Ryzen 7 9700X on Linux. Local race testing +used GOMAXPROCS=4 and package parallelism two; native used GOMAXPROCS=2, +package parallelism one and nice 15 while the live validator remained active. + +Race tests passed on both machines for accounts, alpenglow, block, +blockprod/scheduler, config, consensus, costmodel, merkletree, replay, sbpf, +sigverify, statsd, the synthetic transaction fixture and node. Metrics has no +tests. The full Turbine race suite passed on both machines. Native vet across +all selected packages and the complete `./cmd/mithril` build passed. + +The selected multi-package race command nevertheless exits **1**, because the +existing `pkg/sealevel` BPF-loader suite fails: 19 tests report failures before +`TestExecute_Tx_BpfLoader_Close_ProgramData_Success` panics inside +`UpgradeableLoaderClose`. The unchanged base on both machines reproduces the same failed-test +names and panic. This is not a claim that the full repository test suite passes. +The failure list and raw baseline/candidate logs are included for review. + +The native command and exits are recorded in [validation.json](validation.json): + +```sh +GOMAXPROCS=2 go test -race -p 1 -count=1 \ + ./pkg/accounts ./pkg/alpenglow ./pkg/block ./pkg/blockprod/... \ + ./pkg/config ./pkg/consensus ./pkg/costmodel ./pkg/merkletree \ + ./pkg/metrics ./pkg/replay ./pkg/sbpf ./pkg/sealevel \ + ./pkg/sigverify ./pkg/statsd ./pkg/tpu/txfixture ./pkg/turbine \ + ./cmd/mithril/node +``` + +`go vet -p 1` used the same package list; `go build -p 1 ./cmd/mithril` passed. +The review binary SHA256 is +`67fd57f8fadd73d9a433c16a5d3d9bff814654e744a641822732659821d3fb66`. +It is an isolated review build, not the running validator binary. + +On an unchanged checkout of the base, reproduce the known failure with: + +```sh +go test -race -p 1 -count=1 -run '^TestExecute_Tx_BpfLoader_' ./pkg/sealevel +``` + +Raw logs: [local](local-race.log), [native](native-race.log), +[unchanged local base](baseline-sealevel-race.log), +[unchanged native base](baseline-native-sealevel-race.log), and +[failed-test comparison](baseline-failure-comparison.json). +The original native runner is retained as [validate-native.py](validate-native.py); +its absolute paths identify the isolated historical test directories. + +`git diff --check` passes. Raw Go benchmark output is preserved verbatim, with +attributes allowing its padded CPU-identification lines and collapsing generated +evidence in GitHub review. A credential-pattern scan found no GitHub tokens or +private PEM keys in the changed files; runtime keys and state were not copied. + +## Checkpoint benchmark + +On Zen 5, GOMAXPROCS=1, 300 roots with 5,000 unique keys each: approximately +30 MB encoded output, three repetitions using `-benchtime=200ms -count=3`. +The differential fixture implements the original synchronous selection and +encoding path. That algorithm is unchanged in the PR's base; this benchmark +was run when the checkpoint patch was integrated into the preserved live source. + +| Stage | Three sample results | +| --- | --- | +| Original synchronous snapshot | 206.55 / 207.35 / 202.44 ms/op | +| Capture immutable view on replay | 5.936 / 6.057 / 5.922 µs/op | +| Encode captured view on worker | 202.52 / 201.84 / 200.83 ms/op | + +Capture allocates 35,168 bytes in 11 allocations. Encoding still allocates +approximately 99 MB and consumes its CPU time in the background. The old +additional full-payload copy is excluded from the benchmark baseline. This is +a change in which work blocks replay, not a 30,000-fold speedup of checkpoint +creation or the whole validator. + +```sh +GOMAXPROCS=1 go test -run '^$' \ + -bench '^BenchmarkTransactionStatusCheckpointCapture$' \ + -benchtime=200ms -count=3 ./pkg/replay +``` + +See [raw benchmark output](checkpoint-native-benchmark.log). + +## Live checkpoints and vote progress + +Before the patch, a five-minute trace found nine synchronous status snapshot +stalls of 170–269 ms, exactly 128 replayed banks between consecutive events +(median interval 31 seconds). The first ten post-deployment checkpoint commits +reported capture median **30.653 µs**, range **28.694–34.464 µs**, for encoded +payloads of 24.5–43.4 MB. Worker encoding median was **230.855 ms**, range +**179.073–366.582 ms**; total background fold median was 324.5 ms. +Records and measurement boundaries are in [checkpoint-live.json](checkpoint-live.json). + +A clean restart verified the final version-2 history against its reservation +marker before resuming the same identity and ledger. The bounded probe check +observed 130 replay/admission/notarize events, no admission rejections and no +exhausted reservations; 666 reservation checks retained at least 16 slots of +headroom. [Probe validation](checkpoint-probe-sanity.json). + +Separate earlier live comparisons motivate the other voting changes: an +84.296 ms history rename occurred inside an 84.960 ms history-to-injection +interval despite removing per-vote fsync. Moving writes to the ordered worker +kept filesystem work off the voter. For the atomic replay watermark, sequential +40-second samples measured replay-callback-to-broadcast p95 12.885 → 2.031 ms; +median was 1.684 → 1.622 ms. These compare deployed intermediate versions, not +the current development base against the full combined PR. + +Three subsequent completed five-minute captures with large-block generation +resumed included our vote in 1,746 of 1,796 unique FAST certificates (97.216%). +The matched replay-to-enqueue median was 1.554 ms and p95 2.008 ms. The public +dashboard window and observer differ. Some FAST improvement began before the +checkpoint patch while load was paused, and restart changed transient queue +state; no isolated FAST-score gain is claimed. See +[post-deployment observation summary](post-deploy-voting-summary.json). + +These live observations include the separate FEC/producer work from #259. They +do not replace standalone testing of this PR and do not establish mainnet +power-loss or complete consensus safety. The opt-in mode's assumptions and +operator requirements are in [reserved-vote-history.md](../../../reserved-vote-history.md). diff --git a/docs/results/validator-performance/2026-09-14/baseline-failure-comparison.json b/docs/results/validator-performance/2026-09-14/baseline-failure-comparison.json new file mode 100644 index 00000000..5353cc6e --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/baseline-failure-comparison.json @@ -0,0 +1,26 @@ +{ + "failed_tests": [ + "TestExecute_Tx_BpfLoader_Write_Success", + "TestExecute_Tx_BpfLoader_Write_Offset_Too_Large_Failure", + "TestExecute_Tx_BpfLoader_Write_Buffer_Authority_Didnt_Sign_Failure", + "TestExecute_Tx_BpfLoader_Write_Incorrect_Authority_Failure", + "TestExecute_Tx_BpfLoader_SetAuthority_Not_Enough_Instr_Accts_Failure", + "TestExecute_Tx_BpfLoader_SetAuthorityChecked_Not_Enough_Instr_Accts_Failure", + "TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success", + "TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success", + "TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Immutable_Failure", + "TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Wrong_Upgrade_Authority_Failure", + "TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Authority_Didnt_Sign_Failure", + "TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_New_Authority_Didnt_Sign_Failure", + "TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Uninitialized_Account_Failure", + "TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Immutable_Failure", + "TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Authority_Didnt_Sign_Failure", + "TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_New_Authority_Didnt_Sign_Failure", + "TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Wrong_Authority_Failure", + "TestExecute_Tx_BpfLoader_Close_Buffer_Not_Enough_Accounts", + "TestExecute_Tx_BpfLoader_Close_ProgramData_Success" + ], + "same_failed_test_names": true, + "same_panic_function": "UpgradeableLoaderClose", + "same_failed_test_names_on_native_base": true +} diff --git a/docs/results/validator-performance/2026-09-14/baseline-native-sealevel-race.log b/docs/results/validator-performance/2026-09-14/baseline-native-sealevel-race.log new file mode 100644 index 00000000..2a9dbddc --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/baseline-native-sealevel-race.log @@ -0,0 +1,330 @@ +--- FAIL: TestExecute_Tx_BpfLoader_Write_Success (0.00s) + sealevel_bpf_loader_test.go:147: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:147 + Error: Not equal: + expected: () + actual : *errors.errorString(&errors.errorString{s:"InstrErrInvalidInstructionData"}) + Test: TestExecute_Tx_BpfLoader_Write_Success + sealevel_bpf_loader_test.go:156: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:156 + Error: Not equal: + expected: true + actual : false + Test: TestExecute_Tx_BpfLoader_Write_Success +--- FAIL: TestExecute_Tx_BpfLoader_Write_Offset_Too_Large_Failure (0.00s) + sealevel_bpf_loader_test.go:207: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:207 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrAccountDataTooSmall"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=27) "InstrErrAccountDataTooSmall" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_Write_Offset_Too_Large_Failure +--- FAIL: TestExecute_Tx_BpfLoader_Write_Buffer_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:258: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:258 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_Write_Buffer_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_Write_Incorrect_Authority_Failure (0.00s) + sealevel_bpf_loader_test.go:314: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:314 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrIncorrectAuthority"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=26) "InstrErrIncorrectAuthority" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_Write_Incorrect_Authority_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthority_Not_Enough_Instr_Accts_Failure (0.00s) + sealevel_bpf_loader_test.go:350: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:350 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrNotEnoughAccountKeys"} + actual : &errors.errorString{s:"InstrErrMissingAccount"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=28) "InstrErrNotEnoughAccountKeys" + + s: (string) (len=22) "InstrErrMissingAccount" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthority_Not_Enough_Instr_Accts_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Not_Enough_Instr_Accts_Failure (0.00s) + sealevel_bpf_loader_test.go:860: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:860 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrNotEnoughAccountKeys"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=28) "InstrErrNotEnoughAccountKeys" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Not_Enough_Instr_Accts_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success (0.00s) + sealevel_bpf_loader_test.go:907: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:907 + Error: Not equal: + expected: () + actual : *errors.errorString(&errors.errorString{s:"InstrErrInvalidInstructionData"}) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success + sealevel_bpf_loader_test.go:916: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:916 + Error: Not equal: + expected: solana.PublicKey{0xe0, 0x2f, 0x69, 0x84, 0xc4, 0xc2, 0xdc, 0xcd, 0x7e, 0xd2, 0x18, 0xd9, 0xd0, 0xc4, 0x2a, 0x26, 0x94, 0xe4, 0x5f, 0xab, 0x48, 0xdd, 0x99, 0x34, 0x56, 0x71, 0x23, 0x6b, 0x55, 0x9, 0x3a, 0xb0} + actual : solana.PublicKey{0x13, 0x5a, 0xb4, 0xc3, 0xcd, 0x56, 0x4d, 0x37, 0x52, 0x52, 0x91, 0xd7, 0xb0, 0xee, 0xdf, 0x5b, 0x60, 0xdd, 0xd2, 0x29, 0xc4, 0x8, 0xaf, 0x10, 0x91, 0x31, 0xb3, 0x2d, 0xd8, 0x2d, 0xfa, 0x7a} + + Diff: + --- Expected + +++ Actual + @@ -1,4 +1,4 @@ + (solana.PublicKey) (len=32) { + - 00000000 e0 2f 69 84 c4 c2 dc cd 7e d2 18 d9 d0 c4 2a 26 |./i.....~.....*&| + - 00000010 94 e4 5f ab 48 dd 99 34 56 71 23 6b 55 09 3a b0 |.._.H..4Vq#kU.:.| + + 00000000 13 5a b4 c3 cd 56 4d 37 52 52 91 d7 b0 ee df 5b |.Z...VM7RR.....[| + + 00000010 60 dd d2 29 c4 08 af 10 91 31 b3 2d d8 2d fa 7a |`..).....1.-.-.z| + } + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success (0.00s) + sealevel_bpf_loader_test.go:963: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:963 + Error: Not equal: + expected: () + actual : *errors.errorString(&errors.errorString{s:"InstrErrInvalidInstructionData"}) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success + sealevel_bpf_loader_test.go:972: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:972 + Error: Not equal: + expected: solana.PublicKey{0xf6, 0x2a, 0x26, 0x5a, 0xa4, 0xc0, 0xa8, 0x43, 0x49, 0x5f, 0x28, 0x0, 0x8f, 0xc9, 0x5c, 0xb5, 0xf6, 0x64, 0x40, 0x9, 0x4f, 0x5d, 0xb5, 0x63, 0xbd, 0x44, 0xc2, 0x97, 0xea, 0x9c, 0x48, 0x11} + actual : solana.PublicKey{0xae, 0x2c, 0x74, 0x81, 0x12, 0x60, 0xbe, 0x78, 0xbd, 0x53, 0xbb, 0x92, 0x8d, 0x60, 0xf9, 0xe4, 0xde, 0x77, 0xa8, 0x3d, 0x84, 0x7d, 0x90, 0x56, 0x94, 0xf9, 0xa0, 0x5d, 0x84, 0xaa, 0x5d, 0x69} + + Diff: + --- Expected + +++ Actual + @@ -1,4 +1,4 @@ + (solana.PublicKey) (len=32) { + - 00000000 f6 2a 26 5a a4 c0 a8 43 49 5f 28 00 8f c9 5c b5 |.*&Z...CI_(...\.| + - 00000010 f6 64 40 09 4f 5d b5 63 bd 44 c2 97 ea 9c 48 11 |.d@.O].c.D....H.| + + 00000000 ae 2c 74 81 12 60 be 78 bd 53 bb 92 8d 60 f9 e4 |.,t..`.x.S...`..| + + 00000010 de 77 a8 3d 84 7d 90 56 94 f9 a0 5d 84 aa 5d 69 |.w.=.}.V...]..]i| + } + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Immutable_Failure (0.00s) + sealevel_bpf_loader_test.go:1019: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1019 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrImmutable"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=17) "InstrErrImmutable" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Immutable_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Wrong_Upgrade_Authority_Failure (0.00s) + sealevel_bpf_loader_test.go:1071: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1071 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrIncorrectAuthority"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=26) "InstrErrIncorrectAuthority" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Wrong_Upgrade_Authority_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1118: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1118 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_New_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1165: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1165 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_New_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Uninitialized_Account_Failure (0.00s) + sealevel_bpf_loader_test.go:1212: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1212 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrInvalidArgument"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=23) "InstrErrInvalidArgument" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Uninitialized_Account_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Immutable_Failure (0.00s) + sealevel_bpf_loader_test.go:1259: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1259 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrImmutable"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=17) "InstrErrImmutable" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Immutable_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1306: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1306 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_New_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1353: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1353 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_New_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Wrong_Authority_Failure (0.00s) + sealevel_bpf_loader_test.go:1405: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1405 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrIncorrectAuthority"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=26) "InstrErrIncorrectAuthority" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Wrong_Authority_Failure +--- FAIL: TestExecute_Tx_BpfLoader_Close_Buffer_Not_Enough_Accounts (0.00s) + sealevel_bpf_loader_test.go:1727: + Error Trace: /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1727 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrNotEnoughAccountKeys"} + actual : &errors.errorString{s:"InstrErrMissingAccount"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=28) "InstrErrNotEnoughAccountKeys" + + s: (string) (len=22) "InstrErrMissingAccount" + }) + Test: TestExecute_Tx_BpfLoader_Close_Buffer_Not_Enough_Accounts +--- FAIL: TestExecute_Tx_BpfLoader_Close_ProgramData_Success (0.00s) +panic: runtime error: invalid memory address or nil pointer dereference [recovered, repanicked] +[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x14bb47a] + +goroutine 61 [running]: +testing.tRunner.func1.2({0x1957180, 0x2854af0}) + /usr/local/go/src/testing/testing.go:1974 +0x419 +testing.tRunner.func1() + /usr/local/go/src/testing/testing.go:1977 +0x67f +panic({0x1957180?, 0x2854af0?}) + /usr/local/go/src/runtime/panic.go:860 +0x13a +github.com/Overclock-Validator/mithril/pkg/sealevel.UpgradeableLoaderClose(0xc00016ae40, 0xc000382000, 0xc000001300) + /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/bpf_loader.go:2703 +0xa7a +github.com/Overclock-Validator/mithril/pkg/sealevel.ProcessUpgradeableLoaderInstruction(0xc00016ae40) + /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/bpf_loader.go:2963 +0x325 +github.com/Overclock-Validator/mithril/pkg/sealevel.BpfLoaderProgramExecute(0xc00016ae40) + /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/bpf_loader.go:1461 +0x3b3 +github.com/Overclock-Validator/mithril/pkg/sealevel.(*ExecutionCtx).ExecuteInstruction(0xc00016ae40) + /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/execution_ctx.go:338 +0x477 +github.com/Overclock-Validator/mithril/pkg/sealevel.(*ExecutionCtx).ProcessInstruction(0xc00016ae40, {0xc000485e6c, 0x4, 0x4}, {0xc00048ba80, 0x4, 0x4}, {0xc000485ea8, 0x1, 0x1}) + /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/execution_ctx.go:284 +0x688 +github.com/Overclock-Validator/mithril/pkg/sealevel.TestExecute_Tx_BpfLoader_Close_ProgramData_Success(0xc000175b08) + /srv/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1790 +0x1c98 +testing.tRunner(0xc000175b08, 0x1ca3b18) + /usr/local/go/src/testing/testing.go:2036 +0x21d +created by testing.(*T).Run in goroutine 1 + /usr/local/go/src/testing/testing.go:2101 +0xb13 +FAIL github.com/Overclock-Validator/mithril/pkg/sealevel 0.129s +FAIL diff --git a/docs/results/validator-performance/2026-09-14/baseline-sealevel-race.log b/docs/results/validator-performance/2026-09-14/baseline-sealevel-race.log new file mode 100644 index 00000000..fcccc681 --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/baseline-sealevel-race.log @@ -0,0 +1,330 @@ +--- FAIL: TestExecute_Tx_BpfLoader_Write_Success (0.00s) + sealevel_bpf_loader_test.go:147: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:147 + Error: Not equal: + expected: () + actual : *errors.errorString(&errors.errorString{s:"InstrErrInvalidInstructionData"}) + Test: TestExecute_Tx_BpfLoader_Write_Success + sealevel_bpf_loader_test.go:156: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:156 + Error: Not equal: + expected: true + actual : false + Test: TestExecute_Tx_BpfLoader_Write_Success +--- FAIL: TestExecute_Tx_BpfLoader_Write_Offset_Too_Large_Failure (0.00s) + sealevel_bpf_loader_test.go:207: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:207 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrAccountDataTooSmall"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=27) "InstrErrAccountDataTooSmall" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_Write_Offset_Too_Large_Failure +--- FAIL: TestExecute_Tx_BpfLoader_Write_Buffer_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:258: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:258 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_Write_Buffer_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_Write_Incorrect_Authority_Failure (0.00s) + sealevel_bpf_loader_test.go:314: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:314 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrIncorrectAuthority"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=26) "InstrErrIncorrectAuthority" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_Write_Incorrect_Authority_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthority_Not_Enough_Instr_Accts_Failure (0.00s) + sealevel_bpf_loader_test.go:350: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:350 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrNotEnoughAccountKeys"} + actual : &errors.errorString{s:"InstrErrMissingAccount"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=28) "InstrErrNotEnoughAccountKeys" + + s: (string) (len=22) "InstrErrMissingAccount" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthority_Not_Enough_Instr_Accts_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Not_Enough_Instr_Accts_Failure (0.00s) + sealevel_bpf_loader_test.go:860: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:860 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrNotEnoughAccountKeys"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=28) "InstrErrNotEnoughAccountKeys" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Not_Enough_Instr_Accts_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success (0.00s) + sealevel_bpf_loader_test.go:907: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:907 + Error: Not equal: + expected: () + actual : *errors.errorString(&errors.errorString{s:"InstrErrInvalidInstructionData"}) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success + sealevel_bpf_loader_test.go:916: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:916 + Error: Not equal: + expected: solana.PublicKey{0x10, 0x1e, 0xf, 0xda, 0xd4, 0x11, 0xef, 0xd1, 0xa, 0xd3, 0xf2, 0x5a, 0xa0, 0xc7, 0xbf, 0x10, 0xf8, 0x2b, 0x7, 0x95, 0x55, 0xd1, 0x42, 0xb, 0xe9, 0xa9, 0xe9, 0xef, 0xa8, 0xc9, 0x2b, 0xa5} + actual : solana.PublicKey{0x18, 0x89, 0x7a, 0x5e, 0xe7, 0x98, 0x52, 0xe9, 0xf, 0x51, 0x7b, 0x84, 0x15, 0xab, 0x73, 0xac, 0x87, 0xb2, 0xa, 0x9e, 0xa8, 0x39, 0xa0, 0x92, 0x76, 0x67, 0xa7, 0xf7, 0xb3, 0xab, 0xae, 0xe2} + + Diff: + --- Expected + +++ Actual + @@ -1,4 +1,4 @@ + (solana.PublicKey) (len=32) { + - 00000000 10 1e 0f da d4 11 ef d1 0a d3 f2 5a a0 c7 bf 10 |...........Z....| + - 00000010 f8 2b 07 95 55 d1 42 0b e9 a9 e9 ef a8 c9 2b a5 |.+..U.B.......+.| + + 00000000 18 89 7a 5e e7 98 52 e9 0f 51 7b 84 15 ab 73 ac |..z^..R..Q{...s.| + + 00000010 87 b2 0a 9e a8 39 a0 92 76 67 a7 f7 b3 ab ae e2 |.....9..vg......| + } + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success (0.00s) + sealevel_bpf_loader_test.go:963: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:963 + Error: Not equal: + expected: () + actual : *errors.errorString(&errors.errorString{s:"InstrErrInvalidInstructionData"}) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success + sealevel_bpf_loader_test.go:972: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:972 + Error: Not equal: + expected: solana.PublicKey{0x7b, 0xfa, 0xe7, 0xc, 0x3a, 0xd0, 0x6, 0x9b, 0xfb, 0xba, 0xaa, 0x4d, 0x66, 0xd3, 0x37, 0x5a, 0x46, 0x1c, 0xb7, 0x87, 0xe3, 0x49, 0x76, 0xa8, 0xc5, 0x5f, 0x4, 0x4d, 0xf6, 0x5a, 0x4d, 0xb0} + actual : solana.PublicKey{0xbc, 0xc3, 0x71, 0xe, 0xd8, 0x11, 0x5, 0x52, 0x7f, 0xa0, 0x9e, 0x64, 0xbe, 0x60, 0x8e, 0x31, 0x35, 0x92, 0xc7, 0x84, 0x1e, 0x9a, 0xed, 0x37, 0xdb, 0x97, 0x73, 0xe9, 0xd6, 0x4, 0x97, 0xed} + + Diff: + --- Expected + +++ Actual + @@ -1,4 +1,4 @@ + (solana.PublicKey) (len=32) { + - 00000000 7b fa e7 0c 3a d0 06 9b fb ba aa 4d 66 d3 37 5a |{...:......Mf.7Z| + - 00000010 46 1c b7 87 e3 49 76 a8 c5 5f 04 4d f6 5a 4d b0 |F....Iv.._.M.ZM.| + + 00000000 bc c3 71 0e d8 11 05 52 7f a0 9e 64 be 60 8e 31 |..q....R...d.`.1| + + 00000010 35 92 c7 84 1e 9a ed 37 db 97 73 e9 d6 04 97 ed |5......7..s.....| + } + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Immutable_Failure (0.00s) + sealevel_bpf_loader_test.go:1019: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1019 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrImmutable"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=17) "InstrErrImmutable" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Immutable_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Wrong_Upgrade_Authority_Failure (0.00s) + sealevel_bpf_loader_test.go:1071: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1071 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrIncorrectAuthority"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=26) "InstrErrIncorrectAuthority" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Wrong_Upgrade_Authority_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1118: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1118 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_New_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1165: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1165 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_New_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Uninitialized_Account_Failure (0.00s) + sealevel_bpf_loader_test.go:1212: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1212 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrInvalidArgument"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=23) "InstrErrInvalidArgument" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Uninitialized_Account_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Immutable_Failure (0.00s) + sealevel_bpf_loader_test.go:1259: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1259 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrImmutable"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=17) "InstrErrImmutable" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Immutable_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1306: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1306 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_New_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1353: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1353 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_New_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Wrong_Authority_Failure (0.00s) + sealevel_bpf_loader_test.go:1405: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1405 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrIncorrectAuthority"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=26) "InstrErrIncorrectAuthority" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Wrong_Authority_Failure +--- FAIL: TestExecute_Tx_BpfLoader_Close_Buffer_Not_Enough_Accounts (0.00s) + sealevel_bpf_loader_test.go:1727: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1727 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrNotEnoughAccountKeys"} + actual : &errors.errorString{s:"InstrErrMissingAccount"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=28) "InstrErrNotEnoughAccountKeys" + + s: (string) (len=22) "InstrErrMissingAccount" + }) + Test: TestExecute_Tx_BpfLoader_Close_Buffer_Not_Enough_Accounts +--- FAIL: TestExecute_Tx_BpfLoader_Close_ProgramData_Success (0.00s) +panic: runtime error: invalid memory address or nil pointer dereference [recovered, repanicked] +[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x1034f369c] + +goroutine 72 [running]: +testing.tRunner.func1.2({0x1045f4e40, 0x10477e790}) + /Users/nikolashayes/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.4.darwin-arm64/src/testing/testing.go:1974 +0x2b8 +testing.tRunner.func1() + /Users/nikolashayes/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.4.darwin-arm64/src/testing/testing.go:1977 +0x460 +panic({0x1045f4e40?, 0x10477e790?}) + /Users/nikolashayes/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.4.darwin-arm64/src/runtime/panic.go:860 +0x12c +github.com/Overclock-Validator/mithril/pkg/sealevel.UpgradeableLoaderClose(0xc000145080, 0xc0004a4c40, 0xc000147600) + /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/bpf_loader.go:2703 +0x6fc +github.com/Overclock-Validator/mithril/pkg/sealevel.ProcessUpgradeableLoaderInstruction(0xc000145080) + /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/bpf_loader.go:2963 +0x230 +github.com/Overclock-Validator/mithril/pkg/sealevel.BpfLoaderProgramExecute(0xc000145080) + /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/bpf_loader.go:1461 +0x298 +github.com/Overclock-Validator/mithril/pkg/sealevel.(*ExecutionCtx).ExecuteInstruction(0xc000145080) + /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/execution_ctx.go:338 +0x2fc +github.com/Overclock-Validator/mithril/pkg/sealevel.(*ExecutionCtx).ProcessInstruction(0xc000145080, {0xc0003deeec, 0x4, 0x4}, {0xc000490e80, 0x4, 0x4}, {0xc0003def28, 0x1, 0x1}) + /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/execution_ctx.go:284 +0x484 +github.com/Overclock-Validator/mithril/pkg/sealevel.TestExecute_Tx_BpfLoader_Close_ProgramData_Success(0xc0004a2fc8) + /Users/nikolashayes/Desktop/programming/Codex/mithril-pr-consolidation-20260914/baseline/pkg/sealevel/sealevel_bpf_loader_test.go:1790 +0x138c +testing.tRunner(0xc0004a2fc8, 0x104735a40) + /Users/nikolashayes/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.4.darwin-arm64/src/testing/testing.go:2036 +0x168 +created by testing.(*T).Run in goroutine 1 + /Users/nikolashayes/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.4.darwin-arm64/src/testing/testing.go:2101 +0x7c0 +FAIL github.com/Overclock-Validator/mithril/pkg/sealevel 0.541s +FAIL diff --git a/docs/results/validator-performance/2026-09-14/checkpoint-live.json b/docs/results/validator-performance/2026-09-14/checkpoint-live.json new file mode 100644 index 00000000..0072e918 --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/checkpoint-live.json @@ -0,0 +1,118 @@ +{ + "utc": "2026-09-14T03:34:45Z", + "count": 10, + "log_bytes": 1607896, + "rows": [ + { + "slots": 128, + "through": 3427284, + "worker_ms": 331.0, + "capture_us": 34.174, + "encode_ms": 236.108854, + "bytes": 31488298, + "line": "(+ 28s) async fold: committed 128 slots through 3427284 in 331ms checkpoint_capture=34.174\u00b5s checkpoint_encode=236.108854ms checkpoint_bytes=31488298" + }, + { + "slots": 128, + "through": 3427428, + "worker_ms": 264.0, + "capture_us": 30.366, + "encode_ms": 182.148884, + "bytes": 25219543, + "line": "(+ 59s) async fold: committed 128 slots through 3427428 in 264ms checkpoint_capture=30.366\u00b5s checkpoint_encode=182.148884ms checkpoint_bytes=25219543" + }, + { + "slots": 128, + "through": 3427569, + "worker_ms": 318.0, + "capture_us": 30.417, + "encode_ms": 225.601155, + "bytes": 29886172, + "line": "(+ 1m29s) async fold: committed 128 slots through 3427569 in 318ms checkpoint_capture=30.417\u00b5s checkpoint_encode=225.601155ms checkpoint_bytes=29886172" + }, + { + "slots": 128, + "through": 3427701, + "worker_ms": 258.0, + "capture_us": 29.495, + "encode_ms": 179.07318000000004, + "bytes": 24530250, + "line": "(+ 1m58s) async fold: committed 128 slots through 3427701 in 258ms checkpoint_capture=29.495\u00b5s checkpoint_encode=179.07318ms checkpoint_bytes=24530250" + }, + { + "slots": 128, + "through": 3427841, + "worker_ms": 305.0, + "capture_us": 30.888, + "encode_ms": 216.143473, + "bytes": 29469092, + "line": "(+ 2m28s) async fold: committed 128 slots through 3427841 in 305ms checkpoint_capture=30.888\u00b5s checkpoint_encode=216.143473ms checkpoint_bytes=29469092" + }, + { + "slots": 128, + "through": 3427989, + "worker_ms": 286.0, + "capture_us": 28.694, + "encode_ms": 201.186937, + "bytes": 27782921, + "line": "(+ 2m59s) async fold: committed 128 slots through 3427989 in 286ms checkpoint_capture=28.694\u00b5s checkpoint_encode=201.186937ms checkpoint_bytes=27782921" + }, + { + "slots": 128, + "through": 3428133, + "worker_ms": 376.0, + "capture_us": 28.874, + "encode_ms": 278.613922, + "bytes": 35692638, + "line": "(+ 3m29s) async fold: committed 128 slots through 3428133 in 376ms checkpoint_capture=28.874\u00b5s checkpoint_encode=278.613922ms checkpoint_bytes=35692638" + }, + { + "slots": 128, + "through": 3428269, + "worker_ms": 431.0, + "capture_us": 32.671, + "encode_ms": 329.865906, + "bytes": 37615609, + "line": "(+ 3m58s) async fold: committed 128 slots through 3428269 in 431ms checkpoint_capture=32.671\u00b5s checkpoint_encode=329.865906ms checkpoint_bytes=37615609" + }, + { + "slots": 128, + "through": 3428409, + "worker_ms": 403.0, + "capture_us": 31.108, + "encode_ms": 301.569555, + "bytes": 38403988, + "line": "(+ 4m28s) async fold: committed 128 slots through 3428409 in 403ms checkpoint_capture=31.108\u00b5s checkpoint_encode=301.569555ms checkpoint_bytes=38403988" + }, + { + "slots": 128, + "through": 3428549, + "worker_ms": 477.0, + "capture_us": 34.464, + "encode_ms": 366.581817, + "bytes": 43443732, + "line": "(+ 4m58s) async fold: committed 128 slots through 3428549 in 477ms checkpoint_capture=34.464\u00b5s checkpoint_encode=366.581817ms checkpoint_bytes=43443732" + } + ], + "capture_us": { + "min": 28.694, + "median": 30.652500000000003, + "max": 34.464 + }, + "encode_ms": { + "min": 179.07318000000004, + "median": 230.8550045, + "max": 366.581817 + }, + "bytes": { + "min": 24530250, + "median": 30687235.0, + "max": 43443732 + }, + "worker_ms": { + "min": 258.0, + "median": 324.5, + "max": 477.0 + }, + "error_lines": [] +} diff --git a/docs/results/validator-performance/2026-09-14/checkpoint-native-benchmark.log b/docs/results/validator-performance/2026-09-14/checkpoint-native-benchmark.log new file mode 100644 index 00000000..33f4c2cd --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/checkpoint-native-benchmark.log @@ -0,0 +1,15 @@ +goos: linux +goarch: amd64 +pkg: github.com/Overclock-Validator/mithril/pkg/replay +cpu: AMD Ryzen 7 9700X 8-Core Processor +BenchmarkTransactionStatusCheckpointCapture/SynchronousBaseline 1 206554597 ns/op 99120032 B/op 2731 allocs/op +BenchmarkTransactionStatusCheckpointCapture/SynchronousBaseline 1 207352232 ns/op 99120072 B/op 2732 allocs/op +BenchmarkTransactionStatusCheckpointCapture/SynchronousBaseline 1 202442401 ns/op 99120048 B/op 2731 allocs/op +BenchmarkTransactionStatusCheckpointCapture/CaptureOnReplay 47412 5936 ns/op 35168 B/op 11 allocs/op +BenchmarkTransactionStatusCheckpointCapture/CaptureOnReplay 39506 6057 ns/op 35168 B/op 11 allocs/op +BenchmarkTransactionStatusCheckpointCapture/CaptureOnReplay 37850 5922 ns/op 35168 B/op 11 allocs/op +BenchmarkTransactionStatusCheckpointCapture/EncodeOnWorker 2 202522908 ns/op 99108084 B/op 2723 allocs/op +BenchmarkTransactionStatusCheckpointCapture/EncodeOnWorker 2 201841085 ns/op 99108076 B/op 2723 allocs/op +BenchmarkTransactionStatusCheckpointCapture/EncodeOnWorker 1 200834149 ns/op 99108104 B/op 2724 allocs/op +PASS +ok github.com/Overclock-Validator/mithril/pkg/replay 3.133s diff --git a/docs/results/validator-performance/2026-09-14/checkpoint-probe-sanity.json b/docs/results/validator-performance/2026-09-14/checkpoint-probe-sanity.json new file mode 100644 index 00000000..18f43529 --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/checkpoint-probe-sanity.json @@ -0,0 +1,1302 @@ +{ + "utc": "2026-09-14T03:31:58.546087+00:00", + "unique_fast_certificates": 67, + "included": 65, + "missing": 2, + "fast_percent": 97.01492537313433, + "slow_footer_observations": 52, + "excluded": {}, + "parse_errors": 0, + "truncated_captures": 0, + "certificates": [ + { + "key": "66635ddf984df58cc314e63804d527a30b22d03a2198f63a9517f62e343614da", + "slot": 3427676, + "source_slot": 3427678, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": false, + "serialized_observed": false, + "serialized_after_footer_observed": false, + "transactions": null, + "timings": {} + }, + { + "key": "70957edde11232ddd684bcade7a3ab0ddece13a80996074461bf7567cabbd48f", + "slot": 3427677, + "source_slot": 3427679, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": false, + "serialized_observed": false, + "serialized_after_footer_observed": false, + "transactions": null, + "timings": {} + }, + { + "key": "6860e33f4c6f1f76f194b257b26a564b37b2b479c636587589b320b07327d434", + "slot": 3427679, + "source_slot": 3427680, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 4107, + "timings": { + "first_shred_to_completion_ms": 160.194044, + "completion_to_replay_ms": 15.450752, + "replay_to_vote_ms": 1.788652, + "history_to_injection_ms": 0.607729, + "enqueue_to_serialization_ms": 0.097012 + } + }, + { + "key": "22e9c9801ff4e0ff60ad45f50a50cdcf11d2d57b59760d3301317b17fafa1d67", + "slot": 3427683, + "source_slot": 3427684, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 20.913838, + "completion_to_replay_ms": 5.708277, + "replay_to_vote_ms": 1.659419, + "history_to_injection_ms": 0.55986, + "enqueue_to_serialization_ms": 0.05873 + } + }, + { + "key": "7b42ef8b2d34574855e9bd530f1499a0d40ebb256213db11ba5a2cb60934e2eb", + "slot": 3427684, + "source_slot": 3427686, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 18.440063, + "completion_to_replay_ms": 4.894151, + "replay_to_vote_ms": 1.679888, + "history_to_injection_ms": 0.591409, + "enqueue_to_serialization_ms": 0.06941 + } + }, + { + "key": "feebc5203460436e5471012d5fef6ff61d01717167115fc8afaf082ec152f949", + "slot": 3427687, + "source_slot": 3427688, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 17.608906, + "completion_to_replay_ms": 5.837208, + "replay_to_vote_ms": 1.679787, + "history_to_injection_ms": 0.599373, + "enqueue_to_serialization_ms": 0.047359 + } + }, + { + "key": "ed6e7b570306870f636d4cb1d538b6426d5ba180e73440ae533837df773ad5bd", + "slot": 3427691, + "source_slot": 3427692, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1866, + "timings": { + "first_shred_to_completion_ms": 264.800928, + "completion_to_replay_ms": 11.952247, + "replay_to_vote_ms": 2.67924, + "history_to_injection_ms": 1.086987, + "enqueue_to_serialization_ms": 0.09066 + } + }, + { + "key": "839c21b16c4f079a437ba0aa1f849adc2f6dbaccfd083779ad1458e558249210", + "slot": 3427695, + "source_slot": 3427696, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1900, + "timings": { + "first_shred_to_completion_ms": 226.637928, + "completion_to_replay_ms": 12.56236, + "replay_to_vote_ms": 1.74497, + "history_to_injection_ms": 0.642924, + "enqueue_to_serialization_ms": 0.074469 + } + }, + { + "key": "f8345151c1867cee92aceee8c6d5d0c9ed38acb79edda819d7c095e113c5d5a8", + "slot": 3427696, + "source_slot": 3427698, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 781, + "timings": { + "first_shred_to_completion_ms": 152.249718, + "completion_to_replay_ms": 8.010941, + "replay_to_vote_ms": 1.760559, + "history_to_injection_ms": 0.671087, + "enqueue_to_serialization_ms": 0.075461 + } + }, + { + "key": "e47b25114df954aa9a29dc29446b292ce7419f5a0917cfda28a094431585950a", + "slot": 3427697, + "source_slot": 3427699, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1750, + "timings": { + "first_shred_to_completion_ms": 204.159518, + "completion_to_replay_ms": 10.146062, + "replay_to_vote_ms": 1.744268, + "history_to_injection_ms": 0.649537, + "enqueue_to_serialization_ms": 0.070252 + } + }, + { + "key": "7bbd556bb9aedc97937495f66f1813084872a10934801c05e84b870949dc3350", + "slot": 3427699, + "source_slot": 3427700, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1781, + "timings": { + "first_shred_to_completion_ms": 208.64873, + "completion_to_replay_ms": 10.911857, + "replay_to_vote_ms": 1.754247, + "history_to_injection_ms": 0.667962, + "enqueue_to_serialization_ms": 0.06945 + } + }, + { + "key": "040d46d1079c9bc398e49708c771d9fa3361c3d6d4678f9276fd56792d409fb8", + "slot": 3427703, + "source_slot": 3427704, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1814, + "timings": { + "first_shred_to_completion_ms": 245.512385, + "completion_to_replay_ms": 12.175264, + "replay_to_vote_ms": 1.817336, + "history_to_injection_ms": 0.66705, + "enqueue_to_serialization_ms": 0.076423 + } + }, + { + "key": "ed893c17113b7361a772973b5d7099aca3031f7d113349730001d49da4553eb2", + "slot": 3427704, + "source_slot": 3427706, + "epoch": 63, + "own_rank": 20, + "included": false, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1486, + "timings": { + "first_shred_to_completion_ms": 149.89184, + "completion_to_replay_ms": 61.329651, + "replay_to_vote_ms": 1.165804, + "history_to_injection_ms": 0.065302, + "enqueue_to_serialization_ms": 0.088115 + } + }, + { + "key": "97592466d5ecd5dd74d33a909748b111dad98f9c716626e5d07dd83750ccce14", + "slot": 3427705, + "source_slot": 3427707, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1628, + "timings": { + "first_shred_to_completion_ms": 205.924995, + "completion_to_replay_ms": 9.361562, + "replay_to_vote_ms": 1.205329, + "history_to_injection_ms": 0.07977, + "enqueue_to_serialization_ms": 0.070592 + } + }, + { + "key": "8c7e0d9273901945328457eead6e64f2ec9cebadb04880b3f62977d558b78a9b", + "slot": 3427707, + "source_slot": 3427708, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 2143, + "timings": { + "first_shred_to_completion_ms": 198.933375, + "completion_to_replay_ms": 10.232604, + "replay_to_vote_ms": 1.199237, + "history_to_injection_ms": 0.088786, + "enqueue_to_serialization_ms": 0.09598 + } + }, + { + "key": "c5a6102fb7ca9adad8ee99752fe2e442db28d9d544294828af7644cd343e70f3", + "slot": 3427709, + "source_slot": 3427710, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 2181, + "timings": { + "first_shred_to_completion_ms": 220.804197, + "completion_to_replay_ms": 11.085933, + "replay_to_vote_ms": 1.192364, + "history_to_injection_ms": 0.09634, + "enqueue_to_serialization_ms": 0.072045 + } + }, + { + "key": "a52e49e524ebcdf65b1de76a6623342668fa35e81b2d7a7776525c4dba48db3c", + "slot": 3427711, + "source_slot": 3427712, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1718, + "timings": { + "first_shred_to_completion_ms": 203.291782, + "completion_to_replay_ms": 10.106848, + "replay_to_vote_ms": 1.23792, + "history_to_injection_ms": 0.129894, + "enqueue_to_serialization_ms": 0.067346 + } + }, + { + "key": "cf67b480401070a76fa2f37141c8f8c707552a42a58114730b6cda6c638e52e4", + "slot": 3427712, + "source_slot": 3427714, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 763, + "timings": { + "first_shred_to_completion_ms": 137.552168, + "completion_to_replay_ms": 7.823519, + "replay_to_vote_ms": 1.204678, + "history_to_injection_ms": 0.110727, + "enqueue_to_serialization_ms": 0.073948 + } + }, + { + "key": "546cf922f0e0e1f6a0b3350bdc1dac7787d1c061015f17fd3e8b2c527522f524", + "slot": 3427713, + "source_slot": 3427715, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1775, + "timings": { + "first_shred_to_completion_ms": 205.428756, + "completion_to_replay_ms": 8.906179, + "replay_to_vote_ms": 1.343577, + "history_to_injection_ms": 0.152686, + "enqueue_to_serialization_ms": 0.071885 + } + }, + { + "key": "6e9fe0addccadb0008c41299918d5bf2e01d5da6e2a738668b6d3837556d225e", + "slot": 3427715, + "source_slot": 3427716, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1755, + "timings": { + "first_shred_to_completion_ms": 202.996969, + "completion_to_replay_ms": 8.86942, + "replay_to_vote_ms": 1.250422, + "history_to_injection_ms": 0.130214, + "enqueue_to_serialization_ms": 0.077415 + } + }, + { + "key": "21acbd7bca85e4069d5cf7c0581b69257ee6aef6d0ab9ec3ed0fe04729e2b465", + "slot": 3427716, + "source_slot": 3427718, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 31736, + "timings": { + "first_shred_to_completion_ms": 169.945006, + "completion_to_replay_ms": 93.913487, + "replay_to_vote_ms": 1.357613, + "history_to_injection_ms": 0.158166, + "enqueue_to_serialization_ms": 0.181891 + } + }, + { + "key": "e5b309a9a2d3a9de37f617607956fb30fa7215e4c9fe68fbdd389e7a164028a2", + "slot": 3427717, + "source_slot": 3427719, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 33760, + "timings": { + "first_shred_to_completion_ms": 220.202539, + "completion_to_replay_ms": 110.591158, + "replay_to_vote_ms": 2.255977, + "history_to_injection_ms": 0.253795, + "enqueue_to_serialization_ms": 0.253485 + } + }, + { + "key": "af80ca4a3ea53d577ee427b9c6f8d1bc22a19f435f09b706dece55fb0d455a52", + "slot": 3427719, + "source_slot": 3427721, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 33760, + "timings": { + "first_shred_to_completion_ms": 215.440446, + "completion_to_replay_ms": 103.558139, + "replay_to_vote_ms": 1.314884, + "history_to_injection_ms": 0.177643, + "enqueue_to_serialization_ms": 0.08516 + } + }, + { + "key": "8b08412d60cc66fb729e90767199c5765fd7cf60d580b1565fbcbcb3628286c0", + "slot": 3427720, + "source_slot": 3427722, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 4048, + "timings": { + "first_shred_to_completion_ms": 161.289346, + "completion_to_replay_ms": 14.938192, + "replay_to_vote_ms": 1.321907, + "history_to_injection_ms": 0.182642, + "enqueue_to_serialization_ms": 0.076573 + } + }, + { + "key": "fa20eaa2d9fccade3b0fbc6915a216eaa69aebc34b9cf6d3ecbcae749b1ad074", + "slot": 3427721, + "source_slot": 3427723, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 2048, + "timings": { + "first_shred_to_completion_ms": 201.085948, + "completion_to_replay_ms": 9.475345, + "replay_to_vote_ms": 1.266503, + "history_to_injection_ms": 0.157595, + "enqueue_to_serialization_ms": 0.070532 + } + }, + { + "key": "4ff05ffa25e5514966263d311f1073c3e60448cb591f0c5e10e0396ffac48044", + "slot": 3427723, + "source_slot": 3427724, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1607, + "timings": { + "first_shred_to_completion_ms": 170.084387, + "completion_to_replay_ms": 10.283109, + "replay_to_vote_ms": 1.263167, + "history_to_injection_ms": 0.154189, + "enqueue_to_serialization_ms": 0.070362 + } + }, + { + "key": "b9801a1e83d0465ebfa5bce0490facab3436ef06666bc3e0e837b65219857f53", + "slot": 3427724, + "source_slot": 3427726, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 9793, + "timings": { + "first_shred_to_completion_ms": 181.894417, + "completion_to_replay_ms": 27.00905, + "replay_to_vote_ms": 1.292312, + "history_to_injection_ms": 0.18151, + "enqueue_to_serialization_ms": 0.076323 + } + }, + { + "key": "ed2d7782f03765e5cb4a5695d3ac6c68990fad8f8e21188f27a8342907798e7f", + "slot": 3427725, + "source_slot": 3427727, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 3057, + "timings": { + "first_shred_to_completion_ms": 227.88829, + "completion_to_replay_ms": 15.427278, + "replay_to_vote_ms": 1.316186, + "history_to_injection_ms": 0.183003, + "enqueue_to_serialization_ms": 0.074821 + } + }, + { + "key": "f3828295bc16bda5b3757a54c63cfdde2a3680ed02724ec43b3d22f3d09ad8cb", + "slot": 3427727, + "source_slot": 3427728, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 8358, + "timings": { + "first_shred_to_completion_ms": 216.778082, + "completion_to_replay_ms": 25.156789, + "replay_to_vote_ms": 1.332848, + "history_to_injection_ms": 0.193733, + "enqueue_to_serialization_ms": 0.077214 + } + }, + { + "key": "b744a0e4dc7c7d780658feb9437bd84e0e1fcf5f21744f25b544c7633343d885", + "slot": 3427731, + "source_slot": 3427732, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1642, + "timings": { + "first_shred_to_completion_ms": 186.0591, + "completion_to_replay_ms": 10.786302, + "replay_to_vote_ms": 1.307079, + "history_to_injection_ms": 0.211116, + "enqueue_to_serialization_ms": 0.06921 + } + }, + { + "key": "719047229b13a3a3921de4963bf7303f878836c1fe3103cb797ad119fbc5fb1d", + "slot": 3427732, + "source_slot": 3427734, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 26245, + "timings": { + "first_shred_to_completion_ms": 162.904394, + "completion_to_replay_ms": 84.068979, + "replay_to_vote_ms": 1.414942, + "history_to_injection_ms": 0.248977, + "enqueue_to_serialization_ms": 0.231123 + } + }, + { + "key": "3dfc256f0cec52256785c19994e5bfe681700e6356efbec4be04a4916b35ee47", + "slot": 3427733, + "source_slot": 3427735, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 33760, + "timings": { + "first_shred_to_completion_ms": 221.125058, + "completion_to_replay_ms": 109.323122, + "replay_to_vote_ms": 1.737545, + "history_to_injection_ms": 0.271839, + "enqueue_to_serialization_ms": 0.160641 + } + }, + { + "key": "e5d3918c204c17aced5b9d4af67d3455fc71839a54eed2b566b620ef0dea2515", + "slot": 3427735, + "source_slot": 3427740, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 33760, + "timings": { + "first_shred_to_completion_ms": 220.006311, + "completion_to_replay_ms": 93.49894, + "replay_to_vote_ms": 1.381038, + "history_to_injection_ms": 0.25075, + "enqueue_to_serialization_ms": 0.084799 + } + }, + { + "key": "6630b3b6789622e5c48f210f0f0b5393bc0ae692c185757eeb2a39c33aaa3d0a", + "slot": 3427740, + "source_slot": 3427742, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 59.086287, + "completion_to_replay_ms": 6.707358, + "replay_to_vote_ms": 1.37696, + "history_to_injection_ms": 0.255138, + "enqueue_to_serialization_ms": 0.065112 + } + }, + { + "key": "c3d6f9244bf212c2680ed119e7f894b8e5b13909f543677d4a1b4be46eba52db", + "slot": 3427741, + "source_slot": 3427743, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 10, + "timings": { + "first_shred_to_completion_ms": 167.427028, + "completion_to_replay_ms": 5.480619, + "replay_to_vote_ms": 1.368004, + "history_to_injection_ms": 0.249859, + "enqueue_to_serialization_ms": 0.067887 + } + }, + { + "key": "d9e7f3ba8964125c2e1eb771927fa8a508875a22d4f0a2d37867c2ff52123880", + "slot": 3427743, + "source_slot": 3427744, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 2391, + "timings": { + "first_shred_to_completion_ms": 257.227988, + "completion_to_replay_ms": 13.159669, + "replay_to_vote_ms": 1.389644, + "history_to_injection_ms": 0.26689, + "enqueue_to_serialization_ms": 0.084969 + } + }, + { + "key": "7003c068c0edca7826bd092c0da9c72512a68fb215a6d0faee51c29f5a4eff1c", + "slot": 3427745, + "source_slot": 3427746, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1871, + "timings": { + "first_shred_to_completion_ms": 202.414107, + "completion_to_replay_ms": 11.834366, + "replay_to_vote_ms": 1.35557, + "history_to_injection_ms": 0.255559, + "enqueue_to_serialization_ms": 0.074219 + } + }, + { + "key": "6cbd6f986daca6df360a4104ca91fd4eedaf63711eee5bb0a7a7b5f3d55ea309", + "slot": 3427747, + "source_slot": 3427748, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 2072, + "timings": { + "first_shred_to_completion_ms": 267.146103, + "completion_to_replay_ms": 11.186511, + "replay_to_vote_ms": 1.402629, + "history_to_injection_ms": 0.299612, + "enqueue_to_serialization_ms": 0.074179 + } + }, + { + "key": "bee8bdc7af64235e135f3a222bad6eedc68c8949a88a4a26f8222ba5b888a34a", + "slot": 3427751, + "source_slot": 3427752, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1902, + "timings": { + "first_shred_to_completion_ms": 207.014637, + "completion_to_replay_ms": 12.600361, + "replay_to_vote_ms": 1.381158, + "history_to_injection_ms": 0.284363, + "enqueue_to_serialization_ms": 0.078487 + } + }, + { + "key": "9afd47b978c886618c73f239dbab7fa09499af0c7969013e8bae19336799f46d", + "slot": 3427755, + "source_slot": 3427756, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 17.110633, + "completion_to_replay_ms": 5.695923, + "replay_to_vote_ms": 1.429809, + "history_to_injection_ms": 0.296435, + "enqueue_to_serialization_ms": 0.067497 + } + }, + { + "key": "adb2aefe10a9093430961ce37c049418a8e03c7eed962151655e93fb4bde115c", + "slot": 3427756, + "source_slot": 3427758, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1359, + "timings": { + "first_shred_to_completion_ms": 174.971455, + "completion_to_replay_ms": 9.470005, + "replay_to_vote_ms": 1.490783, + "history_to_injection_ms": 0.325079, + "enqueue_to_serialization_ms": 0.086242 + } + }, + { + "key": "35fa3ad2ce843fb0fb905aa7d1b4515ed468f9c7ef426f7e17ef0eba62f4f70e", + "slot": 3427757, + "source_slot": 3427759, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1896, + "timings": { + "first_shred_to_completion_ms": 176.069902, + "completion_to_replay_ms": 10.261619, + "replay_to_vote_ms": 1.441421, + "history_to_injection_ms": 0.318777, + "enqueue_to_serialization_ms": 0.073748 + } + }, + { + "key": "a7ba4241262f7dc58326978390d06d6ba4ce4c15accde3cb62472b39e7efbe57", + "slot": 3427759, + "source_slot": 3427760, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1837, + "timings": { + "first_shred_to_completion_ms": 198.988147, + "completion_to_replay_ms": 9.498499, + "replay_to_vote_ms": 1.429078, + "history_to_injection_ms": 0.32046, + "enqueue_to_serialization_ms": 0.065653 + } + }, + { + "key": "cdf842d883ac7f6759ff05fb050aabd68b46cd326ab4b1eed30382153270e895", + "slot": 3427760, + "source_slot": 3427761, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 16.056287, + "completion_to_replay_ms": 4.92618, + "replay_to_vote_ms": 1.427095, + "history_to_injection_ms": 0.350276, + "enqueue_to_serialization_ms": 0.072195 + } + }, + { + "key": "b1bed142a5efaf6ddc9d65a658cd6bc40f7cf699743b0ad50161ab8a07e05fa5", + "slot": 3427763, + "source_slot": 3427764, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 76.630822, + "completion_to_replay_ms": 5.588381, + "replay_to_vote_ms": 1.416946, + "history_to_injection_ms": 0.34686, + "enqueue_to_serialization_ms": 0.070402 + } + }, + { + "key": "1f25b1876f3ddcaac51d3ff973cb63446612819c8f98b1972b92da7237a9e72a", + "slot": 3427764, + "source_slot": 3427766, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 3655, + "timings": { + "first_shred_to_completion_ms": 156.070858, + "completion_to_replay_ms": 13.328255, + "replay_to_vote_ms": 1.43037, + "history_to_injection_ms": 0.342752, + "enqueue_to_serialization_ms": 0.082525 + } + }, + { + "key": "35b2cfdbdf95e76274ce131fc69caba50f9874fe5fd1cd188496b11d2896270d", + "slot": 3427765, + "source_slot": 3427767, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1969, + "timings": { + "first_shred_to_completion_ms": 205.403319, + "completion_to_replay_ms": 10.133628, + "replay_to_vote_ms": 1.492948, + "history_to_injection_ms": 0.359544, + "enqueue_to_serialization_ms": 0.065783 + } + }, + { + "key": "4f43013c63129cddd3de0e1158a98874be1e636dd09511729037a723b211f588", + "slot": 3427767, + "source_slot": 3427768, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1854, + "timings": { + "first_shred_to_completion_ms": 199.97666, + "completion_to_replay_ms": 9.726786, + "replay_to_vote_ms": 1.47266, + "history_to_injection_ms": 0.377087, + "enqueue_to_serialization_ms": 0.097302 + } + }, + { + "key": "c5850338aaa64e742f7914312f6d6da25603f353f6867e48646a0883e16bdbda", + "slot": 3427769, + "source_slot": 3427770, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 19.513715, + "completion_to_replay_ms": 6.661432, + "replay_to_vote_ms": 1.890703, + "history_to_injection_ms": 0.508272, + "enqueue_to_serialization_ms": 0.105818 + } + }, + { + "key": "0e1025e90c5405220ec9ef3984ac935506473b14552d609bd7e32ca434d31f7c", + "slot": 3427770, + "source_slot": 3427771, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 81.144831, + "completion_to_replay_ms": 5.034724, + "replay_to_vote_ms": 1.569541, + "history_to_injection_ms": 0.425147, + "enqueue_to_serialization_ms": 0.073518 + } + }, + { + "key": "fb4c1604ce9f74f7ae6eb389fbbf671d73b25dd8dae4bc5e1dda633eab8407c0", + "slot": 3427771, + "source_slot": 3427772, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 82.455526, + "completion_to_replay_ms": 4.860548, + "replay_to_vote_ms": 1.438796, + "history_to_injection_ms": 0.359594, + "enqueue_to_serialization_ms": 0.072075 + } + }, + { + "key": "f5227a53bfaa7476bd0605263369c2a36938429cf00e2a61bbc514accf92d49e", + "slot": 3427775, + "source_slot": 3427776, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 4558, + "timings": { + "first_shred_to_completion_ms": 202.041198, + "completion_to_replay_ms": 18.736349, + "replay_to_vote_ms": 1.512494, + "history_to_injection_ms": 0.424114, + "enqueue_to_serialization_ms": 0.079349 + } + }, + { + "key": "b8455e61b5a229aab86257b0beabca12a26331139c5df1237d0a6884019f8b65", + "slot": 3427779, + "source_slot": 3427780, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1728, + "timings": { + "first_shred_to_completion_ms": 208.888409, + "completion_to_replay_ms": 10.567201, + "replay_to_vote_ms": 1.571866, + "history_to_injection_ms": 0.40566, + "enqueue_to_serialization_ms": 0.075171 + } + }, + { + "key": "ff811298c2dd93248dfc8648cd23adb2add946dbb481b2c69f1650eccbc7030f", + "slot": 3427783, + "source_slot": 3427784, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 2079, + "timings": { + "first_shred_to_completion_ms": 241.654797, + "completion_to_replay_ms": 12.636388, + "replay_to_vote_ms": 1.52071, + "history_to_injection_ms": 0.452958, + "enqueue_to_serialization_ms": 0.077936 + } + }, + { + "key": "e3d2ecec0b107a7b27237d650b02f485af900ba18df99092a1e7f470139e6e9c", + "slot": 3427787, + "source_slot": 3427788, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 27.731845, + "completion_to_replay_ms": 5.601185, + "replay_to_vote_ms": 1.607632, + "history_to_injection_ms": 0.490278, + "enqueue_to_serialization_ms": 0.070222 + } + }, + { + "key": "e6735e09d712aa4bfd588d8212ea0b3784bded8c8b980130367e1ca2e0116bd9", + "slot": 3427791, + "source_slot": 3427792, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 27.287802, + "completion_to_replay_ms": 5.837739, + "replay_to_vote_ms": 1.555375, + "history_to_injection_ms": 0.449803, + "enqueue_to_serialization_ms": 0.068548 + } + }, + { + "key": "29ad0046829bd7d6db749064a575f5f80c2cf94497f5a16ccd88cc6a7b261660", + "slot": 3427792, + "source_slot": 3427794, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 20.415104, + "completion_to_replay_ms": 5.116067, + "replay_to_vote_ms": 1.561536, + "history_to_injection_ms": 0.470381, + "enqueue_to_serialization_ms": 0.06959 + } + }, + { + "key": "2971a122990cfc1a34e3c352dc9b63c2b80a5308ee7457274e4b1456cde9f1fa", + "slot": 3427795, + "source_slot": 3427796, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 0, + "timings": { + "first_shred_to_completion_ms": 22.505722, + "completion_to_replay_ms": 6.11056, + "replay_to_vote_ms": 1.570643, + "history_to_injection_ms": 0.489257, + "enqueue_to_serialization_ms": 0.070643 + } + }, + { + "key": "ec2b9b17126f1b8d53accae2c48abab7caef30c259444f99367b57e5957c81a8", + "slot": 3427796, + "source_slot": 3427798, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 661, + "timings": { + "first_shred_to_completion_ms": 123.082354, + "completion_to_replay_ms": 7.898811, + "replay_to_vote_ms": 1.652556, + "history_to_injection_ms": 0.528781, + "enqueue_to_serialization_ms": 0.073548 + } + }, + { + "key": "1efd498bffd4bbbbba29749ed7f4e5b85206faf755bf433aad3503a819583136", + "slot": 3427797, + "source_slot": 3427799, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1658, + "timings": { + "first_shred_to_completion_ms": 185.806908, + "completion_to_replay_ms": 11.071937, + "replay_to_vote_ms": 1.589768, + "history_to_injection_ms": 0.50142, + "enqueue_to_serialization_ms": 0.085711 + } + }, + { + "key": "526cb0d6bbf1c23a1b9df28bf9c1763ff13c03066c54901a5f5ab923e631c5f1", + "slot": 3427799, + "source_slot": 3427800, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 1952, + "timings": { + "first_shred_to_completion_ms": 206.06048, + "completion_to_replay_ms": 11.90601, + "replay_to_vote_ms": 1.593906, + "history_to_injection_ms": 0.495298, + "enqueue_to_serialization_ms": 0.067437 + } + }, + { + "key": "6f3331e4c902ea987385c0d6cb76b1a82cfe6ba7f331003bc2ee52d9d35ffe1d", + "slot": 3427803, + "source_slot": 3427804, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 2023, + "timings": { + "first_shred_to_completion_ms": 260.300125, + "completion_to_replay_ms": 11.481044, + "replay_to_vote_ms": 1.66514, + "history_to_injection_ms": 0.530755, + "enqueue_to_serialization_ms": 0.069109 + } + }, + { + "key": "9bf0025d99d2c01845d6278107e99938eb0d9b5350473e020d4a4ade2c299712", + "slot": 3427804, + "source_slot": 3427806, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 31873, + "timings": { + "first_shred_to_completion_ms": 175.892941, + "completion_to_replay_ms": 105.377889, + "replay_to_vote_ms": 1.834307, + "history_to_injection_ms": 0.615123, + "enqueue_to_serialization_ms": 0.221725 + } + }, + { + "key": "a7ab49cecc37985fd1ddec744fa471ded8018a26fc3f983bf01d994854319de7", + "slot": 3427805, + "source_slot": 3427807, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 33760, + "timings": { + "first_shred_to_completion_ms": 228.689502, + "completion_to_replay_ms": 110.120166, + "replay_to_vote_ms": 1.852892, + "history_to_injection_ms": 0.637384, + "enqueue_to_serialization_ms": 0.157996 + } + }, + { + "key": "9d86bb10a5bfd66014f3b324f344cf4f7426e8ccb572b79db6871f26cdc3928d", + "slot": 3427807, + "source_slot": 3427812, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 33760, + "timings": { + "first_shred_to_completion_ms": 277.260165, + "completion_to_replay_ms": 99.471141, + "replay_to_vote_ms": 1.781748, + "history_to_injection_ms": 0.64013, + "enqueue_to_serialization_ms": 0.134733 + } + }, + { + "key": "ec206c19aebd0c1123ec0c9ceb93d82928b7c85d25ee7a0a01c32465980fe177", + "slot": 3427812, + "source_slot": 3427814, + "epoch": 63, + "own_rank": 20, + "included": false, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 32061, + "timings": { + "first_shred_to_completion_ms": 223.06323, + "completion_to_replay_ms": 120.796931, + "replay_to_vote_ms": 1.903487, + "history_to_injection_ms": 0.635471, + "enqueue_to_serialization_ms": 0.166973 + } + }, + { + "key": "568d1ab1d5356068b9f2a5109ea14589361ea820ea0c06a45ffe214efa0fea93", + "slot": 3427813, + "source_slot": 3427815, + "epoch": 63, + "own_rank": 20, + "included": true, + "notarize_observed": true, + "serialized_observed": true, + "serialized_after_footer_observed": false, + "transactions": 33760, + "timings": { + "first_shred_to_completion_ms": 266.445771, + "completion_to_replay_ms": 98.727998, + "replay_to_vote_ms": 1.789233, + "history_to_injection_ms": 0.621334, + "enqueue_to_serialization_ms": 0.174827 + } + } + ], + "notes": [ + "Local successfully replayed footer sample; not necessarily identical to Puffin observer sample.", + "Certificates deduplicated by full wire proof within this capture; aggregate across captures by key.", + "No remote receipt timestamp. First-shred interval includes leader production.", + "Capture edges and absent rank are excluded; no notarize event inside a capture does not prove no vote was ever sent." + ], + "probe_counts": { + "S": 130, + "J": 130, + "B": 130, + "R": 130, + "A": 130, + "C": 130, + "H": 130, + "I": 130, + "Q": 130, + "K": 172, + "V": 257, + "W": 0 + }, + "admission_rejections": 0, + "reservations": [ + [ + 666, + 16 + ] + ], + "replays_without_observed_notarize": [] +} diff --git a/docs/results/validator-performance/2026-09-14/local-race.log b/docs/results/validator-performance/2026-09-14/local-race.log new file mode 100644 index 00000000..8c34d45b --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/local-race.log @@ -0,0 +1,347 @@ +ok github.com/Overclock-Validator/mithril/pkg/accounts 1.302s +ok github.com/Overclock-Validator/mithril/pkg/alpenglow 7.635s +ok github.com/Overclock-Validator/mithril/pkg/block 1.478s +ok github.com/Overclock-Validator/mithril/pkg/blockprod 20.370s +ok github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler 1.664s +ok github.com/Overclock-Validator/mithril/pkg/config 1.166s +ok github.com/Overclock-Validator/mithril/pkg/consensus 9.605s +ok github.com/Overclock-Validator/mithril/pkg/costmodel 1.437s +ok github.com/Overclock-Validator/mithril/pkg/merkletree 1.438s +? github.com/Overclock-Validator/mithril/pkg/metrics [no test files] +ok github.com/Overclock-Validator/mithril/pkg/replay 4.394s +ok github.com/Overclock-Validator/mithril/pkg/sbpf 1.486s +--- FAIL: TestExecute_Tx_BpfLoader_Write_Success (0.00s) + sealevel_bpf_loader_test.go:147: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:147 + Error: Not equal: + expected: () + actual : *errors.errorString(&errors.errorString{s:"InstrErrInvalidInstructionData"}) + Test: TestExecute_Tx_BpfLoader_Write_Success + sealevel_bpf_loader_test.go:156: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:156 + Error: Not equal: + expected: true + actual : false + Test: TestExecute_Tx_BpfLoader_Write_Success +--- FAIL: TestExecute_Tx_BpfLoader_Write_Offset_Too_Large_Failure (0.00s) + sealevel_bpf_loader_test.go:207: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:207 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrAccountDataTooSmall"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=27) "InstrErrAccountDataTooSmall" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_Write_Offset_Too_Large_Failure +--- FAIL: TestExecute_Tx_BpfLoader_Write_Buffer_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:258: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:258 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_Write_Buffer_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_Write_Incorrect_Authority_Failure (0.00s) + sealevel_bpf_loader_test.go:314: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:314 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrIncorrectAuthority"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=26) "InstrErrIncorrectAuthority" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_Write_Incorrect_Authority_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthority_Not_Enough_Instr_Accts_Failure (0.00s) + sealevel_bpf_loader_test.go:350: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:350 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrNotEnoughAccountKeys"} + actual : &errors.errorString{s:"InstrErrMissingAccount"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=28) "InstrErrNotEnoughAccountKeys" + + s: (string) (len=22) "InstrErrMissingAccount" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthority_Not_Enough_Instr_Accts_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Not_Enough_Instr_Accts_Failure (0.00s) + sealevel_bpf_loader_test.go:860: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:860 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrNotEnoughAccountKeys"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=28) "InstrErrNotEnoughAccountKeys" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Not_Enough_Instr_Accts_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success (0.00s) + sealevel_bpf_loader_test.go:907: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:907 + Error: Not equal: + expected: () + actual : *errors.errorString(&errors.errorString{s:"InstrErrInvalidInstructionData"}) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success + sealevel_bpf_loader_test.go:916: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:916 + Error: Not equal: + expected: solana.PublicKey{0xb9, 0x48, 0x47, 0x2a, 0xf7, 0x91, 0xfe, 0x1b, 0x56, 0x76, 0x65, 0xc, 0xd2, 0xa7, 0x3a, 0x5c, 0xbb, 0x1, 0x84, 0xa1, 0xe3, 0xf9, 0x82, 0x1f, 0x2d, 0xeb, 0xa4, 0x6c, 0x16, 0xa5, 0x6a, 0x77} + actual : solana.PublicKey{0x9b, 0xaf, 0x7f, 0xbd, 0x59, 0x62, 0xe8, 0x87, 0x25, 0xf0, 0xd6, 0x82, 0x94, 0x6, 0x8f, 0xfd, 0xe0, 0xbb, 0x1d, 0xd5, 0xb8, 0x4a, 0xb7, 0xc9, 0x9, 0x36, 0xce, 0xea, 0x9c, 0xc1, 0x50, 0xd4} + + Diff: + --- Expected + +++ Actual + @@ -1,4 +1,4 @@ + (solana.PublicKey) (len=32) { + - 00000000 b9 48 47 2a f7 91 fe 1b 56 76 65 0c d2 a7 3a 5c |.HG*....Vve...:\| + - 00000010 bb 01 84 a1 e3 f9 82 1f 2d eb a4 6c 16 a5 6a 77 |........-..l..jw| + + 00000000 9b af 7f bd 59 62 e8 87 25 f0 d6 82 94 06 8f fd |....Yb..%.......| + + 00000010 e0 bb 1d d5 b8 4a b7 c9 09 36 ce ea 9c c1 50 d4 |.....J...6....P.| + } + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success (0.00s) + sealevel_bpf_loader_test.go:963: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:963 + Error: Not equal: + expected: () + actual : *errors.errorString(&errors.errorString{s:"InstrErrInvalidInstructionData"}) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success + sealevel_bpf_loader_test.go:972: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:972 + Error: Not equal: + expected: solana.PublicKey{0x26, 0x9b, 0x63, 0x0, 0xf, 0x98, 0xd7, 0x33, 0xe3, 0x1e, 0xf, 0xd6, 0x56, 0xc3, 0x26, 0x7, 0x5e, 0xc2, 0x1f, 0xff, 0x4d, 0xb9, 0x51, 0xb6, 0xd0, 0xb1, 0x65, 0x90, 0xd9, 0x7b, 0x77, 0x65} + actual : solana.PublicKey{0x8f, 0xc3, 0xfc, 0x76, 0x79, 0xcf, 0xce, 0xe0, 0xef, 0x19, 0xef, 0x92, 0x32, 0x8f, 0xb, 0xa6, 0x41, 0xc2, 0xa1, 0x23, 0xad, 0x4f, 0x80, 0x3e, 0x73, 0x6e, 0xcb, 0x96, 0x14, 0xbe, 0x60, 0x96} + + Diff: + --- Expected + +++ Actual + @@ -1,4 +1,4 @@ + (solana.PublicKey) (len=32) { + - 00000000 26 9b 63 00 0f 98 d7 33 e3 1e 0f d6 56 c3 26 07 |&.c....3....V.&.| + - 00000010 5e c2 1f ff 4d b9 51 b6 d0 b1 65 90 d9 7b 77 65 |^...M.Q...e..{we| + + 00000000 8f c3 fc 76 79 cf ce e0 ef 19 ef 92 32 8f 0b a6 |...vy.......2...| + + 00000010 41 c2 a1 23 ad 4f 80 3e 73 6e cb 96 14 be 60 96 |A..#.O.>sn....`.| + } + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Immutable_Failure (0.00s) + sealevel_bpf_loader_test.go:1019: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:1019 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrImmutable"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=17) "InstrErrImmutable" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Immutable_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Wrong_Upgrade_Authority_Failure (0.00s) + sealevel_bpf_loader_test.go:1071: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:1071 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrIncorrectAuthority"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=26) "InstrErrIncorrectAuthority" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Wrong_Upgrade_Authority_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1118: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:1118 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_New_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1165: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:1165 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_New_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Uninitialized_Account_Failure (0.00s) + sealevel_bpf_loader_test.go:1212: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:1212 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrInvalidArgument"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=23) "InstrErrInvalidArgument" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Uninitialized_Account_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Immutable_Failure (0.00s) + sealevel_bpf_loader_test.go:1259: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:1259 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrImmutable"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=17) "InstrErrImmutable" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Immutable_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1306: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:1306 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_New_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1353: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:1353 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_New_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Wrong_Authority_Failure (0.00s) + sealevel_bpf_loader_test.go:1405: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:1405 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrIncorrectAuthority"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=26) "InstrErrIncorrectAuthority" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Wrong_Authority_Failure +--- FAIL: TestExecute_Tx_BpfLoader_Close_Buffer_Not_Enough_Accounts (0.00s) + sealevel_bpf_loader_test.go:1727: + Error Trace: /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:1727 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrNotEnoughAccountKeys"} + actual : &errors.errorString{s:"InstrErrMissingAccount"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=28) "InstrErrNotEnoughAccountKeys" + + s: (string) (len=22) "InstrErrMissingAccount" + }) + Test: TestExecute_Tx_BpfLoader_Close_Buffer_Not_Enough_Accounts +--- FAIL: TestExecute_Tx_BpfLoader_Close_ProgramData_Success (0.00s) +panic: runtime error: invalid memory address or nil pointer dereference [recovered, repanicked] +[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x10146565c] + +goroutine 180 [running]: +testing.tRunner.func1.2({0x102569820, 0x1026f2790}) + /Users/nikolashayes/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.4.darwin-arm64/src/testing/testing.go:1974 +0x2b8 +testing.tRunner.func1() + /Users/nikolashayes/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.4.darwin-arm64/src/testing/testing.go:1977 +0x460 +panic({0x102569820?, 0x1026f2790?}) + /Users/nikolashayes/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.4.darwin-arm64/src/runtime/panic.go:860 +0x12c +github.com/Overclock-Validator/mithril/pkg/sealevel.UpgradeableLoaderClose(0xc000598000, 0xc00058e000, 0xc0005a6000) + /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/bpf_loader.go:2703 +0x6fc +github.com/Overclock-Validator/mithril/pkg/sealevel.ProcessUpgradeableLoaderInstruction(0xc000598000) + /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/bpf_loader.go:2963 +0x230 +github.com/Overclock-Validator/mithril/pkg/sealevel.BpfLoaderProgramExecute(0xc000598000) + /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/bpf_loader.go:1461 +0x298 +github.com/Overclock-Validator/mithril/pkg/sealevel.(*ExecutionCtx).ExecuteInstruction(0xc000598000) + /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/execution_ctx.go:340 +0x358 +github.com/Overclock-Validator/mithril/pkg/sealevel.(*ExecutionCtx).ProcessInstruction(0xc000598000, {0xc00037602c, 0x4, 0x4}, {0xc00058c000, 0x4, 0x4}, {0xc000376068, 0x1, 0x1}) + /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/execution_ctx.go:286 +0x504 +github.com/Overclock-Validator/mithril/pkg/sealevel.TestExecute_Tx_BpfLoader_Close_ProgramData_Success(0xc00052f8c8) + /Users/nikolashayes/Desktop/programming/Codex/mithril-alpenglow-validator-performance/pkg/sealevel/sealevel_bpf_loader_test.go:1790 +0x1360 +testing.tRunner(0xc00052f8c8, 0x1026aaaa0) + /Users/nikolashayes/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.4.darwin-arm64/src/testing/testing.go:2036 +0x168 +created by testing.(*T).Run in goroutine 1 + /Users/nikolashayes/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.26.4.darwin-arm64/src/testing/testing.go:2101 +0x7c0 +FAIL github.com/Overclock-Validator/mithril/pkg/sealevel 0.694s +ok github.com/Overclock-Validator/mithril/pkg/sigverify 23.094s +ok github.com/Overclock-Validator/mithril/pkg/statsd 1.212s +ok github.com/Overclock-Validator/mithril/pkg/tpu/txfixture 53.483s +ok github.com/Overclock-Validator/mithril/pkg/turbine 9.166s +ok github.com/Overclock-Validator/mithril/cmd/mithril/node 1.879s +FAIL diff --git a/docs/results/validator-performance/2026-09-14/native-build.log b/docs/results/validator-performance/2026-09-14/native-build.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/results/validator-performance/2026-09-14/native-race.log b/docs/results/validator-performance/2026-09-14/native-race.log new file mode 100644 index 00000000..b726e75e --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/native-race.log @@ -0,0 +1,347 @@ +ok github.com/Overclock-Validator/mithril/pkg/accounts 1.082s +ok github.com/Overclock-Validator/mithril/pkg/alpenglow 4.930s +ok github.com/Overclock-Validator/mithril/pkg/block 1.131s +ok github.com/Overclock-Validator/mithril/pkg/blockprod 13.762s +ok github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler 1.288s +ok github.com/Overclock-Validator/mithril/pkg/config 1.007s +ok github.com/Overclock-Validator/mithril/pkg/consensus 3.049s +ok github.com/Overclock-Validator/mithril/pkg/costmodel 1.122s +ok github.com/Overclock-Validator/mithril/pkg/merkletree 1.230s +? github.com/Overclock-Validator/mithril/pkg/metrics [no test files] +ok github.com/Overclock-Validator/mithril/pkg/replay 2.107s +ok github.com/Overclock-Validator/mithril/pkg/sbpf 1.505s +--- FAIL: TestExecute_Tx_BpfLoader_Write_Success (0.00s) + sealevel_bpf_loader_test.go:147: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:147 + Error: Not equal: + expected: () + actual : *errors.errorString(&errors.errorString{s:"InstrErrInvalidInstructionData"}) + Test: TestExecute_Tx_BpfLoader_Write_Success + sealevel_bpf_loader_test.go:156: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:156 + Error: Not equal: + expected: true + actual : false + Test: TestExecute_Tx_BpfLoader_Write_Success +--- FAIL: TestExecute_Tx_BpfLoader_Write_Offset_Too_Large_Failure (0.00s) + sealevel_bpf_loader_test.go:207: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:207 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrAccountDataTooSmall"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=27) "InstrErrAccountDataTooSmall" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_Write_Offset_Too_Large_Failure +--- FAIL: TestExecute_Tx_BpfLoader_Write_Buffer_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:258: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:258 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_Write_Buffer_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_Write_Incorrect_Authority_Failure (0.00s) + sealevel_bpf_loader_test.go:314: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:314 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrIncorrectAuthority"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=26) "InstrErrIncorrectAuthority" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_Write_Incorrect_Authority_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthority_Not_Enough_Instr_Accts_Failure (0.00s) + sealevel_bpf_loader_test.go:350: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:350 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrNotEnoughAccountKeys"} + actual : &errors.errorString{s:"InstrErrMissingAccount"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=28) "InstrErrNotEnoughAccountKeys" + + s: (string) (len=22) "InstrErrMissingAccount" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthority_Not_Enough_Instr_Accts_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Not_Enough_Instr_Accts_Failure (0.00s) + sealevel_bpf_loader_test.go:860: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:860 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrNotEnoughAccountKeys"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=28) "InstrErrNotEnoughAccountKeys" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Not_Enough_Instr_Accts_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success (0.00s) + sealevel_bpf_loader_test.go:907: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:907 + Error: Not equal: + expected: () + actual : *errors.errorString(&errors.errorString{s:"InstrErrInvalidInstructionData"}) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success + sealevel_bpf_loader_test.go:916: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:916 + Error: Not equal: + expected: solana.PublicKey{0x7c, 0xbd, 0x4b, 0xff, 0x23, 0x2e, 0x27, 0x4a, 0x2d, 0xad, 0x25, 0x38, 0x38, 0x37, 0x7e, 0x16, 0xd6, 0xec, 0x5b, 0xde, 0x8, 0x95, 0x5c, 0x57, 0x2d, 0x5f, 0xd4, 0x69, 0x66, 0x14, 0xc4, 0x89} + actual : solana.PublicKey{0xfe, 0x7b, 0xda, 0xed, 0xd6, 0x67, 0xcc, 0x69, 0xcb, 0xa, 0xcf, 0x6b, 0x20, 0xc4, 0x8c, 0xb0, 0x36, 0xa1, 0x7e, 0x3b, 0xfa, 0xd5, 0x77, 0xf2, 0xd3, 0xc2, 0x98, 0x93, 0x2b, 0x9d, 0xb6, 0x7b} + + Diff: + --- Expected + +++ Actual + @@ -1,4 +1,4 @@ + (solana.PublicKey) (len=32) { + - 00000000 7c bd 4b ff 23 2e 27 4a 2d ad 25 38 38 37 7e 16 ||.K.#.'J-.%887~.| + - 00000010 d6 ec 5b de 08 95 5c 57 2d 5f d4 69 66 14 c4 89 |..[...\W-_.if...| + + 00000000 fe 7b da ed d6 67 cc 69 cb 0a cf 6b 20 c4 8c b0 |.{...g.i...k ...| + + 00000010 36 a1 7e 3b fa d5 77 f2 d3 c2 98 93 2b 9d b6 7b |6.~;..w.....+..{| + } + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Success +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success (0.00s) + sealevel_bpf_loader_test.go:963: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:963 + Error: Not equal: + expected: () + actual : *errors.errorString(&errors.errorString{s:"InstrErrInvalidInstructionData"}) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success + sealevel_bpf_loader_test.go:972: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:972 + Error: Not equal: + expected: solana.PublicKey{0x70, 0x1c, 0x71, 0xd3, 0x2a, 0x49, 0x54, 0x61, 0x18, 0xe, 0xa4, 0x21, 0x37, 0xe8, 0x7e, 0xcb, 0xd8, 0xc4, 0xe0, 0x1, 0xdd, 0x1e, 0x95, 0x91, 0xb8, 0xb, 0x62, 0xa4, 0xa0, 0x4e, 0x3d, 0x9c} + actual : solana.PublicKey{0xf7, 0x1f, 0x4e, 0xa8, 0x17, 0x6d, 0xe1, 0x3, 0x61, 0xb3, 0xf5, 0x47, 0xcc, 0x42, 0x2a, 0x18, 0xcf, 0x9c, 0xd4, 0x1e, 0x9b, 0x33, 0x62, 0x3, 0xc2, 0xd1, 0x4f, 0xd9, 0xea, 0x79, 0xf8, 0x49} + + Diff: + --- Expected + +++ Actual + @@ -1,4 +1,4 @@ + (solana.PublicKey) (len=32) { + - 00000000 70 1c 71 d3 2a 49 54 61 18 0e a4 21 37 e8 7e cb |p.q.*ITa...!7.~.| + - 00000010 d8 c4 e0 01 dd 1e 95 91 b8 0b 62 a4 a0 4e 3d 9c |..........b..N=.| + + 00000000 f7 1f 4e a8 17 6d e1 03 61 b3 f5 47 cc 42 2a 18 |..N..m..a..G.B*.| + + 00000010 cf 9c d4 1e 9b 33 62 03 c2 d1 4f d9 ea 79 f8 49 |.....3b...O..y.I| + } + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Success +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Immutable_Failure (0.00s) + sealevel_bpf_loader_test.go:1019: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:1019 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrImmutable"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=17) "InstrErrImmutable" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Immutable_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Wrong_Upgrade_Authority_Failure (0.00s) + sealevel_bpf_loader_test.go:1071: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:1071 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrIncorrectAuthority"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=26) "InstrErrIncorrectAuthority" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Wrong_Upgrade_Authority_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1118: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:1118 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_New_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1165: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:1165 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_New_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Uninitialized_Account_Failure (0.00s) + sealevel_bpf_loader_test.go:1212: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:1212 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrInvalidArgument"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=23) "InstrErrInvalidArgument" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_Buffer_Uninitialized_Account_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Immutable_Failure (0.00s) + sealevel_bpf_loader_test.go:1259: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:1259 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrImmutable"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=17) "InstrErrImmutable" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Immutable_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1306: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:1306 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_New_Authority_Didnt_Sign_Failure (0.00s) + sealevel_bpf_loader_test.go:1353: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:1353 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrMissingRequiredSignature"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=32) "InstrErrMissingRequiredSignature" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_New_Authority_Didnt_Sign_Failure +--- FAIL: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Wrong_Authority_Failure (0.00s) + sealevel_bpf_loader_test.go:1405: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:1405 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrIncorrectAuthority"} + actual : &errors.errorString{s:"InstrErrInvalidInstructionData"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=26) "InstrErrIncorrectAuthority" + + s: (string) (len=30) "InstrErrInvalidInstructionData" + }) + Test: TestExecute_Tx_BpfLoader_SetAuthorityChecked_ProgramData_Wrong_Authority_Failure +--- FAIL: TestExecute_Tx_BpfLoader_Close_Buffer_Not_Enough_Accounts (0.00s) + sealevel_bpf_loader_test.go:1727: + Error Trace: /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:1727 + Error: Not equal: + expected: &errors.errorString{s:"InstrErrNotEnoughAccountKeys"} + actual : &errors.errorString{s:"InstrErrMissingAccount"} + + Diff: + --- Expected + +++ Actual + @@ -1,3 +1,3 @@ + (*errors.errorString)({ + - s: (string) (len=28) "InstrErrNotEnoughAccountKeys" + + s: (string) (len=22) "InstrErrMissingAccount" + }) + Test: TestExecute_Tx_BpfLoader_Close_Buffer_Not_Enough_Accounts +--- FAIL: TestExecute_Tx_BpfLoader_Close_ProgramData_Success (0.00s) +panic: runtime error: invalid memory address or nil pointer dereference [recovered, repanicked] +[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x14d097a] + +goroutine 151 [running]: +testing.tRunner.func1.2({0x1968b80, 0x286daf0}) + /usr/local/go/src/testing/testing.go:1974 +0x419 +testing.tRunner.func1() + /usr/local/go/src/testing/testing.go:1977 +0x67f +panic({0x1968b80?, 0x286daf0?}) + /usr/local/go/src/runtime/panic.go:860 +0x13a +github.com/Overclock-Validator/mithril/pkg/sealevel.UpgradeableLoaderClose(0xc0002aecc0, 0xc0004e81c0, 0xc0001f7200) + /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/bpf_loader.go:2703 +0xa7a +github.com/Overclock-Validator/mithril/pkg/sealevel.ProcessUpgradeableLoaderInstruction(0xc0002aecc0) + /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/bpf_loader.go:2963 +0x325 +github.com/Overclock-Validator/mithril/pkg/sealevel.BpfLoaderProgramExecute(0xc0002aecc0) + /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/bpf_loader.go:1461 +0x3b3 +github.com/Overclock-Validator/mithril/pkg/sealevel.(*ExecutionCtx).ExecuteInstruction(0xc0002aecc0) + /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/execution_ctx.go:340 +0x4bf +github.com/Overclock-Validator/mithril/pkg/sealevel.(*ExecutionCtx).ProcessInstruction(0xc0002aecc0, {0xc000018c8c, 0x4, 0x4}, {0xc000485f00, 0x4, 0x4}, {0xc000018cc8, 0x1, 0x1}) + /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/execution_ctx.go:286 +0x6af +github.com/Overclock-Validator/mithril/pkg/sealevel.TestExecute_Tx_BpfLoader_Close_ProgramData_Success(0xc0004e46c8) + /srv/mithril-pr-consolidation-20260914/source/pkg/sealevel/sealevel_bpf_loader_test.go:1790 +0x1c18 +testing.tRunner(0xc0004e46c8, 0x1cb5eb8) + /usr/local/go/src/testing/testing.go:2036 +0x21d +created by testing.(*T).Run in goroutine 1 + /usr/local/go/src/testing/testing.go:2101 +0xb13 +FAIL github.com/Overclock-Validator/mithril/pkg/sealevel 0.199s +ok github.com/Overclock-Validator/mithril/pkg/sigverify 19.843s +ok github.com/Overclock-Validator/mithril/pkg/statsd 1.010s +ok github.com/Overclock-Validator/mithril/pkg/tpu/txfixture 31.497s +ok github.com/Overclock-Validator/mithril/pkg/turbine 5.471s +ok github.com/Overclock-Validator/mithril/cmd/mithril/node 1.138s +FAIL diff --git a/docs/results/validator-performance/2026-09-14/native-vet.log b/docs/results/validator-performance/2026-09-14/native-vet.log new file mode 100644 index 00000000..e69de29b diff --git a/docs/results/validator-performance/2026-09-14/post-deploy-voting-summary.json b/docs/results/validator-performance/2026-09-14/post-deploy-voting-summary.json new file mode 100644 index 00000000..1c2016f7 --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/post-deploy-voting-summary.json @@ -0,0 +1,91 @@ +{ + "utc": "2026-09-14T03:49:49Z", + "fast_captures": [ + { + "utc": "2026-09-14T03:39:38.170708+00:00", + "unique_fast_certificates": 612, + "included": 594, + "missing": 18, + "fast_percent": 97.05882352941177 + }, + { + "utc": "2026-09-14T03:44:39.613287+00:00", + "unique_fast_certificates": 555, + "included": 543, + "missing": 12, + "fast_percent": 97.83783783783784 + }, + { + "utc": "2026-09-14T03:49:40.954783+00:00", + "unique_fast_certificates": 629, + "included": 609, + "missing": 20, + "fast_percent": 96.82034976152623 + } + ], + "deduplicated_fast": { + "unique_proofs": 1796, + "included": 1746, + "missing": 50, + "replay_to_vote_ms": { + "count": 1792, + "median": 1.5542625, + "p95": 2.008444, + "max": 9.103538 + } + }, + "titan_windows": { + "1000": { + "actual_slots": 1000, + "voter": [ + 1000, + 997, + 3, + 99.7 + ], + "leader": [ + 12, + 12, + 0, + 100, + 80.81761, + 85.6667 + ] + }, + "5000": { + "actual_slots": 5000, + "voter": [ + 5000, + 4970, + 30, + 99.4 + ], + "leader": [ + 56, + 56, + 0, + 100, + 80.727763, + 85.5714 + ] + }, + "epoch": { + "actual_slots": 27516, + "voter": [ + 27514, + 27033, + 481, + 98.251799 + ], + "leader": [ + 346, + 346, + 0, + 100, + 80.837605, + 85.6879 + ] + } + }, + "note": "Observed windows; not controlled attribution to a single patch." +} diff --git a/docs/results/validator-performance/2026-09-14/tested-code-commit.txt b/docs/results/validator-performance/2026-09-14/tested-code-commit.txt new file mode 100644 index 00000000..03956e9e --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/tested-code-commit.txt @@ -0,0 +1 @@ +4a5a9761847a4fd8e7d60c72810082cf91fb73d5 diff --git a/docs/results/validator-performance/2026-09-14/validate-native.py b/docs/results/validator-performance/2026-09-14/validate-native.py new file mode 100644 index 00000000..b71751a9 --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/validate-native.py @@ -0,0 +1,43 @@ +import hashlib +import json +import os +import pathlib +import subprocess +import tarfile +import time + +root = pathlib.Path('/srv/mithril-pr-consolidation-20260914') +assert not root.exists(), 'refuse to overwrite an existing validation' +source = root / 'source' +source.mkdir(parents=True) +with tarfile.open('/srv/mithril-pr-consolidation-20260914-source.tar') as archive: + archive.extractall(source, filter='data') +env = dict(os.environ, PATH='/usr/local/go/bin:' + os.environ['PATH'], GOMAXPROCS='2') +packages = ['./pkg/accounts', './pkg/alpenglow', './pkg/block', './pkg/blockprod/...', + './pkg/config', './pkg/consensus', './pkg/costmodel', './pkg/merkletree', + './pkg/metrics', './pkg/replay', './pkg/sbpf', './pkg/sealevel', + './pkg/sigverify', './pkg/statsd', './pkg/tpu/txfixture', './pkg/turbine', + './cmd/mithril/node'] +checks = [ + ('native-race', ['go', 'test', '-race', '-p', '1', '-count=1', *packages]), + ('native-vet', ['go', 'vet', '-p', '1', *packages]), + ('native-build', ['go', 'build', '-p', '1', '-o', str(root / 'mithril-review'), './cmd/mithril']), +] +results = [] +for label, command in checks: + print('starting', label, flush=True) + started = time.time() + with (root / (label + '.log')).open('w') as output: + result = subprocess.run(['nice', '-n', '15', *command], cwd=source, env=env, + stdout=output, stderr=subprocess.STDOUT) + row = dict(label=label, command=command, exit_code=result.returncode, + elapsed_seconds=time.time() - started) + results.append(row) + (root / 'validation.json').write_text(json.dumps(results, indent=2)) + print(json.dumps(row), flush=True) + if result.returncode: + print((root / (label + '.log')).read_text()[-4000:], flush=True) +binary = root / 'mithril-review' +if binary.exists(): + print('review_binary_sha256', hashlib.sha256(binary.read_bytes()).hexdigest(), flush=True) +raise SystemExit(any(row['exit_code'] for row in results)) diff --git a/docs/results/validator-performance/2026-09-14/validation.json b/docs/results/validator-performance/2026-09-14/validation.json new file mode 100644 index 00000000..a347eb56 --- /dev/null +++ b/docs/results/validator-performance/2026-09-14/validation.json @@ -0,0 +1,74 @@ +[ + { + "label": "native-race", + "command": [ + "go", + "test", + "-race", + "-p", + "1", + "-count=1", + "./pkg/accounts", + "./pkg/alpenglow", + "./pkg/block", + "./pkg/blockprod/...", + "./pkg/config", + "./pkg/consensus", + "./pkg/costmodel", + "./pkg/merkletree", + "./pkg/metrics", + "./pkg/replay", + "./pkg/sbpf", + "./pkg/sealevel", + "./pkg/sigverify", + "./pkg/statsd", + "./pkg/tpu/txfixture", + "./pkg/turbine", + "./cmd/mithril/node" + ], + "exit_code": 1, + "elapsed_seconds": 124.60674452781677 + }, + { + "label": "native-vet", + "command": [ + "go", + "vet", + "-p", + "1", + "./pkg/accounts", + "./pkg/alpenglow", + "./pkg/block", + "./pkg/blockprod/...", + "./pkg/config", + "./pkg/consensus", + "./pkg/costmodel", + "./pkg/merkletree", + "./pkg/metrics", + "./pkg/replay", + "./pkg/sbpf", + "./pkg/sealevel", + "./pkg/sigverify", + "./pkg/statsd", + "./pkg/tpu/txfixture", + "./pkg/turbine", + "./cmd/mithril/node" + ], + "exit_code": 0, + "elapsed_seconds": 10.573910236358643 + }, + { + "label": "native-build", + "command": [ + "go", + "build", + "-p", + "1", + "-o", + "/srv/mithril-pr-consolidation-20260914/mithril-review", + "./cmd/mithril" + ], + "exit_code": 0, + "elapsed_seconds": 3.2196364402770996 + } +] \ No newline at end of file From b24de8e616b28eccd1f86c3384a1af96bf9624c0 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Sun, 13 Sep 2026 23:26:26 -0500 Subject: [PATCH 19/22] Preserve raw test-log whitespace in generated evidence --- .gitattributes | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index d6ca540f..d64360d9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,7 @@ # Keep raw benchmark evidence available without overwhelming the review. -# Go's CPU-identification line may include trailing padding; retain raw bytes. +# Retain raw Go CPU padding and test-framework space/tab indentation. docs/results/**/*.json linguist-generated=true docs/results/**/*.jsonl linguist-generated=true docs/results/**/*.txt linguist-generated=true whitespace=-blank-at-eol -docs/results/**/*.log linguist-generated=true whitespace=-blank-at-eol +docs/results/**/*.log linguist-generated=true -whitespace docs/results/**/*.tar.gz linguist-generated=true From 667d002f3241e81e9578648351449a459d21572c Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Mon, 14 Sep 2026 00:20:11 -0500 Subject: [PATCH 20/22] Bound scheduler transaction references in both priority heaps --- pkg/blockprod/scheduler/buffer.go | 64 ++++--- pkg/blockprod/scheduler/buffer_order_test.go | 5 +- .../scheduler/buffer_retention_test.go | 158 ++++++++++++++++++ 3 files changed, 189 insertions(+), 38 deletions(-) create mode 100644 pkg/blockprod/scheduler/buffer_retention_test.go diff --git a/pkg/blockprod/scheduler/buffer.go b/pkg/blockprod/scheduler/buffer.go index bb493529..38bb57a9 100644 --- a/pkg/blockprod/scheduler/buffer.go +++ b/pkg/blockprod/scheduler/buffer.go @@ -28,6 +28,9 @@ type entry struct { skipGen uint64 alive bool + // Indexes belong to Buffer.mu. Every buffered entry appears exactly once + // in each heap; -1 denotes absence while an entry is owned by the consumer. + maxIndex, minIndex int } type maxHeap []*entry @@ -38,15 +41,18 @@ func (h maxHeap) Less(i, j int) bool { } func (h maxHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] + h[i].maxIndex, h[j].maxIndex = i, j } func (h *maxHeap) Push(x any) { e := x.(*entry) + e.maxIndex = len(*h) *h = append(*h, e) } func (h *maxHeap) Pop() any { old := *h n := len(old) e := old[n-1] + e.maxIndex = -1 old[n-1] = nil *h = old[:n-1] return e @@ -64,15 +70,18 @@ func (h minHeap) Less(i, j int) bool { } func (h minHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] + h[i].minIndex, h[j].minIndex = i, j } func (h *minHeap) Push(x any) { e := x.(*entry) + e.minIndex = len(*h) *h = append(*h, e) } func (h *minHeap) Pop() any { old := *h n := len(old) e := old[n-1] + e.minIndex = -1 old[n-1] = nil *h = old[:n-1] return e @@ -162,21 +171,19 @@ func (b *Buffer) Cleanup(drop func(*entry) bool) int { b.mu.Lock() defer b.mu.Unlock() - var doomed []*entry + dropped := 0 for _, e := range b.byHash { - if e.alive && drop(e) { - doomed = append(doomed, e) + if drop(e) { + b.killLocked(e) + dropped++ } } - for _, e := range doomed { - b.killLocked(e) - } - b.drainDeadLocked() - return len(doomed) + return dropped } func (b *Buffer) pushAliveLocked(e *entry) { e.alive = true + e.maxIndex, e.minIndex = -1, -1 b.byHash[e.messageHash] = e heap.Push(&b.max, e) heap.Push(&b.min, e) @@ -187,17 +194,20 @@ func (b *Buffer) killLocked(e *entry) { if e == nil || !e.alive { return } + if e.maxIndex >= 0 { + heap.Remove(&b.max, e.maxIndex) + } + if e.minIndex >= 0 { + heap.Remove(&b.min, e.minIndex) + } e.alive = false delete(b.byHash, e.messageHash) b.alive-- } func (b *Buffer) popMaxAliveLocked() *entry { - for b.max.Len() > 0 { + if b.max.Len() > 0 { e := b.max.popEntry() - if !e.alive { - continue - } b.killLocked(e) return e } @@ -210,6 +220,7 @@ func (b *Buffer) popMaxAliveLocked() *entry { func (h *maxHeap) popEntry() *entry { nodes := *h root := nodes[0] + root.maxIndex = -1 last := nodes[len(nodes)-1] nodes[len(nodes)-1] = nil nodes = nodes[:len(nodes)-1] @@ -227,9 +238,11 @@ func (h *maxHeap) popEntry() *entry { break } nodes[i] = nodes[child] + nodes[i].maxIndex = i i = child } nodes[i] = last + last.maxIndex = i } *h = nodes return root @@ -243,38 +256,17 @@ func higherPriority(a, b *entry) bool { } func (b *Buffer) peekMinAliveLocked() *entry { - for b.min.Len() > 0 { - if b.min[0].alive { - return b.min[0] - } - heap.Pop(&b.min) + if b.min.Len() > 0 { + return b.min[0] } return nil } func (b *Buffer) popMinAliveLocked() *entry { - for b.min.Len() > 0 { + if b.min.Len() > 0 { e := heap.Pop(&b.min).(*entry) - if !e.alive { - continue - } b.killLocked(e) return e } return nil } - -func (b *Buffer) drainDeadLocked() { - for b.max.Len() > 0 { - if b.max[0].alive { - break - } - heap.Pop(&b.max) - } - for b.min.Len() > 0 { - if b.min[0].alive { - break - } - heap.Pop(&b.min) - } -} diff --git a/pkg/blockprod/scheduler/buffer_order_test.go b/pkg/blockprod/scheduler/buffer_order_test.go index 7ed342c3..11a091bf 100644 --- a/pkg/blockprod/scheduler/buffer_order_test.go +++ b/pkg/blockprod/scheduler/buffer_order_test.go @@ -35,8 +35,8 @@ func TestMaxHeapRemovalMatchesSortedOrder(t *testing.T) { } // Compare interleaved insertion, eviction, cleanup and removal with a small -// unsorted reference model. Reusing hashes after removal also exercises stale -// nodes left in the other heap without reusing the entry objects themselves. +// unsorted reference model. Reusing hashes after removal exercises replacement +// entries as well as counterpart removal from both indexed heaps. func TestBufferMixedOperationsMatchReference(t *testing.T) { const capacity = 64 rng := rand.New(rand.NewSource(418)) @@ -95,6 +95,7 @@ func TestBufferMixedOperationsMatchReference(t *testing.T) { require.Equal(t, want, b.Cleanup(func(e *entry) bool { return e.seq%11 == mod })) } require.Equal(t, len(model), b.Len(), "step=%d", step) + assertBufferIndexes(t, b) } for len(model) > 0 { want := best(true) diff --git a/pkg/blockprod/scheduler/buffer_retention_test.go b/pkg/blockprod/scheduler/buffer_retention_test.go new file mode 100644 index 00000000..6b24aac8 --- /dev/null +++ b/pkg/blockprod/scheduler/buffer_retention_test.go @@ -0,0 +1,158 @@ +package scheduler + +import ( + "encoding/binary" + "sync" + "testing" + + "github.com/gagliardetto/solana-go" + "github.com/stretchr/testify/require" +) + +func retainedTestEntry(id, reward uint64) *entry { + e := &entry{tx: &solana.Transaction{}, wire: []byte{1, 2, 3}, seq: id, reward: reward} + binary.LittleEndian.PutUint64(e.messageHash[:], id) + return e +} + +// Check both membership and backing-array references: shrinking a slice alone +// must not leave transaction payloads reachable outside its visible length. +func assertBufferIndexes(t *testing.T, b *Buffer) { + t.Helper() + b.mu.Lock() + defer b.mu.Unlock() + require.Equal(t, b.alive, len(b.byHash)) + require.Equal(t, b.alive, len(b.max)) + require.Equal(t, b.alive, len(b.min)) + require.LessOrEqual(t, b.alive, b.capacity) + for i, e := range b.max { + require.True(t, e.alive) + require.Equal(t, i, e.maxIndex) + require.Same(t, e, b.byHash[e.messageHash]) + require.Same(t, e, b.min[e.minIndex]) + if i > 0 { + require.False(t, b.max.Less(i, (i-1)/2)) + } + } + for i, e := range b.min { + require.Equal(t, i, e.minIndex) + require.Same(t, e, b.max[e.maxIndex]) + if i > 0 { + require.False(t, b.min.Less(i, (i-1)/2)) + } + } + for _, backing := range [][]*entry{b.max[:cap(b.max)], b.min[:cap(b.min)]} { + for _, e := range backing[b.alive:] { + require.Nil(t, e) + } + } +} + +func TestBufferConsumedEntriesReleaseBothHeapReferences(t *testing.T) { + b := NewBuffer(256) + pinned := retainedTestEntry(0, 1) + b.Insert(pinned) + for i := uint64(1); i <= 100000; i++ { + e := retainedTestEntry(i, 2) + result, _ := b.Insert(e) + require.Equal(t, InsertAccepted, result) + require.Same(t, e, b.PopMax()) + // The consumer still owns usable payloads after index removal. + require.NotNil(t, e.tx) + require.Equal(t, []byte{1, 2, 3}, e.wire) + if i%1000 == 0 { + assertBufferIndexes(t, b) + } + } + b.Cleanup(func(*entry) bool { return false }) + assertBufferIndexes(t, b) + t.Logf("capacity=%d active=%d max_refs=%d min_refs=%d", b.capacity, b.Len(), len(b.max), len(b.min)) + require.Same(t, pinned, b.PopMax()) + assertBufferIndexes(t, b) +} + +func TestBufferEvictionAndCleanupReleaseBothHeapReferences(t *testing.T) { + b := NewBuffer(2) + pinned := retainedTestEntry(0, 1000000) + b.Insert(pinned) + previous := retainedTestEntry(1, 1) + b.Insert(previous) + for i := uint64(2); i < 10000; i++ { + next := retainedTestEntry(i, i) + result, evicted := b.Insert(next) + require.Equal(t, InsertAccepted, result) + require.Same(t, previous, evicted) + require.False(t, evicted.alive) + require.Equal(t, -1, evicted.maxIndex) + require.Equal(t, -1, evicted.minIndex) + previous = next + if i%100 == 0 { + assertBufferIndexes(t, b) + } + } + require.Equal(t, 1, b.Cleanup(func(e *entry) bool { return e != pinned })) + assertBufferIndexes(t, b) + require.Same(t, pinned, b.PopMax()) + assertBufferIndexes(t, b) +} + +func TestBufferRepeatedRebufferPreservesNewHigherPriorityArrivals(t *testing.T) { + s := New(nil) + s.bankGen = 1 + skipped := retainedTestEntry(1, 10) + skipped.skipGen = s.bankGen + s.buffer.Insert(skipped) + for i := uint64(2); i < 10002; i++ { + low := retainedTestEntry(i, 1) + s.buffer.Insert(low) + picked, retry := s.popSchedulable(s.bankGen) + require.Same(t, low, picked) + require.Equal(t, []*entry{skipped}, retry) + for _, e := range retry { + s.rebuffer(e) + } + if i%100 == 0 { + assertBufferIndexes(t, s.buffer) + } + } + // Preserve the existing scan/retry policy when a higher-fee packet arrives. + high := retainedTestEntry(20000, 20) + s.buffer.Insert(high) + picked, retry := s.popSchedulable(s.bankGen) + require.Same(t, high, picked) + require.Empty(t, retry) + // A later bank may retry the previously skipped transaction. + s.bankGen++ + picked, retry = s.popSchedulable(s.bankGen) + require.Same(t, skipped, picked) + require.Empty(t, retry) + assertBufferIndexes(t, s.buffer) +} + +func TestBufferConcurrentInsertRemovalAndCleanup(t *testing.T) { + b := NewBuffer(64) + var workers sync.WaitGroup + for worker := uint64(0); worker < 4; worker++ { + workers.Go(func() { + for i := uint64(0); i < 2000; i++ { + id := worker*2000 + i + b.Insert(retainedTestEntry(id, id%17)) + } + }) + } + workers.Go(func() { + for i := 0; i < 8000; i++ { + b.PopMax() + } + }) + workers.Go(func() { + for i := 0; i < 100; i++ { + b.Cleanup(func(e *entry) bool { return e.seq%3 == 0 }) + } + }) + workers.Wait() + assertBufferIndexes(t, b) + for b.PopMax() != nil { + } + assertBufferIndexes(t, b) +} From bcada839110c0c4a7656892a6f9a8c5670e106f0 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Mon, 14 Sep 2026 00:20:41 -0500 Subject: [PATCH 21/22] Run validator race regressions in CI and document queue bounds --- .github/workflows/go_build.yml | 25 ++++++ docs/alpenglow_validator_performance.md | 19 ++++- docs/leader_block_packing.md | 8 +- .../queue-retention/2026-09-14/README.md | 79 ++++++++++++++++++ .../2026-09-14/ci-race-local.log | 8 ++ .../2026-09-14/drain-1-baseline.log | 7 ++ .../2026-09-14/drain-1-candidate.log | 7 ++ .../2026-09-14/drain-2-baseline.log | 7 ++ .../2026-09-14/drain-2-candidate.log | 7 ++ .../2026-09-14/drain-3-baseline.log | 7 ++ .../2026-09-14/drain-3-candidate.log | 7 ++ .../2026-09-14/drain-runs.json | 80 +++++++++++++++++++ .../2026-09-14/drain-summary.json | 38 +++++++++ .../2026-09-14/queue-fixed-race.log | 1 + .../queue-retention/2026-09-14/retention.log | 4 + .../2026-09-14/README.md | 5 +- 16 files changed, 303 insertions(+), 6 deletions(-) create mode 100644 docs/results/queue-retention/2026-09-14/README.md create mode 100644 docs/results/queue-retention/2026-09-14/ci-race-local.log create mode 100644 docs/results/queue-retention/2026-09-14/drain-1-baseline.log create mode 100644 docs/results/queue-retention/2026-09-14/drain-1-candidate.log create mode 100644 docs/results/queue-retention/2026-09-14/drain-2-baseline.log create mode 100644 docs/results/queue-retention/2026-09-14/drain-2-candidate.log create mode 100644 docs/results/queue-retention/2026-09-14/drain-3-baseline.log create mode 100644 docs/results/queue-retention/2026-09-14/drain-3-candidate.log create mode 100644 docs/results/queue-retention/2026-09-14/drain-runs.json create mode 100644 docs/results/queue-retention/2026-09-14/drain-summary.json create mode 100644 docs/results/queue-retention/2026-09-14/queue-fixed-race.log create mode 100644 docs/results/queue-retention/2026-09-14/retention.log diff --git a/.github/workflows/go_build.yml b/.github/workflows/go_build.yml index 50383467..bdc68590 100644 --- a/.github/workflows/go_build.yml +++ b/.github/workflows/go_build.yml @@ -17,3 +17,28 @@ jobs: - name: Build run: go build -v ./cmd/mithril + + regression-tests: + runs-on: ubuntu-latest + timeout-minutes: 20 + permissions: + contents: read + env: + GOMAXPROCS: "2" + steps: + - uses: actions/checkout@v3 + + - name: Setup Go + uses: actions/setup-go@v4 + with: + go-version: 1.26.4 + + - name: Voting, checkpoint, streaming and scheduler race regressions + # Run the complete affected package suites, including subprocess crash + # recovery and cancellation tests. The independent sealevel suite has + # known base-branch failures documented in the validation report. + run: >- + go test -race -p 2 -count=1 + ./pkg/alpenglow ./pkg/consensus ./pkg/replay + ./pkg/turbine ./pkg/sigverify ./pkg/blockprod/... + ./cmd/mithril/node diff --git a/docs/alpenglow_validator_performance.md b/docs/alpenglow_validator_performance.md index f81b8d4e..45b9fec1 100644 --- a/docs/alpenglow_validator_performance.md +++ b/docs/alpenglow_validator_performance.md @@ -18,7 +18,9 @@ The commits preserve the implementation sequence and can be reviewed by subsyste 2. **Leader packing:** decode and statically prepare owned transactions before leadership, revalidate bank-dependent inputs, reduce scratch allocation, compute signature roots without unused proof nodes, and improve scheduler - ordering. Expose queue capacity and completion-reserve settings while keeping + ordering. Remove consumed, evicted and expired entries from both indexed + priority heaps so retained references stay bounded by queue capacity. Expose + queue capacity and completion-reserve settings while keeping their defaults. Correct slot-duration-dependent resource budgets. [Design and synthetic block tests](leader_block_packing.md). 3. **Observer and certificate compatibility:** enable existing safe VM pooling @@ -110,8 +112,12 @@ tests and a clean live restart do not qualify host power-loss behavior. A halted cluster can leave conservative crash recovery waiting indefinitely. Preserve the reservation and latest history across application or AccountsDB rollback. -The separately observed scheduler heap retention issue is not fixed here, and -the remaining FAST/Titan reward omissions are not assumed to be solved. Server +The scheduler now removes entries from both heaps, including repeated cross-slot +rebuffering. Its existing scan/retry policy is preserved: newly arrived +higher-priority transactions remain eligible for normal selection. The queue +retention fix does not establish a measured improvement in block fullness or +FAST participation. Remaining FAST/Titan reward omissions are not assumed to +be solved. Server loader scheduling, faucet automation, monitoring scripts and validator keys are not part of the product changes in this PR. @@ -122,3 +128,10 @@ results and the baseline BPF-loader failures are recorded in [the validation report](results/validator-performance/2026-09-14/README.md). Archived benchmark output is retained verbatim and marked as generated for review; the Markdown explanations and executable test harnesses remain visible. + +CI additionally runs complete race suites for Alpenglow, consensus, replay, +Turbine, signature verification, block production/scheduling and node startup. +These include signing-reservation recovery, history-writer ordering, checkpoint +capture, streaming cancellation and queue-retention regressions. The separately +documented base failures in the sealevel suite remain outside this selected CI +command. [Queue follow-up validation](results/queue-retention/2026-09-14/README.md). diff --git a/docs/leader_block_packing.md b/docs/leader_block_packing.md index c650221e..332c1a81 100644 --- a/docs/leader_block_packing.md +++ b/docs/leader_block_packing.md @@ -10,8 +10,12 @@ execution and all resource budgets remain bank-dependent checks. Leader execution reuses borrowed-account scratch and skips detailed replay stage timers. Missing-current-bank lookup errors defer base58 formatting until used, avoiding wasted work before parent lookup. Entry Merkle hashing retains -only the root-building scratch, and queue removal avoids heap interface dispatch -and unused index updates while preserving priority/FIFO order. +only the root-building scratch, and max-heap removal avoids heap interface dispatch +while preserving priority/FIFO order. Both priority heaps now track entry +indexes so consumption, eviction and expiry remove every buffer reference. +Repeated rebuffering reuses the caller's intact transaction without accumulating +duplicate heap references. The existing slot-local skip scanning policy remains +unchanged, including selection of newly arrived higher-priority transactions. ## Capacity and protocol limits diff --git a/docs/results/queue-retention/2026-09-14/README.md b/docs/results/queue-retention/2026-09-14/README.md new file mode 100644 index 00000000..ef631bbd --- /dev/null +++ b/docs/results/queue-retention/2026-09-14/README.md @@ -0,0 +1,79 @@ +# Bounded scheduler references and regression CI + +Follow-up to PR 279 at `b24de8e616b28eccd1f86c3384a1af96bf9624c0`, with the +queue fix in `667d002f`. This bounds scheduler references and adds regression +CI. The existing slot-local skip scanning/rebuffering policy is unchanged; +`scheduler.go` is identical to the comparison commit. + +## Queue lifetime + +Each entry now records its position in both priority heaps. Consumption, +eviction and expiration remove its counterpart directly, clear the removed +backing-array pointer and release the deduplication-map reference. Returned +transactions retain their owned wire, decoded and prepared data for the caller; +rebuffering inserts exactly one reference in each heap again. Neither priority +ordering, equal-reward FIFO, capacity, fee scoring nor cross-slot retry rules +change. + +The regression keeps one low-reward entry while inserting and consuming 100,000 +higher-reward entries. With capacity 256, the old algorithm retained 100,001 +min-heap references. The candidate finishes with **one active entry and one +reference in each heap**, without requiring a later cleanup sweep. Additional +tests cover symmetric eviction, expiration, repeated rebuffering, new +higher-priority arrivals, retry in a later bank, concurrent operations, exact +reference-model ordering and zeroed slice storage beyond the visible length. + +Both heaps' lengths and map membership equal the number of buffered entries. +Backing-array allocation can retain its high-water capacity, but unused positions +are nil and cannot retain transaction payloads. + +## Validation + +Apple M4 Pro, Go 1.26.4. The scheduler race suite passed with GOMAXPROCS=4. +Scheduler vet and a complete `./cmd/mithril` build also passed. +The exact new CI command also passed locally with GOMAXPROCS=2: + +```sh +GOMAXPROCS=2 go test -race -p 2 -count=1 \ + ./pkg/alpenglow ./pkg/consensus ./pkg/replay \ + ./pkg/turbine ./pkg/sigverify ./pkg/blockprod/... ./cmd/mithril/node +``` + +The new `regression-tests` GitHub job runs these complete package suites, +including reservation/crash recovery, background history ordering, checkpoint +capture and streaming cancellation, alongside the existing build job. This is +not the full repository suite; pre-existing sealevel failures remain documented +in the [combined validation report](../../validator-performance/2026-09-14/README.md). + +Logs: [scheduler race](queue-fixed-race.log), [CI command locally](ci-race-local.log), +[retention result](retention.log). + +## Removal-cost tradeoff + +Three alternating paired rounds of the existing `BenchmarkBufferDrain`, +GOMAXPROCS=1, each selecting all 120,000 transactions from a prefilled queue. +Queue construction and transaction preparation are excluded. Baseline uses the +PR's `b24de8e6` buffer; candidate uses indexed counterpart removal. Both run the +same benchmark. Timing excludes GC costs outside the timed region and does not +measure a sustained validator workload. + +| Priority distribution | Baseline median | Candidate median | +| --- | ---: | ---: | +| Equal rewards / FIFO | 143.0 ns/pop | 188.0 ns/pop | +| Mixed rewards | 254.0 ns/pop | 274.2 ns/pop | + +Both report zero timed allocations per pop. Equal-reward candidate samples were +188.0 / 302.4 / 183.5 ns, so the raw variability should not be hidden behind the +median. These measurements show the direct removal cost, not a net validator +speedup. Memory retention is bounded; the effect on block fullness, garbage +collection and FAST omissions remains unmeasured. No live deployment or +validator restart was performed for this follow-up. + +```sh +GOMAXPROCS=1 go test ./pkg/blockprod/scheduler -run '^$' \ + -bench '^BenchmarkBufferDrain$' -benchtime=120000x -count=3 +``` + +[All sample values](drain-summary.json), [execution order](drain-runs.json), +raw baseline/candidate logs alongside this report. These results are local M4 +measurements and must not be substituted for the earlier Zen 5 bank benchmarks. diff --git a/docs/results/queue-retention/2026-09-14/ci-race-local.log b/docs/results/queue-retention/2026-09-14/ci-race-local.log new file mode 100644 index 00000000..7fd86596 --- /dev/null +++ b/docs/results/queue-retention/2026-09-14/ci-race-local.log @@ -0,0 +1,8 @@ +ok github.com/Overclock-Validator/mithril/pkg/alpenglow 7.643s +ok github.com/Overclock-Validator/mithril/pkg/consensus 9.953s +ok github.com/Overclock-Validator/mithril/pkg/replay 4.578s +ok github.com/Overclock-Validator/mithril/pkg/turbine 9.324s +ok github.com/Overclock-Validator/mithril/pkg/sigverify 23.233s +ok github.com/Overclock-Validator/mithril/pkg/blockprod 21.894s +ok github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler 5.706s +ok github.com/Overclock-Validator/mithril/cmd/mithril/node 1.918s diff --git a/docs/results/queue-retention/2026-09-14/drain-1-baseline.log b/docs/results/queue-retention/2026-09-14/drain-1-baseline.log new file mode 100644 index 00000000..c525bd0d --- /dev/null +++ b/docs/results/queue-retention/2026-09-14/drain-1-baseline.log @@ -0,0 +1,7 @@ +goos: darwin +goarch: arm64 +pkg: github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler +cpu: Apple M4 Pro +BenchmarkBufferDrain/equal_rewards 120000 143.0 ns/op 0 B/op 0 allocs/op +BenchmarkBufferDrain/mixed_rewards 120000 240.2 ns/op 0 B/op 0 allocs/op +PASS diff --git a/docs/results/queue-retention/2026-09-14/drain-1-candidate.log b/docs/results/queue-retention/2026-09-14/drain-1-candidate.log new file mode 100644 index 00000000..42687082 --- /dev/null +++ b/docs/results/queue-retention/2026-09-14/drain-1-candidate.log @@ -0,0 +1,7 @@ +goos: darwin +goarch: arm64 +pkg: github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler +cpu: Apple M4 Pro +BenchmarkBufferDrain/equal_rewards 120000 188.0 ns/op 0 B/op 0 allocs/op +BenchmarkBufferDrain/mixed_rewards 120000 268.0 ns/op 0 B/op 0 allocs/op +PASS diff --git a/docs/results/queue-retention/2026-09-14/drain-2-baseline.log b/docs/results/queue-retention/2026-09-14/drain-2-baseline.log new file mode 100644 index 00000000..42fe15e6 --- /dev/null +++ b/docs/results/queue-retention/2026-09-14/drain-2-baseline.log @@ -0,0 +1,7 @@ +goos: darwin +goarch: arm64 +pkg: github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler +cpu: Apple M4 Pro +BenchmarkBufferDrain/equal_rewards 120000 147.6 ns/op 0 B/op 0 allocs/op +BenchmarkBufferDrain/mixed_rewards 120000 261.2 ns/op 0 B/op 0 allocs/op +PASS diff --git a/docs/results/queue-retention/2026-09-14/drain-2-candidate.log b/docs/results/queue-retention/2026-09-14/drain-2-candidate.log new file mode 100644 index 00000000..3aeae2d9 --- /dev/null +++ b/docs/results/queue-retention/2026-09-14/drain-2-candidate.log @@ -0,0 +1,7 @@ +goos: darwin +goarch: arm64 +pkg: github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler +cpu: Apple M4 Pro +BenchmarkBufferDrain/equal_rewards 120000 302.4 ns/op 0 B/op 0 allocs/op +BenchmarkBufferDrain/mixed_rewards 120000 276.0 ns/op 0 B/op 0 allocs/op +PASS diff --git a/docs/results/queue-retention/2026-09-14/drain-3-baseline.log b/docs/results/queue-retention/2026-09-14/drain-3-baseline.log new file mode 100644 index 00000000..6a332b50 --- /dev/null +++ b/docs/results/queue-retention/2026-09-14/drain-3-baseline.log @@ -0,0 +1,7 @@ +goos: darwin +goarch: arm64 +pkg: github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler +cpu: Apple M4 Pro +BenchmarkBufferDrain/equal_rewards 120000 140.1 ns/op 0 B/op 0 allocs/op +BenchmarkBufferDrain/mixed_rewards 120000 254.0 ns/op 0 B/op 0 allocs/op +PASS diff --git a/docs/results/queue-retention/2026-09-14/drain-3-candidate.log b/docs/results/queue-retention/2026-09-14/drain-3-candidate.log new file mode 100644 index 00000000..b79128a8 --- /dev/null +++ b/docs/results/queue-retention/2026-09-14/drain-3-candidate.log @@ -0,0 +1,7 @@ +goos: darwin +goarch: arm64 +pkg: github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler +cpu: Apple M4 Pro +BenchmarkBufferDrain/equal_rewards 120000 183.5 ns/op 0 B/op 0 allocs/op +BenchmarkBufferDrain/mixed_rewards 120000 274.2 ns/op 0 B/op 0 allocs/op +PASS diff --git a/docs/results/queue-retention/2026-09-14/drain-runs.json b/docs/results/queue-retention/2026-09-14/drain-runs.json new file mode 100644 index 00000000..df2c7f65 --- /dev/null +++ b/docs/results/queue-retention/2026-09-14/drain-runs.json @@ -0,0 +1,80 @@ +[ + { + "round": 1, + "variant": "baseline", + "command": [ + "/Users/nikolashayes/Desktop/programming/Codex/mithril-pr-review-279-20260914/baseline-scheduler.test", + "-test.run=^$", + "-test.bench=^BenchmarkBufferDrain$", + "-test.benchtime=120000x", + "-test.count=1" + ], + "exit": 0, + "seconds": 0.6474292278289795 + }, + { + "round": 1, + "variant": "candidate", + "command": [ + "/Users/nikolashayes/Desktop/programming/Codex/mithril-pr-review-279-20260914/candidate-scheduler.test", + "-test.run=^$", + "-test.bench=^BenchmarkBufferDrain$", + "-test.benchtime=120000x", + "-test.count=1" + ], + "exit": 0, + "seconds": 0.6436319351196289 + }, + { + "round": 2, + "variant": "candidate", + "command": [ + "/Users/nikolashayes/Desktop/programming/Codex/mithril-pr-review-279-20260914/candidate-scheduler.test", + "-test.run=^$", + "-test.bench=^BenchmarkBufferDrain$", + "-test.benchtime=120000x", + "-test.count=1" + ], + "exit": 0, + "seconds": 0.306088924407959 + }, + { + "round": 2, + "variant": "baseline", + "command": [ + "/Users/nikolashayes/Desktop/programming/Codex/mithril-pr-review-279-20260914/baseline-scheduler.test", + "-test.run=^$", + "-test.bench=^BenchmarkBufferDrain$", + "-test.benchtime=120000x", + "-test.count=1" + ], + "exit": 0, + "seconds": 0.2681159973144531 + }, + { + "round": 3, + "variant": "baseline", + "command": [ + "/Users/nikolashayes/Desktop/programming/Codex/mithril-pr-review-279-20260914/baseline-scheduler.test", + "-test.run=^$", + "-test.bench=^BenchmarkBufferDrain$", + "-test.benchtime=120000x", + "-test.count=1" + ], + "exit": 0, + "seconds": 0.2570068836212158 + }, + { + "round": 3, + "variant": "candidate", + "command": [ + "/Users/nikolashayes/Desktop/programming/Codex/mithril-pr-review-279-20260914/candidate-scheduler.test", + "-test.run=^$", + "-test.bench=^BenchmarkBufferDrain$", + "-test.benchtime=120000x", + "-test.count=1" + ], + "exit": 0, + "seconds": 0.269057035446167 + } +] diff --git a/docs/results/queue-retention/2026-09-14/drain-summary.json b/docs/results/queue-retention/2026-09-14/drain-summary.json new file mode 100644 index 00000000..217c079f --- /dev/null +++ b/docs/results/queue-retention/2026-09-14/drain-summary.json @@ -0,0 +1,38 @@ +{ + "samples_ns_per_pop": { + "baseline": { + "equal_rewards": [ + 143.0, + 147.6, + 140.1 + ], + "mixed_rewards": [ + 240.2, + 261.2, + 254.0 + ] + }, + "candidate": { + "equal_rewards": [ + 188.0, + 302.4, + 183.5 + ], + "mixed_rewards": [ + 268.0, + 276.0, + 274.2 + ] + } + }, + "median_ns_per_pop": { + "baseline": { + "equal_rewards": 143.0, + "mixed_rewards": 254.0 + }, + "candidate": { + "equal_rewards": 188.0, + "mixed_rewards": 274.2 + } + } +} diff --git a/docs/results/queue-retention/2026-09-14/queue-fixed-race.log b/docs/results/queue-retention/2026-09-14/queue-fixed-race.log new file mode 100644 index 00000000..5241695b --- /dev/null +++ b/docs/results/queue-retention/2026-09-14/queue-fixed-race.log @@ -0,0 +1 @@ +ok github.com/Overclock-Validator/mithril/pkg/blockprod/scheduler 5.106s diff --git a/docs/results/queue-retention/2026-09-14/retention.log b/docs/results/queue-retention/2026-09-14/retention.log new file mode 100644 index 00000000..86627651 --- /dev/null +++ b/docs/results/queue-retention/2026-09-14/retention.log @@ -0,0 +1,4 @@ +=== RUN TestBufferConsumedEntriesReleaseBothHeapReferences + buffer_retention_test.go:69: capacity=256 active=1 max_refs=1 min_refs=1 +--- PASS: TestBufferConsumedEntriesReleaseBothHeapReferences (0.10s) +PASS diff --git a/docs/results/validator-performance/2026-09-14/README.md b/docs/results/validator-performance/2026-09-14/README.md index 6f169588..9c05284a 100644 --- a/docs/results/validator-performance/2026-09-14/README.md +++ b/docs/results/validator-performance/2026-09-14/README.md @@ -3,7 +3,10 @@ The combined runtime code was archived and tested at commit `4a5a9761847a4fd8e7d60c72810082cf91fb73d5` against base `33dde4050d9250557583395810799aaac2f54017`. Subsequent additions are review -documentation, attributes and evidence. The live validator was not redeployed. +documentation, attributes and evidence through `b24de8e6`. The later queue +retention fix and automated regression CI have a separate +[validation report](../../queue-retention/2026-09-14/README.md). +The live validator was not redeployed for these validation runs. This folder adds the checkpoint/voting evidence to the existing standalone streaming and leader-packing benchmark reports. From d1172b9ab47d614ca25d577b4d62a6c5ebccfe93 Mon Sep 17 00:00:00 2001 From: 7layermagik <7layermagik@users.noreply.github.com> Date: Mon, 14 Sep 2026 15:30:32 -0500 Subject: [PATCH 22/22] Fix vote deque ownership, prefetch reset accounting and voting startup order --- .github/workflows/go_build.yml | 5 +- cmd/mithril/configcmd/configcmd.go | 8 ++- cmd/mithril/configcmd/configcmd_test.go | 25 +++++++ cmd/mithril/node/node.go | 85 ++++++++++++----------- docs/alpenglow_validator_performance.md | 32 ++++++++- docs/reserved-vote-history.md | 20 ++++++ docs/transaction_sigverify_streaming.md | 13 +++- go.sum | 2 - pkg/sealevel/vote_deque_ownership_test.go | 29 ++++++++ pkg/sealevel/vote_program.go | 9 ++- pkg/turbine/entry_prefetch.go | 9 +++ pkg/turbine/entry_prefetch_test.go | 44 ++++++++++++ 12 files changed, 232 insertions(+), 49 deletions(-) create mode 100644 cmd/mithril/configcmd/configcmd_test.go create mode 100644 pkg/sealevel/vote_deque_ownership_test.go diff --git a/.github/workflows/go_build.yml b/.github/workflows/go_build.yml index bdc68590..4fa7306c 100644 --- a/.github/workflows/go_build.yml +++ b/.github/workflows/go_build.yml @@ -41,4 +41,7 @@ jobs: go test -race -p 2 -count=1 ./pkg/alpenglow ./pkg/consensus ./pkg/replay ./pkg/turbine ./pkg/sigverify ./pkg/blockprod/... - ./cmd/mithril/node + ./cmd/mithril/node ./cmd/mithril/configcmd + + - name: Vote-program deque ownership race regression + run: go test -race -count=1 ./pkg/sealevel -run '^TestProcessNewVoteStateOwnsRetainedDeque$' diff --git a/cmd/mithril/configcmd/configcmd.go b/cmd/mithril/configcmd/configcmd.go index 903ff68c..977fc728 100644 --- a/cmd/mithril/configcmd/configcmd.go +++ b/cmd/mithril/configcmd/configcmd.go @@ -261,7 +261,13 @@ max_rps = 8 # Verifier's own RPC budget (never shares the block-fe # ── Replay tuning ──────────────────────────────────────────────────────── [tuning] txpar = 24 # Validator auto-defaults to 2x CPU cores only when unset; explicit 0 = sequential -sigverify_backend = "auto" # auto|r51|generic|stdlib; stdlib uses Go's crypto/ed25519 impl after strict checks. +use_pool = true # Reuse execution buffers to reduce allocation + +[sigverify] +backend = "auto" # auto|r51|generic|stdlib +workers = 0 # 0 = min(2, GOMAXPROCS); explicit value overrides the shared transaction pool +batch_target = 8 # 4 or 8 signature lanes; available work runs immediately +disable_shred_overlap = false # Diagnostic fallback: verify after complete block assembly # ── Mithril's RPC server ───────────────────────────────────────────────── [rpc] diff --git a/cmd/mithril/configcmd/configcmd_test.go b/cmd/mithril/configcmd/configcmd_test.go new file mode 100644 index 00000000..5249ca1a --- /dev/null +++ b/cmd/mithril/configcmd/configcmd_test.go @@ -0,0 +1,25 @@ +package configcmd + +import ( + "strings" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/require" +) + +func TestStarterConfigSignatureVerification(t *testing.T) { + for _, validator := range []bool{false, true} { + v := viper.New() + v.SetConfigType("toml") + require.NoError(t, v.ReadConfig(strings.NewReader(generateStarterConfig(validator)))) + require.Equal(t, "auto", v.GetString("sigverify.backend")) + require.True(t, v.IsSet("sigverify.workers")) + require.Zero(t, v.GetInt("sigverify.workers")) + require.Equal(t, 8, v.GetInt("sigverify.batch_target")) + require.True(t, v.IsSet("sigverify.disable_shred_overlap")) + require.False(t, v.GetBool("sigverify.disable_shred_overlap")) + require.True(t, v.GetBool("tuning.use_pool")) + require.False(t, v.IsSet("tuning.sigverify_backend")) + } +} diff --git a/cmd/mithril/node/node.go b/cmd/mithril/node/node.go index 95afb631..66a81e3e 100644 --- a/cmd/mithril/node/node.go +++ b/cmd/mithril/node/node.go @@ -2681,47 +2681,6 @@ postBootstrap: waitToVoteSlot := effectiveWaitToVoteSlot(startupWallSlot, validatorWaitToVoteSlot) mlog.Log.Infof("ALPENGLOW voting startup watermark: wall_clock=%d configured_wait_to_vote=%d wait_to_vote=%d", startupWallSlot, validatorWaitToVoteSlot, waitToVoteSlot) - identityPubkey := solana.PrivateKey(validatorIdentity).PublicKey() - if err := consensusEngine.EnableVoting(consensusengine.VotingConfig{ - Identity: validatorIdentity, - AuthorizedVoter: validatorAuthorizedVoter, - VoteAccount: validatorVoteAccount, - HistoryDir: blockstorePath, - ReservedHistory: validatorReservedHistory, - InitializeVoteReservation: validatorInitializeReservation, - Genesis: solana.MustHashFromBase58(networkGenesisHash), - EpochForSlot: epochSchedule.GetEpoch, - SlotDuration: blockprod.AlpenglowSlotDuration, - WaitToVoteSlot: waitToVoteSlot, - ReadyToVote: func(slot uint64) bool { - wallSlot := global.WallClockSlot() - if liveSlot, ok := consensusEngine.AlpenglowLiveSlot(); ok { - wallSlot = liveSlot - } - return slot >= wallSlot || wallSlot-slot <= alpenglow.LeaderWindowSlots - }, - Peers: func(validators []alpenglow.ValidatorStake) []alpenglow.VotorPeer { - peers := make([]alpenglow.VotorPeer, 0, len(validators)) - seen := make(map[solana.PublicKey]struct{}, len(validators)) - for _, validator := range validators { - if validator.Stake == 0 || validator.NodePubkey == identityPubkey { - continue - } - addr, ok := sharedGossip.LookupAlpenglow(validator.NodePubkey) - if !ok { - continue - } - if _, duplicate := seen[validator.NodePubkey]; duplicate { - continue - } - seen[validator.NodePubkey] = struct{}{} - peers = append(peers, alpenglow.VotorPeer{Identity: validator.NodePubkey, Addr: addr}) - } - return peers - }, - }); err != nil { - klog.Fatalf("enable Alpenglow voting: %v", err) - } broadcaster, err := turbine.NewTurbineBroadcaster(turbine.TurbineBroadcasterConfig{ Self: solana.PrivateKey(validatorIdentity).PublicKey(), Peers: sharedGossip, @@ -2780,6 +2739,50 @@ postBootstrap: mlog.Log.Warnf("validator gossip TPU advertisement: %v", err) } + // Bind and validate local transports before consuming the durable clean + // voting marker. Startup configuration failures must not force recovery. + identityPubkey := solana.PrivateKey(validatorIdentity).PublicKey() + if err := consensusEngine.EnableVoting(consensusengine.VotingConfig{ + Identity: validatorIdentity, + AuthorizedVoter: validatorAuthorizedVoter, + VoteAccount: validatorVoteAccount, + HistoryDir: blockstorePath, + ReservedHistory: validatorReservedHistory, + InitializeVoteReservation: validatorInitializeReservation, + Genesis: solana.MustHashFromBase58(networkGenesisHash), + EpochForSlot: epochSchedule.GetEpoch, + SlotDuration: blockprod.AlpenglowSlotDuration, + WaitToVoteSlot: waitToVoteSlot, + ReadyToVote: func(slot uint64) bool { + wallSlot := global.WallClockSlot() + if liveSlot, ok := consensusEngine.AlpenglowLiveSlot(); ok { + wallSlot = liveSlot + } + return slot >= wallSlot || wallSlot-slot <= alpenglow.LeaderWindowSlots + }, + Peers: func(validators []alpenglow.ValidatorStake) []alpenglow.VotorPeer { + peers := make([]alpenglow.VotorPeer, 0, len(validators)) + seen := make(map[solana.PublicKey]struct{}, len(validators)) + for _, validator := range validators { + if validator.Stake == 0 || validator.NodePubkey == identityPubkey { + continue + } + addr, ok := sharedGossip.LookupAlpenglow(validator.NodePubkey) + if !ok { + continue + } + if _, duplicate := seen[validator.NodePubkey]; duplicate { + continue + } + seen[validator.NodePubkey] = struct{}{} + peers = append(peers, alpenglow.VotorPeer{Identity: validator.NodePubkey, Addr: addr}) + } + return peers + }, + }); err != nil { + klog.Fatalf("enable Alpenglow voting: %v", err) + } + rewardBuilder := rewardcerts.NewBuilder(rewardcerts.BuilderConfig{ RootSlot: global.Slot, BeforeBuild: consensusEngine.FlushAlpenglowRewardVotes, diff --git a/docs/alpenglow_validator_performance.md b/docs/alpenglow_validator_performance.md index 45b9fec1..60c522ad 100644 --- a/docs/alpenglow_validator_performance.md +++ b/docs/alpenglow_validator_performance.md @@ -23,8 +23,8 @@ The commits preserve the implementation sequence and can be reviewed by subsyste queue capacity and completion-reserve settings while keeping their defaults. Correct slot-duration-dependent resource budgets. [Design and synthetic block tests](leader_block_packing.md). -3. **Observer and certificate compatibility:** enable existing safe VM pooling - defaults, reduce observer statistics overhead, and use the Votor-compatible +3. **Observer and certificate compatibility:** enable VM pooling + defaults and give retained vote-state deques their own backing storage, reduce observer statistics overhead, and use the Votor-compatible certificate layout accepted by Agave/Firedancer. The regression fixture is an X.509 certificate, with no private signing key. 4. **Voting persistence and recovery:** optional durable signing reservations, @@ -135,3 +135,31 @@ These include signing-reservation recovery, history-writer ordering, checkpoint capture, streaming cancellation and queue-retention regressions. The separately documented base failures in the sealevel suite remain outside this selected CI command. [Queue follow-up validation](results/queue-retention/2026-09-14/README.md). + +## Review follow-up + +Retained vote-state deques now own their backing storage before TowerSync returns +its scratch deque to the pool. Reset prefetch generations retain their capacity +until both queued work and verification readers retire. Local transport setup +precedes consumption of the clean voting marker. Starter configs emit the +current `[sigverify]` keys; the two-worker default and default-mode voting +changes are documented in the linked configuration and recovery guides. + +The slot-time cost, account-data, shred and entry-byte table was checked against +[Agave v4.3.0-rc.1 slot_params.rs](https://github.com/anza-xyz/agave/blob/v4.3.0-rc.1/runtime/src/slot_params.rs), +including next-epoch activation, shortest-duration precedence and the 100/60 +account/block cost multiplier. No table correction was needed. + +Avoiding static preparation for duplicate or below-floor ingress transactions is +left for the separate block-production work; this follow-up changes no scheduler +admission policy. Existing raw benchmark evidence stays linked to this PR. + +Local validation of this follow-up passed: full race suites for Turbine, node, +config generation, signature verification, consensus, Alpenglow, replay and cost +model; vote-program tests selected by `Test.*Vote`; vet for the changed Go +packages; and a complete validator build. Both ownership and reset-capacity +regressions fail against the prior PR implementation and pass with these fixes. +The reset regression also passed ten race runs. CI now includes config generation +and the targeted vote-deque regression. The previously documented unrelated +full-sealevel failures remain outside these passing targeted suites. This +follow-up has not been deployed or benchmarked on the live validator. diff --git a/docs/reserved-vote-history.md b/docs/reserved-vote-history.md index 483c82db..00e85248 100644 --- a/docs/reserved-vote-history.md +++ b/docs/reserved-vote-history.md @@ -95,3 +95,23 @@ These are software tests, not a host power-loss qualification or formal proof of the full consensus protocol. `BenchmarkVoteHistoryPersistence` compares the actual serialization/write paths with 32 recorded notarizations; it does not measure block replay or full validator FAST participation. + +## Startup configuration and default-mode changes + +`--reserved-vote-history` and `--initialize-vote-reservation` are explicit CLI +flags; they are not TOML keys. Keep the mode flag in the service command line. +The initialization flag is one-time enrollment authorization and must be removed +after migration, rather than saved as a permanent configuration default. +Transport binding and advertised-address validation complete before voting is +enabled and before a clean marker is consumed, so those startup failures do not +turn a clean shutdown into uncertain recovery. + +Only the asynchronous history writer and reservation protocol are opt-in. +Synchronous mode also acquires the history-directory lock, refuses a directory +already migrated to reserved history, queues durable-root pruning behind replay, +and uses the retained pool/history voting floor instead of immediately retiring +votes at the latest finality certificate. Thus the default persistence method is +unchanged, but the full default voting path is not. If replay lags, the voter can +now emit skip votes for a slot already finalized by the network while that slot +remains inside the retained voting window. The leader reservation gate +applies when reservation mode is enabled. diff --git a/docs/transaction_sigverify_streaming.md b/docs/transaction_sigverify_streaming.md index 3baee788..02619d45 100644 --- a/docs/transaction_sigverify_streaming.md +++ b/docs/transaction_sigverify_streaming.md @@ -1,7 +1,7 @@ # Transaction signature verification during shred arrival Turbine verifies transaction signatures as complete entry batches arrive. The -default shared transaction pool has two workers and targets eight signature +default shared transaction pool has `min(2, GOMAXPROCS)` workers and targets eight signature lanes. A ready batch containing four transactions runs immediately; there is no timer or minimum occupancy requirement. The same policy handles live reception and repair catch-up without a mode transition or a 200 ms batching delay. @@ -120,3 +120,14 @@ measures direct ordering, authenticated-root reuse, and the four-vector job poli The [standalone PR review](results/streaming-pr-review/2026-09-13/README.md) records extraction onto current `alpenglow-dev`, the small shared component-boundary prerequisite, final allocation improvement, and the scope of the live trial. + +## Worker default compatibility + +The automatic transaction-verifier default changes from `(GOMAXPROCS + 1) / 2` workers to +`min(2, GOMAXPROCS)`, including when shred overlap is disabled. This favors spare +CPU capacity for execution and other verification at the tip. It is not a claim +of maximum catch-up throughput on every core count or backend. Set +`--sigverify-workers N` or `[sigverify] workers = N` explicitly when tuning a +larger machine; disabling overlap alone does not restore the previous worker +count. Existing two/four-worker contention measurements are in the September 12 +report above. No 32-core comparison was performed. diff --git a/go.sum b/go.sum index b5997c28..086dc43c 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,6 @@ github.com/Overclock-Validator/crypto v0.0.0-20250307094320-aaf52fac5261 h1:Y715 github.com/Overclock-Validator/crypto v0.0.0-20250307094320-aaf52fac5261/go.mod h1:ZhRHOaVg8I1gg0VK4wmqOQPnlgPgKFT9McZ+TCW/hBA= github.com/Overclock-Validator/gnark-crypto v0.0.0-20250309203346-2a67ed08a105 h1:mP6FWHZ8ddcmbE8UTrVVI2Mi2c24aqX/8p12Vn6zokQ= github.com/Overclock-Validator/gnark-crypto v0.0.0-20250309203346-2a67ed08a105/go.mod h1:Poczuq3dbt+CwyTKgOjGaEwJOMP7YxQobF7QhgNcguk= -github.com/Overclock-Validator/narya-ed25519 v0.0.0-20260726222623-da0d045dae9d h1:ipaL+9MHKeI8QIfWneId0VLa+STLRM1e6MnvZhQyhPU= -github.com/Overclock-Validator/narya-ed25519 v0.0.0-20260726222623-da0d045dae9d/go.mod h1:B7/xqV/5NtGJa8OlZAa9TRMHgeIE+VEJNiPzMP4FrIg= github.com/Overclock-Validator/narya-ed25519 v0.0.0-20260730051143-c265ee966713 h1:nRAD+snanlR/sX4W5rkrxr6+sYBZ3Hl2xZQ6HCpM0Hc= github.com/Overclock-Validator/narya-ed25519 v0.0.0-20260730051143-c265ee966713/go.mod h1:B7/xqV/5NtGJa8OlZAa9TRMHgeIE+VEJNiPzMP4FrIg= github.com/Overclock-Validator/solana-snapshot-finder-go v0.0.0-20260223201452-d8363b514fc0 h1:elgavEQb8l7Zn3gS3Y+2/98PlUylOWdlM3V1VumQ7mA= diff --git a/pkg/sealevel/vote_deque_ownership_test.go b/pkg/sealevel/vote_deque_ownership_test.go new file mode 100644 index 00000000..b7d50f43 --- /dev/null +++ b/pkg/sealevel/vote_deque_ownership_test.go @@ -0,0 +1,29 @@ +package sealevel + +import ( + "testing" + + "github.com/Overclock-Validator/mithril/pkg/features" + "github.com/gagliardetto/solana-go" + "github.com/gammazero/deque" + "github.com/stretchr/testify/require" +) + +func TestProcessNewVoteStateOwnsRetainedDeque(t *testing.T) { + // Model the TowerSync scratch deque being returned to its pool and reused. + scratch := new(deque.Deque[LandedVote]) + scratch.PushBack(LandedVote{Lockout: VoteLockout{Slot: 100, ConfirmationCount: 2}}) + scratch.PushBack(LandedVote{Lockout: VoteLockout{Slot: 101, ConfirmationCount: 1}}) + state := new(VoteState) + require.NoError(t, processNewVoteState(state, scratch, nil, nil, 0, 101, features.Features{})) + cached := newVoteState4FromCurrent(state, solana.PublicKey{}) + want := []LandedVote{state.Votes.At(0), state.Votes.At(1)} + + scratch.Clear() + scratch.PushBack(LandedVote{Lockout: VoteLockout{Slot: 200, ConfirmationCount: 2}}) + scratch.PushBack(LandedVote{Lockout: VoteLockout{Slot: 201, ConfirmationCount: 1}}) + for i, vote := range want { + require.Equal(t, vote, state.Votes.At(i)) + require.Equal(t, vote, cached.Votes.At(i)) + } +} diff --git a/pkg/sealevel/vote_program.go b/pkg/sealevel/vote_program.go index 43a8500c..679d1787 100644 --- a/pkg/sealevel/vote_program.go +++ b/pkg/sealevel/vote_program.go @@ -1951,7 +1951,14 @@ func processNewVoteState(voteState *VoteState, newState *deque.Deque[LandedVote] } voteState.RootSlot = newRoot - voteState.Votes = *newState + // newState may be a pooled deque. Own the backing storage before its + // caller returns it to the pool: the resulting state can escape into the + // shared vote cache after this instruction completes. + var owned deque.Deque[LandedVote] + for i := 0; i < newState.Len(); i++ { + owned.PushBack(newState.At(i)) + } + voteState.Votes = owned return nil } diff --git a/pkg/turbine/entry_prefetch.go b/pkg/turbine/entry_prefetch.go index 05171918..31083bdb 100644 --- a/pkg/turbine/entry_prefetch.go +++ b/pkg/turbine/entry_prefetch.go @@ -45,6 +45,7 @@ type slotEntryPrefetch struct { batches map[uint32]*prefetchedShredBatch next int queued, released bool + queueDone chan struct{} // closed after the queued/running token retires bytes int } @@ -110,6 +111,7 @@ func (p *entryPrefetchPool) enqueueLocked(s *slotState) { select { case p.jobs <- s: f.queued = true + f.queueDone = make(chan struct{}) default: } } @@ -121,6 +123,7 @@ func (p *entryPrefetchPool) run() { f := s.prefetch if p.closed || f.released || f.ctx.Err() != nil || p.a.slots[s.slot] != s || s.completing { f.queued = false + close(f.queueDone) p.a.mu.Unlock() continue } @@ -157,6 +160,7 @@ func (p *entryPrefetchPool) run() { } if batch == nil { f.queued = false + close(f.queueDone) p.a.mu.Unlock() continue } @@ -181,6 +185,7 @@ func (p *entryPrefetchPool) run() { close(ready) p.a.mu.Lock() f.queued = false + close(f.queueDone) p.enqueueLocked(s) p.a.mu.Unlock() } @@ -212,6 +217,7 @@ func (a *SlotAssembler) releasePrefetchLocked(s *slotState) { f.released = true f.cancel() p := f.pool + queueDone := f.queueDone p.cleanup.Add(1) go func() { defer p.cleanup.Done() @@ -221,6 +227,9 @@ func (a *SlotAssembler) releasePrefetchLocked(s *slotState) { b.verification.wait() } } + if queueDone != nil { + <-queueDone + } p.a.mu.Lock() p.slots-- p.bytes -= f.bytes diff --git a/pkg/turbine/entry_prefetch_test.go b/pkg/turbine/entry_prefetch_test.go index e22f514c..5f64912e 100644 --- a/pkg/turbine/entry_prefetch_test.go +++ b/pkg/turbine/entry_prefetch_test.go @@ -358,3 +358,47 @@ func TestEntryPrefetchCanceledCompletionCanRetrySameGeneration(t *testing.T) { require.NotNil(t, blk) require.True(t, blk.TransactionSignaturesVerified()) } + +func TestEntryPrefetchResetRetainsQueuedReservations(t *testing.T) { + a := NewSlotAssembler() + ctx, cancel := context.WithCancel(context.Background()) + // Hold workers until after reset so all tokens remain in the channel. + p := &entryPrefetchPool{a: a, ctx: ctx, cancel: cancel, jobs: make(chan *slotState, entryPrefetchSlots)} + a.entryPrefetch = p + startWorker := sync.OnceFunc(func() { + p.workers.Add(1) + go p.run() + }) + defer func() { + startWorker() + p.closeAndWait() + }() + for i := 0; i < entryPrefetchSlots; i++ { + s := &slotState{slot: uint64(i), completeBatches: []shredBatchRange{{0, 0}}} + a.mu.Lock() + a.slots[s.slot] = s + a.prefetchEntriesLocked(s) + a.releasePrefetchLocked(s) + a.mu.Unlock() + } + require.Equal(t, entryPrefetchSlots, len(p.jobs)) + // Cleanup must not admit another generation while stale queue tokens live. + require.Never(t, func() bool { + a.mu.Lock() + defer a.mu.Unlock() + return p.slots != entryPrefetchSlots + }, 50*time.Millisecond, time.Millisecond) + fresh := &slotState{slot: 100, completeBatches: []shredBatchRange{{0, 0}}} + a.mu.Lock() + a.prefetchEntriesLocked(fresh) + reserved := fresh.prefetch != nil + a.mu.Unlock() + // Start the worker before assertions so test failures cannot strand cleanup. + startWorker() + require.False(t, reserved) + p.cleanup.Wait() + a.mu.Lock() + slots := p.slots + a.mu.Unlock() + require.Zero(t, slots) +}