bench

package
v1.28.19 Latest Latest
Warning

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

Go to latest
Published: Jun 3, 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.goLUXD_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