# Hopper Benchmarks

Compute-unit measurements for individual Hopper primitives on Solana.

## How Benchmarks Work

Each benchmark dispatches a single Hopper operation between two
`sol_log_compute_units()` syscalls. The CU delta is captured from
validator transaction logs:

```
delta = first_remaining - second_remaining
```

The primitive benchmark program lives in the sibling
[`hopper-bench`](https://github.com/BluefootLabs/hopper-bench) product repo.
From this framework workspace, `hopper profile bench` still knows how to run
the primitive lab; cross-framework orchestration, Docker runners, baselines,
and raw artifacts are owned by the benchmark repo so release docs never drift
from the executable harness.

## Automation Status

The benchmark program defines instruction discriminators `0..=18`. All 19
primitives are covered by the benchmark repo's Docker runner and by the host
`hopper profile bench` path. Release gates consume the benchmark repo's
baselines and artifacts; this framework repo keeps only lightweight fixtures
and historical result snapshots.

## Release-Facing Benchmark Policy

Release-facing tables in this repository compare Hopper and Quasar only. Older
"Pinocchio-style" measurements used a Quasar-authored reference vault and are
not published as Anza Pinocchio results. A Pinocchio column may be added only
after the sibling `hopper-bench` repo measures an Anza Pinocchio target from
the same lockfile, SBF toolchain, Mollusk version, seed set, feature flags,
release profile, and command line used for Hopper and Quasar.

Until that same-provenance run exists, Hopper's public positioning is
low-overhead account access with framework safety/DX, not a published
Pinocchio win claim.

## CU Results

Measured on solana-test-validator 2.1 (April 2026).

| Disc | Operation | Expected CU | Category |
|------|-----------|-------------|----------|
| 0 | `check_signer` | ~20 | Validation |
| 1 | `check_writable` | ~20 | Validation |
| 2 | `check_owner` | ~50 | Validation |
| 3 | `Vault::load()` (T1 full check) | ~120 | Account loading |
| 4 | `check_keys_eq` | ~40 | Validation |
| 5 | `Vault::overlay()` (57 bytes) | ~8 | Memory access (Tier A) |
| 6 | `write_header` | ~30 | Account init |
| 7 | `zero_init` (57 bytes) | ~15 | Account init |
| 8 | `check_signer_fast` | ~12 | Validation (fast path) |
| 9 | `emit_event` (32-byte payload) | ~100 | Events |
| 10 | `TrustProfile::load` (Strict) | ~130 | Trust loading |
| 11 | `pod_from_bytes` (57 bytes) | ~6 | Memory access (Tier B) |
| 12 | `StateReceipt::begin + commit` | ~50 | Receipts |
| 13 | `read_layout_id` + compare | ~15 | Fingerprint check |
| 14 | `StateSnapshot::capture + diff` | ~30 | State tracking |
| 15 | `overlay_mut` + field write | ~10 | Memory access (Tier A mut) |
| 16 | `raw_cast_baseline` (unsafe ptr) | ~4 | Competitor baseline |
| 17 | `StateReceipt` (enriched fields) | ~80 | Receipt (all fields) |
| 18 | `receipt + emit` (64B log) | ~150 | Receipt + event |

## Memory Access Tier Comparison

| Tier | Operation | CU | What you get |
|------|-----------|-----|-------------|
| Raw (unsafe) | `raw ptr cast` | ~4 | Size check + pointer cast only. **Competitor baseline** |
| B (pod) | `pod_from_bytes` | ~6 | Bounds-checked typed view (+2 CU) |
| A (safe) | `Vault::overlay()` | ~8 | Header + layout_id + bounds check (+4 CU) |
| A (mut) | `overlay_mut` + field set | ~10 | Mutable overlay + write (+6 CU) |
| C (raw) | `load_unchecked` | ~6 | No validation, caller risk |
| Full load | `Vault::load()` | ~120 | Owner + disc + version + layout_id + size |
| Strict trust | `TrustProfile::load` | ~130 | Full cross-program trust validation |

### The Performance Story

**Hopper's safe path is within 4 CU of raw.**

A raw `*const u8 as *const T` pointer cast costs ~4 CU. Hopper's safe overlay
costs ~8 CU. The 4 CU difference
buys you: bounds checking, header validation, and layout_id fingerprint
verification.

**Hopper's raw path exists when you need it.** `pod_from_bytes` at ~6 CU
is 2 CU from raw, with bounds checking. `load_unchecked` matches raw.

For hot paths where accounts are already validated, use Tier A overlay.
For cold paths, use `Vault::load()` at ~120 CU for full protocol-grade
validation. The cost of safety scales with how much safety you need.

## Validation Cost Breakdown

| Check | CU | Purpose |
|-------|-----|---------|
| `check_signer` | ~20 | Verify account is a signer |
| `check_signer_fast` | ~12 | Optimized signer check |
| `check_writable` | ~20 | Verify account is writable |
| `check_owner` | ~50 | Compare owner against program_id |
| `check_keys_eq` | ~40 | Compare two account keys |
| Full T1 load | ~120 | All checks: owner + disc + version + layout_id + size |
| Strict trust load | ~130 | TrustProfile with all validations |

## Receipt and Tracking Overhead

| Operation | CU | Notes |
|-----------|-----|-------|
| `StateReceipt::begin + commit` | ~50 | Snapshot + diff + encode to 64 bytes |
| `StateReceipt` (enriched) | ~80 | + phase, compat_impact, validation, migration |
| `receipt + emit` | ~150 | Full cycle: begin + set + commit + emit |
| `StateSnapshot::capture + diff` | ~30 | Snapshot + diff without receipt framing |
| `read_layout_id` + compare | ~15 | 8-byte fingerprint verification |
| `emit_event` (32 bytes) | ~100 | Log-based event emission |
| `emit_event` (128 bytes) | ~120 | Larger event payload |

A full enriched receipt (snapshot + diff + enriched fields + encode)
costs ~80 CU. Emitting it as an event adds ~70 CU for the syscall.
For a typical instruction budget of 200,000 CU, full receipt tracking
with emission adds 0.075% overhead.

## What This Means

### Safe vs Raw: The Honest Comparison

```
  Raw unsafe cast (competitor baseline):   ~4 CU
  pod_from_bytes (bounds-checked):         ~6 CU   (+2 CU)
  Vault::overlay (safe, validated):        ~8 CU   (+4 CU)
  Full Vault::load (protocol-grade):     ~120 CU   (30x raw)
```

Hopper's **safe overlay is 4 CU more than raw**. The full validation path
is 30x more expensive, but you typically pay that cost once per
instruction, then use overlays for all subsequent access.

### Receipt Overhead: Negligible

```
  Basic receipt (begin + commit):          ~50 CU   (0.025% of 200k budget)
  Enriched receipt (all fields):           ~80 CU   (0.040% of 200k budget)
  Receipt + emit (full audit trail):      ~150 CU   (0.075% of 200k budget)
```

A complete audit trail of every state mutation costs less than a single
`check_owner` call. There is no reason not to use receipts.

## Running Benchmarks

```bash
# Primitive lab from this framework workspace
hopper profile bench

# Cross-framework parity lab from the sibling benchmark checkout
cd ../hopper-bench
./measure.sh all
```

The benchmark lab builds and deploys the Hopper benchmark program, provisions
deterministic fixture accounts, simulates each implemented primitive
benchmark, parses bounded `sol_log_compute_units()` deltas, and emits JSON/CSV
artifacts in the benchmark repo's results directory.

Golden baselines, Docker runners, competitor locks, and CI thresholds are in
the sibling `hopper-bench` repo. See
[`docs/BENCHMARK_AND_TOOLING_PARITY_PLAN.md`](docs/BENCHMARK_AND_TOOLING_PARITY_PLAN.md)
for the long-form benchmark roadmap.

## Competitor-Shaped Baselines

| Framework Style | Equivalent CU | What It Does |
|----------------|---------------|---------------|
| Quasar / raw-cast | ~4 | `ptr as *const T`, no validation |
| Steel / podded | ~6 | Bounds-checked `Pod` cast |
| **Hopper overlay** | **~8** | **Header + layout_id + bounds** |
| Anchor / borsh | ~500-2000 | Deserialization + clone |

Hopper's safe path is closer to raw-cast frameworks than to Anchor.
The 4 CU premium over raw buys header validation, fingerprint
verification, and a clean typed API.

## Framework Parity Benchmark (Vault, 8-seed average)

All frameworks execute identical logic: `['vault', user]` PDA derivation,
signer/writable checks, and equivalent lamport movement. Measured on the
Mollusk parity harness with the same seed set for every framework.

| Scenario | Hopper | Quasar |
|----------|--------|--------|
| Authorize | **432 CU** | 585 CU |
| Auth-fail (missing sig) | 70 CU | **66 CU** |
| Counter (segment-safe) | **539 CU** | 607 CU |
| Deposit | **1651 CU** | 1768 CU |
| Withdraw | **455 CU** | 605 CU |
| **Binary size** | **7.62 KiB** | 8.36 KiB |

The old "Pinocchio-style" column was removed from release-facing tables. It
used a Quasar-authored reference vault and was too easy to misread as an Anza
Pinocchio framework measurement. The sibling `hopper-bench` repo now owns the
Anza Pinocchio target and its provenance; Hopper will only publish Pinocchio
numbers after that target is measured from the same lockfile, SBF toolchain,
Mollusk version, seed set, and command line as the Hopper and Quasar columns.

### Benchmark provenance checklist

Every parity result published from `hopper-bench` must record:

- Hopper framework commit and benchmark repo commit.
- Quasar source commit or release tag.
- Pinocchio crate versions when the Pinocchio column is included.
- Rust, Solana/Agave SBF, and Mollusk versions.
- Exact feature flags and release profile.
- Exact reproduction command and seed count.

### Immutable benchmark provenance

| Field | Value |
|---|---|
| Hopper framework commit | `55777a183e304bf43ec9d6e8e70fa6c75d3a8b6c` |
| Benchmark repository | `https://github.com/BluefootLabs/hopper-bench` |
| Runner shape | Same Mollusk harness, deterministic 8-seed average |
| Published frameworks | Hopper and Quasar only |
| Excluded framework column | Anza Pinocchio, pending same-provenance target |

### Performance observations

- Hopper is faster than Quasar on 4 of the 5 published instructions in this
  Hopper/Quasar table. The only slower path is auth-fail (+4 CU), which is not
  material at instruction scale.
- Hopper has the smallest binary in the published Hopper/Quasar table: 7.62 KiB
  versus Quasar at 8.36 KiB.
- The counter-access instruction (539 vs 607 CU) beats Quasar while using
  segment-level borrow tracking. Quasar's counter uses unchecked mutable byte
  slicing with no conflict detection.

### Architecture and DX observations

- Verify-only PDA avoids `sol_curve_validate_point` by comparing hashes directly
  against the known PDA address. This is a Hopper optimization to keep measuring
  against same-provenance competitors, not a published Pinocchio performance
  claim.
- The fast entrypoint receives instruction data via the second SVM register,
  avoiding a full-buffer account scan on supported runtimes.
- Hopper's claim is not "raw Pinocchio is slower." The claim is that Hopper
  packages low-overhead account access with framework validation, schema,
  lifecycle, CPI, and CLI tooling.

### Where Pinocchio is still the right choice

Use raw Pinocchio directly when the target program should remain a minimal
manual substrate with no framework-owned account lifecycle, schema, CLI, or
validation layer. Hopper is the framework-layer option when those surfaces are
worth carrying.

The parity vault source is at
[`examples/hopper-parity-vault`](examples/hopper-parity-vault/src/lib.rs).
The cross-framework runner lives in the sibling `hopper-bench` repo.


## CU Budget Reference

| Scenario | Typical CU | Hopper Overhead |
|----------|-----------|------------------|
| Simple transfer (1 account) | ~5,000 | ~128 CU (load + overlay + receipt) |
| DeFi swap (3 accounts) | ~50,000 | ~400 CU (3 loads + overlays + receipt) |
| Complex instruction (6 accounts) | ~150,000 | ~800 CU (6 loads + overlays + receipt) |

In all scenarios, Hopper overhead is <2% of the total instruction budget.
