Glue adjustment¶
When a runtime measurement targets, say, ADD, the fixture's recorded
wall-clock isn't pure ADD execution — it also contains the cost of the
glue opcodes that set up arguments, advance the program counter, and
clean the stack. NNLS attributes the entire intercept + slope·opcount
budget to the target, so if PUSH and JUMPDEST runtimes happen to scale
with the loop body, the inferred target_coef is contaminated.
Glue adjustment subtracts a per-fixture estimate of that overhead from each fitted target coefficient before it is converted to gas.
Enable it under glue_adjustment:
The 30 canonical glue opcodes¶
evm-gasfit does not dynamically discover glue opcodes — the list is
hardcoded in src/evm_gasfit/glue/required.py
under PRICED_GLUE_SPECS. Each opcode lives in one of four tiers; the tier
controls how its per-client runtime is estimated.
| Tier | Opcodes | How its runtime is fit |
|---|---|---|
pure (5) |
ISZERO, JUMPDEST, POP, STOP, SWAP |
Single-feature NNLS per (client, opcode). POP / STOP have no driver fixture yet, so they're skipped with a warning. SWAP is fit jointly over SWAP1..SWAP16. |
cycle (8) |
CALLDATASIZE, DUP, GAS, MLOAD, PUSH, PUSH0, STATICCALL |
One joint NNLS per client over all cycle opcodes simultaneously, with family members (DUP1..16, PUSH1..32) row-wise summed into a single canonical feature. |
mixed_a (16) |
ADD, AND, CALLDATACOPY, CALLDATALOAD, DIV, EXP, GT, JUMPI, LT, MSTORE, MSTORE8, MUL, PC, RETURNDATASIZE, SELFBALANCE, SUB |
Single-feature NNLS per (client, opcode), but the LHS (test_runtime_ms) is pre-adjusted by subtracting contributions from priced pure + cycle partners. |
mixed_b (2) |
JUMP, KECCAK256 |
Same as mixed_a but partners may also come from priced mixed_a results. |
The tier order is a static dependency graph: pure and cycle produce
runtimes that mixed_a consumes, which in turn produce runtimes that
mixed_b consumes. No cycles, no iteration to convergence.
(Some target opcodes — like ADD — are both a target you'd want to price
and a glue contaminant on other targets. That's why they appear in
mixed_a / mixed_b: they need a fit of their own first.)
Step 1 — detect per-test glue partners¶
For every (spec, model_by-combo) slice, the detector
(glue/detect.py)
walks every canonical glue opcode and asks: did this opcode's per-fixture
count co-vary with the target opcount? It computes:
- The Pearson correlation between the glue opcode's per-fixture count
and
opcount. - The mean delta ratio \(\text{ratio} = \overline{\Delta\text{glue count}} / \overline{\Delta\text{opcount}}\) taken over the sorted-by-opcount sequence.
An opcode is kept as a partner only when both:
ratio_corr_eps defaults to 0.05 (see
glue_adjustment); the ratio floor
is a fixed _RATIO_FLOOR constant. The output is glue_opcodes_by_test.csv
— one row per (test_name, target_opcode, model_by-combo, glue_opcode)
holding the corr and ratio of each surviving partner.
Family opcodes (DUP1..DUP16, PUSH1..PUSH32, SWAP1..SWAP16) are folded
into their canonical name before the thresholding so a fixture's DUP
contribution is the sum of all 16 family members.
Step 2 — estimate per-client glue runtimes¶
For each priced glue opcode, the estimator
(glue/estimate.py)
runs the appropriate tiered fit and writes one row per (client, glue_opcode)
to glue_results.csv with glue_runtime_ms, p_value, rsquared, and
confidence interval columns. The mixed_* tiers pre-adjust their LHS using
already-priced partners from earlier tiers — same subtraction shape as the
target-coefficient adjustment below.
Step 3 — adjust the target coefficient¶
For every row of results.csv, the adjuster
(glue/adjust.py)
looks up each partner from step 1, multiplies its ratio by that client's
fitted glue runtime, and sums:
The lower and upper CI bounds are shifted by the same \(\Delta_\text{glue}\)
and clipped at zero identically. Only the target coefficient is adjusted —
the intercept and model_params extras are left alone.
Per-glue-fit quality gates¶
A glue opcode contributes to \(\Delta_\text{glue}\) only if its per-client fit passes both:
| Knob | Default |
|---|---|
glue_contribution_p_value_threshold |
0.05 |
glue_contribution_rsquared_threshold |
0.5 |
A partner whose fit failed, was skipped (POP/STOP), or has NaN
statistics is dropped from the sum. Every such skip is recorded; the
proposal report's Missing glue adjustments warnings section enumerates the
affected (client, gas_param) pairs so reviewers know which proposed values
don't carry a glue correction.
What lands on disk¶
When glue_adjustment.enabled: true:
glue_results.csv— per(client, glue_opcode)fit results.glue_opcodes_by_test.csv— per-test partner ratios from step 1.glue_opcodes_autogenerated_report.md— per-opcode summary with plots.- The proposal layer consumes
adjusted_target_coef_*columns wherever a matching row exists; absent rows fall back to the originaltarget_coef_*.