Help us shape the future of Go! Take the 2026 Go Developer Survey

bench

package
v1.30.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2026 License: BSD-3-Clause Imports: 10 Imported by: 0

README

platformvm/txs/bench — linearcodec vs native-ZAP harness

Measures the parse/build/field/workload deltas between the current linearcodec path (txs.Parse / codec.Manager.Marshal) and the native-ZAP path Blue is building under vms/platformvm/txs/zap_native.

Status

zap_native has landed AdvanceTimeTx as the LP-023 canary. That is the one tx type with a real native path today; benchmarks report the real 37× parse / 5.2× build lift for it.

Every other tx type (BaseTx, AddPermissionlessValidator, etc.) is still legacy-only on both sides because Blue's per-type accessors have not yet been written. The per-type tables in ../bench_results/RESULTS.md mark these _legacy only_ so the honest reader can tell which numbers reflect the real native path and which are baseline-only.

Reproduce

From repo root:

cd ~/work/lux/node

# Full bench (≈3 minutes per architecture at -benchtime=2s)
GOWORK=off go test -count=1 \
  -bench='^Benchmark' -benchmem -benchtime=2s -run='^$' \
  -cpuprofile=/tmp/bench_cpu.prof \
  ./vms/platformvm/txs/bench/

# Per-type parse only
GOWORK=off go test -count=1 -bench='^BenchmarkParse' -benchmem -benchtime=2s -run='^$' \
  ./vms/platformvm/txs/bench/

# Real-workload only
GOWORK=off go test -count=1 -bench='^BenchmarkWorkload' -benchmem -benchtime=5s -run='^$' \
  ./vms/platformvm/txs/bench/

# Alloc pressure (5s parse loop each codec, AdvanceTimeTx fixture)
GOWORK=off go test -count=1 -bench='^BenchmarkAllocPressure' -benchmem -benchtime=1x -run='^$' \
  ./vms/platformvm/txs/bench/

# Disable-legacy gate verification
GOWORK=off go test -count=1 -run TestLegacyCodecGate -v \
  ./vms/platformvm/txs/bench/

The bench scope is the v1 (current) codec only. v0 is read-only on the production node; benching its write path would measure dead code.

Files

  • fixtures.go — representative fixtures for each tx type (2-in/2-out base, realistic stake / signer / rewards-owner counts).
  • parse_bench_test.go — per-type Parse: legacy + native-AdvanceTime.
  • build_bench_test.go — per-type Build: legacy + native-AdvanceTime.
  • field_bench_test.go — field-access after-parse: legacy vs native.
  • workload_bench_test.go — 1000-tx mempool, 200-block parse.
  • alloc_bench_test.go — sustained-parse GC/alloc profile.
  • disable_legacy_test.go — LUXD_ENABLE_LEGACY_CODEC gate behavior (default off + subprocess on).
  • testdata/ — captured mainnet bytes when present.

Capturing real mainnet inputs

The synthetic mempool/block fixtures use the modal-mainnet mix documented in workload_bench_test.go::mainnetMix. For absolute ground-truth numbers, drop captured bytes into testdata/:

Mempool dump
POD=$(kubectl -n lux get pods -l app=luxd -o jsonpath='{.items[0].metadata.name}')
kubectl -n lux exec "$POD" -- curl -s http://localhost:9650/ext/bc/P \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"platform.getMempool"}' \
  > /tmp/mempool.json

# For each txID in the result, fetch the canonical signed bytes and
# pack into testdata/ as the length-prefixed stream the harness reads.
go run ../../../../../scripts/capture-mempool/main.go \
  --rpc http://luxd-0.lux.svc:9650 \
  --out testdata/mainnet-mempool-1000.bytes \
  --max 1000

(The capture-mempool helper is intentionally separate from this package — the bench harness should not pull in a kubectl/RPC stack at build time. Re-encode raw bytes into testdata/ as a one-shot.)

When testdata/mainnet-mempool-1000.bytes is present, the harness prefers it over the synthetic mix. The log line at bench start tells you which input was used.

Disable-legacy gate verification

The env gate is LUXD_ENABLE_LEGACY_CODEC=1 (per Blue's LP-023): native ZAP is the default; legacy is opt-in. The flag is read at zap_native.LegacyEnabled once per process.

The bench harness verifies two cases:

  1. TestLegacyCodecGateDefault — default; native ZAP picked for write; zap_native.LegacyEnabled == false.
  2. TestLegacyCodecGateEnabledViaSubprocess — re-execs with LUXD_ENABLE_LEGACY_CODEC=1; legacy enabled; pre-activation timestamps pick legacy for write, post-activation picks ZAP.

The subprocess pattern is required because zap_native.LegacyEnabled is initialized once at package load — flipping the env var mid-process is a no-op.

Important architecture note. The gate is enforced at the new ZAP-vs-legacy wire dispatcher (zap_native.IsZAPBytes and zap_native.ShouldUseZAPForWrite), NOT at platformvm/txs.Parse. The platformvm txs.Parse entry point owns the byte-preserving v0→TxID migration invariant: any validator bootstrapping from pre-activation history must be able to read v0 bytes regardless of the gate. Gating Parse would break this. The gate's enforcement point is correctly one layer up.

Caveats

  • Synthetic mempool fixtures repeat the same payload bytes. The codec doesn't know that; parse cost is measured honestly. But cache locality favors repeated parses. Drop a real mempool dump into testdata/ to escape this artifact.
  • Bench fixtures are at v1 only. v0 is the read-only read path on the production node; benching its write path would measure dead code.
  • Bench numbers on darwin/arm64 (M-series) do NOT predict linux/amd64. Run on the target architecture for production-relevant numbers — the results document records both.

Documentation

Overview

Package bench holds the linearcodec-vs-native-ZAP benchmark harness for the platformvm txs.

FIXTURE PHILOSOPHY: every tx the harness measures is built with field counts representative of mainnet — a typical AddPermissionlessValidator in production carries 2 inputs + 2 outputs + 1 stake-out + signer.PoP (the most common case as of 2026-06). Empty BaseTx{} fixtures are excluded from the headline numbers; they appear only as a "minimal payload" floor in the report to bound how much of the speedup is fixed-cost.

Index

Constants

This section is empty.

Variables

View Source
var MainnetAssetID = ids.ID{
	0x51, 0xc2, 0x4f, 0xe7, 0xee, 0x02, 0x01, 0xff,
	0x0f, 0x33, 0x5f, 0x51, 0x99, 0x28, 0xdb, 0x6e,
	0xef, 0x62, 0x24, 0x25, 0x45, 0x52, 0xc9, 0x6b,
	0x6b, 0x42, 0x5f, 0xbc, 0x18, 0xfa, 0x24, 0x3b,
}

MainnetAssetID is the canonical LUX asset ID on the mainnet P-Chain, used in every fixture so that benchmark inputs match production tx payloads byte-for-byte in the "asset ID" 32-byte slot.

Functions

func FixtureMap

func FixtureMap() map[string]txs.UnsignedTx

FixtureMap returns the named fixtures the bench harness iterates over. Add new tx types here as Blue lands accessors for them; the per-type bench is reflective over this map.

Insertion order matters for table output: this is the order the columns appear in RESULTS.md.

func MustMarshal

func MustMarshal(unsigned txs.UnsignedTx) []byte

MustMarshal panics on error; intended for fixture pre-encoding.

func MustMarshalSignedTx

func MustMarshalSignedTx(unsigned txs.UnsignedTx) []byte

MustMarshalSignedTx wraps an unsigned in a *txs.Tx with empty creds and returns the canonical signed-byte form. This matches the mempool/block path: txs on the wire are *txs.Tx, not bare unsigned.

func NewAddDelegatorTxFixture

func NewAddDelegatorTxFixture() *txs.AddDelegatorTx

NewAddDelegatorTxFixture is the delegator counterpart — same shape, no DelegationShares (delegators don't carry that field).

func NewAddPermissionlessDelegatorTxFixture

func NewAddPermissionlessDelegatorTxFixture() *txs.AddPermissionlessDelegatorTx

NewAddPermissionlessDelegatorTxFixture is the delegator companion to AddPermissionlessValidator. No Signer; no RewardsOwner split; just the single DelegationRewardsOwner. Smaller wire footprint than the validator fixture by ~200B (no PoP).

func NewAddPermissionlessValidatorTxFixture

func NewAddPermissionlessValidatorTxFixture() *txs.AddPermissionlessValidatorTx

NewAddPermissionlessValidatorTxFixture is the Banff-era primary-net validator with a full ProofOfPossession (a real mainnet validator tx has this signer). Field count: BaseTx(2/2) + Validator + ChainID + Signer(PoP) + StakeOuts(1) + ValidatorRewardsOwner + DelegatorRewardsOwner + DelegationShares = 8 top-level fields.

func NewAddValidatorTxFixture

func NewAddValidatorTxFixture() *txs.AddValidatorTx

NewAddValidatorTxFixture returns a legacy primary-network AddValidator with a realistic 2-in / 2-out base, 1 stake-out, and a rewards owner of size 1.

func NewAdvanceTimeTxFixture

func NewAdvanceTimeTxFixture() *txs.AdvanceTimeTx

NewAdvanceTimeTxFixture is the minimal scheduled-time-advance tx. 4 fields in the wire layout: codec version, type ID, then a single uint64. Smallest of the smalls.

func NewBaseTxFixture

func NewBaseTxFixture() *txs.BaseTx

NewBaseTxFixture returns the canonical "1 input / 1 output" BaseTx the way it appears in real mainnet bytes — the smallest non-trivial payload and the unit by which all other fixtures are bounded from below.

func NewBaseTxWithStakeableFixture

func NewBaseTxWithStakeableFixture() *txs.BaseTx

NewBaseTxWithStakeableFixture stresses the stakeable.LockOut path (slot 22). Real validator deposit txs use this; ignoring it would understate the codec cost.

func NewCreateChainTxFixture

func NewCreateChainTxFixture() *txs.CreateChainTx

NewCreateChainTxFixture is the legacy create-blockchain tx.

func NewCreateNetworkTxFixture

func NewCreateNetworkTxFixture() *txs.CreateNetworkTx

NewCreateNetworkTxFixture is the legacy create-network tx.

func NewExportTxFixture

func NewExportTxFixture() *txs.ExportTx

NewExportTxFixture covers the P→X (or P→C) export.

func NewImportTxFixture

func NewImportTxFixture() *txs.ImportTx

NewImportTxFixture covers the cross-chain import path; the ImportedInputs slice is what real X→P moves carry. Two imported inputs is the modal case (one for amount, one for fee).

func NewRewardValidatorTxFixture

func NewRewardValidatorTxFixture() *txs.RewardValidatorTx

NewRewardValidatorTxFixture is the "reward this validator" tx. Carries a single ids.ID. Tiny.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL