Skip to content

chore(avm): report total instructions executed from the public tx simulator#24968

Open
vezenovm wants to merge 8 commits into
merge-train/fairiesfrom
mv/restore-opcode-bench-metric
Open

chore(avm): report total instructions executed from the public tx simulator#24968
vezenovm wants to merge 8 commits into
merge-train/fairiesfrom
mv/restore-opcode-bench-metric

Conversation

@vezenovm

@vezenovm vezenovm commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Problem

The public-tx simulator contract benchmarks (dashboard: yarn-project/simulator/<contract tests>)
lost their "instructions executed" metric when the TS AVM simulator was removed only total duration
and mana remained. This is a regression in our ability to track opcode-level work (e.g. the public
dispatch/macro optimizations that public_fns_with_emit_repro_contract tracked in the benchmark).

Root cause

recordEnqueuedCallSimulation (which carried the per-call instruction count from the old TS
interpreter) has no callers once simulation routes through the C++ simulator, so
TestExecutorMetrics.stopRecordingTxSimulation summed an always-empty enqueuedCalls list and
reported a flat 0. manaUsed hit the same wall but was rescued earlier by reading the tx-level
gasUsed; the instruction count was not, because the C++ result carried no such field.

(For clarity: the public-bytecode-size metric was not lost — it moved to a dedicated noir-contracts
bench and lives on the dashboard under noir-projects/noir-contracts/artifact-size/<contract>/.)

Change

  • C++: add total_instructions_executed to TxSimulationResult, populated in both result builders
    (simulate_fast_internal and simulate_for_witgen_internal) from the AVM execution-id delta. The
    execution loop advances the execution id exactly once per instruction it processes — across all
    enqueued and nested calls — so the delta is the number of instructions executed, i.e. the number of
    AVM execution trace rows. This is a simulation-output field only: no proving / VK impact.
  • TS: thread it through PublicTxResult and record it as the tx-level
    TestExecutorMetrics.totalInstructionsExecuted, which the benchmark JSON already emits — so the
    dashboard series is repopulated with no dashboard-side change.
  • Added the field to the AVM fuzzer's fast-vs-witgen result comparison so the two paths can't silently
    diverge on it.

The metric counts AVM execution steps: one per instruction the VM processes, including a terminal
halting/failing instruction. For successful executions — which is all of the benchmarked contracts —
this equals the number of instructions executed.

vezenovm added 4 commits July 24, 2026 12:27
…lator

The simulator contract benchmarks lost their "instructions executed" metric when the
TS AVM simulator was removed: recordEnqueuedCallSimulation (which carried the per-call
instruction count) has no callers under the C++ simulator, so TestExecutorMetrics summed
an always-empty list and reported a flat 0. manaUsed had the same problem but was rescued
by reading tx-level gasUsed; the instruction count was not, because the C++ result carried
no such field.

Expose it from the C++ simulator via a new TxSimulationResult field,
total_instructions_executed, set to the AVM execution-id delta: the execution loop advances
the execution id once per instruction it processes, across all enqueued and nested calls, so
the delta is the number of executed instructions (equivalently, execution trace rows). Thread
it through PublicTxResult and record it as the tx-level TestExecutorMetrics
totalInstructionsExecuted, which the benchmark JSON already reports.
…serialization fallback

Addresses review feedback:
- avm_test.test.ts: run the real simulator through an explicit TestExecutorMetrics and assert the
  emitted totalInstructionsExecuted matches the value on the PublicTxResult, so a miswired metrics
  call site (which the prior tests would not have caught) fails the test.
- avm.test.ts: exercise PublicTxResult.fromPlainObject (the production C++ deserialization path, not
  just the schema) for present, missing, and null totalInstructionsExecuted, using a real serialized
  tx_result as the base so sub-fields keep their C++ shapes.
…in object

The committed tx_result testdata fixture is obsolete (it has no publicTxEffect key), so
PublicTxResult.fromPlainObject throws in PublicTxEffect.fromPlainObject before reaching the
totalInstructionsExecuted fallback. Replace it with a minimal, well-formed plain object built
in the test (Fr/RevertCode plain values are bare numbers; the optional proving fields are
omitted) so the present/missing/null cases actually exercise `obj.totalInstructionsExecuted ?? 0`.
Drop the isolated PublicTxResult schema/fromPlainObject unit tests and the
standalone TestExecutorMetrics unit test. The avm_test bulk integration test
already exercises totalInstructionsExecuted through the real simulator and into
the benchmark JSON, so those add nothing.

Keep the C++ fast-vs-witgen count check, drop the redundant halting-instruction
test, shorten the TxSimulationResult field comment to one line, and drop the
now-pointless "(C++)"/"hint-collecting" qualifiers in comments (there is only
one simulator).
@vezenovm vezenovm changed the title feat(avm): report total instructions executed from the public tx simulator chore(avm): report total instructions executed from the public tx simulator Jul 24, 2026
vezenovm added 3 commits July 24, 2026 13:24
The bulk test keeps the direct assertion that the tx result carries a
non-zero instruction count (exercising the C++ result deserialization);
the metrics round-trip is validated by the actual benchmark CI job, so
the in-test benchmark-JSON matching and its metrics plumbing are removed.
The tx-level total_instructions_executed replaced a TS-side sum over
per-enqueued-call counts, so pin that it still spans every enqueued call
and every nested call underneath them.
@vezenovm
vezenovm marked this pull request as ready for review July 24, 2026 17:31
@vezenovm
vezenovm requested review from Thunkar, dbanks12 and fcarreiro and removed request for MirandaWood July 24, 2026 17:31
Comment thread barretenberg/cpp/src/barretenberg/vm2/integration_tests/custom_bytecode.test.cpp Outdated
@AztecBot

Copy link
Copy Markdown
Collaborator

Flakey Tests

🤖 says: This CI run detected 3 tests that failed, but were tolerated due to a .test_patterns.yml entry.

\033FLAKED\033 (8;;http://ci.aztec-labs.com/f255a2e12a720b08�f255a2e12a720b088;;�):  yarn-project/end-to-end/scripts/run_test.sh simple src/p2p/reqresp/reqresp.test.ts (188s) (code: 0) group:e2e-p2p-epoch-flakes
\033FLAKED\033 (8;;http://ci.aztec-labs.com/292c0df86c0d9968�292c0df86c0d99688;;�):  yarn-project/end-to-end/scripts/run_test.sh simple src/multi-node/slashing/slash_veto_demo.test.ts (196s) (code: 0) group:e2e-p2p-epoch-flakes
\033FLAKED\033 (8;;http://ci.aztec-labs.com/7dc40474adba779d�7dc40474adba779d8;;�):  yarn-project/end-to-end/scripts/run_test.sh simple src/multi-node/invalid-attestations/invalidate_block.parallel.test.ts "archiver skips a descendant of an invalid-attestations checkpoint" (235s) (code: 0) group:e2e-p2p-epoch-flakes

GasUsed gas_used;
RevertCode revert_code;
// Total AVM instructions executed by the tx, across all enqueued calls and their nested calls.
uint32_t total_instructions_executed = 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit ad-hoc, and ad-hoc additions grow organically. There was already a plan to support statistics such as this one, but we never got to implement it: http://linear.app/aztec-labs/issue/AVM-194/implement-stats-collection-for-c-simulator

The idea is to add a map string->string here.

Then, use collect_statistics as a filter (because some stats might be expensive): https://github.com/AztecProtocol/aztec-packages/blob/next/barretenberg/cpp/src/barretenberg/vm2/common/avm_io.hpp#L453C10-L453C28

The original plan, as the Linear issue mentions, was to use a kind of statistics collector class, which could then be either a noop, or a real one. I don't expect anyone will be adding more stats than this, so maybe THAT is overkill and you can avoid it.

I saw you used the execution_id to count instructions. That is smart, since you don't need to modify the simulator loop itself to add an instruction counter. However, it uses/exposes internal information that might not always work like that. I'm not completely against it, especially since you avoid modifying the Execution loop. Strictly speaking it could bring problems down the line, but the AVM is in maintenance mode so I don't expect that would happen.

Of course, I'm not in the team anymore, so @IlyasRidhuan to decide on the above ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants