Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
0ca12e2
feat: benchmark avm simulator token test
Mar 24, 2025
77a152e
deps
Mar 24, 2025
953dc8b
blah
Mar 24, 2025
6439d18
bench
Mar 24, 2025
f3f3ea9
cleanup and more tests
Mar 24, 2025
7e85f9d
cleanup
Mar 24, 2025
47fd1c2
merge
Mar 24, 2025
3e2e154
cleanup
Mar 24, 2025
622a694
revert token test
Mar 24, 2025
bf08766
more in bench test
Mar 24, 2025
bc072e0
revert ci3.yml
Mar 24, 2025
8f000a1
fix
Mar 24, 2025
1c6f718
amm and proper tallying
Mar 25, 2025
ee27f05
test helpers
Mar 25, 2025
b0ec099
cleanup
Mar 25, 2025
4086163
cleanup
Mar 26, 2025
c9047df
fix
Mar 26, 2025
71f8c40
fix talling
Mar 26, 2025
8bde642
fix
Mar 26, 2025
abe0bdf
unfix
Mar 26, 2025
5cbac04
cleanup
Mar 26, 2025
3115193
cleanup
Mar 26, 2025
bac8671
merge
Mar 26, 2025
a26e912
rename proving tester create to new
Mar 26, 2025
73eb5e5
fix comment
Mar 26, 2025
6f14c63
executor metrics for testing
Mar 27, 2025
9642476
subclasses of public tx simulator for metrics
Mar 27, 2025
bcc1292
subclasses
Mar 27, 2025
e09f98a
rm stale
Mar 27, 2025
c126bcb
rm bench.ts
Mar 27, 2025
bac743a
imports
Mar 27, 2025
6f9f1cb
cleanup
Mar 27, 2025
b0fbb66
fix
Mar 27, 2025
b22ebe7
single map for debug fns
Mar 27, 2025
25d7d91
fix
Mar 27, 2025
428f4d7
timer
Mar 27, 2025
9cc649e
pretty print
Mar 27, 2025
4d0c6b7
dependency cycle
Mar 27, 2025
f9b8fda
import
Mar 27, 2025
b3ae882
fixes
Mar 27, 2025
9537351
undo telemetry changes
Mar 27, 2025
80849ee
respond to PR comments
Mar 27, 2025
9f0191d
fix
Mar 27, 2025
45c643b
amm cleanup
Mar 27, 2025
a54add9
import
Mar 27, 2025
010377d
markdown
Mar 27, 2025
6b33a7b
markdown file
Mar 27, 2025
89abe9a
optional md file
Mar 27, 2025
993139e
json file
Mar 27, 2025
921bdf5
Merge branch 'master' into db/bench
dbanks12 Mar 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions .github/workflows/ci3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,12 @@ jobs:
max-items-in-chart: 50

- name: Store yarn project benchmark result
if: matrix.settings.arch == 'amd64' && github.event_name == 'push' && github.ref_name == 'master' && env.SKIP_YP_BENCH != 'true'
if: matrix.settings.arch == 'amd64' && github.event_name == 'push' && github.ref_name == 'db/bench' && env.SKIP_YP_BENCH != 'true'
continue-on-error: true
uses: benchmark-action/github-action-benchmark@4de1bed97a47495fc4c5404952da0499e31f5c29
with:
name: "End-to-end Benchmark"
benchmark-data-dir-path: "dev/e2e-bench"
benchmark-data-dir-path: "dev/avm-sim-bench"
tool: "customSmallerIsBetter"
output-file-path: ./bench-out/yp-bench.json
github-token: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }}
Expand All @@ -160,6 +160,23 @@ jobs:
alert-comment-cc-users: "@philwindle @spalladino"
max-items-in-chart: 50

#- name: Store AVM simulator benchmark result
# if: matrix.settings.arch == 'amd64' && github.event_name == 'push' && github.ref_name == 'db/bench' && env.SKIP_YP_BENCH != 'true'
# continue-on-error: true
# uses: benchmark-action/github-action-benchmark@4de1bed97a47495fc4c5404952da0499e31f5c29
# with:
# name: "AVM Simulator Benchmark"
# benchmark-data-dir-path: "dev/avm-sim-bench"
# tool: "customSmallerIsBetter"
# output-file-path: ./bench-out/yp-bench.json
# github-token: ${{ secrets.AZTEC_BOT_GITHUB_TOKEN }}
# auto-push: true
# alert-threshold: "150%"
# comment-on-alert: true
# fail-on-alert: false
# alert-comment-cc-users: "@dbanks12"
# max-items-in-chart: 50

# - name: Store p2p benchmark result
# if: matrix.settings.arch == 'amd64' && github.event_name == 'push' && github.ref_name == 'master'
# continue-on-error: true
Expand Down
28 changes: 0 additions & 28 deletions yarn-project/end-to-end/src/bench/utils.ts
Comment thread
dbanks12 marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import type { AztecNodeService } from '@aztec/aztec-node';
import { type AztecNode, BatchCall, INITIAL_L2_BLOCK_NUM, type SentTx, type WaitOpts } from '@aztec/aztec.js';
import { mean, stdDev, times } from '@aztec/foundation/collection';
import { randomInt } from '@aztec/foundation/crypto';
import { BenchmarkingContract } from '@aztec/noir-contracts.js/Benchmarking';
import { type PXEService, type PXEServiceConfig, createPXEService } from '@aztec/pxe/server';
import type { MetricsType } from '@aztec/telemetry-client';
import type { BenchmarkDataPoint, BenchmarkMetricsType, BenchmarkTelemetryClient } from '@aztec/telemetry-client/bench';

import { writeFileSync } from 'fs';
import { mkdirpSync } from 'fs-extra';
import { globSync } from 'glob';
import { join } from 'path';

import { type EndToEndContext, type SetupOptions, setup } from '../fixtures/utils.js';

Expand Down Expand Up @@ -94,30 +90,6 @@ function getMetricValues(points: BenchmarkDataPoint[]) {
}
}

/**
* Creates and returns a directory with the current job name and a random number.
* @param index - Index to merge into the dir path.
* @returns A path to a created dir.
*/
export function makeDataDirectory(index: number) {
const testName = expect.getState().currentTestName!.split(' ')[0].replaceAll('/', '_');
const db = join('data', testName, index.toString(), `${randomInt(99)}`);
mkdirpSync(db);
return db;
}

/**
* Returns the size in disk of a folder.
* @param path - Path to the folder.
* @returns Size in bytes.
*/
export function getFolderSize(path: string): number {
return globSync('**', { stat: true, cwd: path, nodir: true, withFileTypes: true }).reduce(
(accum, file) => accum + (file as any as { /** Size */ size: number }).size,
0,
);
}

/**
* Returns a call to the benchmark contract. Each call has a private execution (account entrypoint),
* a nested private call (create_note), a public call (increment_balance), and a nested public
Expand Down
3 changes: 3 additions & 0 deletions yarn-project/simulator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,15 @@
"@aztec/merkle-tree": "workspace:^",
"@aztec/noir-contracts.js": "workspace:^",
"@jest/globals": "^29.5.0",
"@types/fs-extra": "^11.0.2",
"@types/jest": "^29.5.0",
"@types/levelup": "^5.1.3",
"@types/lodash.clonedeep": "^4.5.7",
"@types/lodash.merge": "^4.6.9",
"@types/memdown": "^3.0.2",
"@types/node": "^18.7.23",
"fs-extra": "11.2.0",
"glob": "10.3.10",
"jest": "^29.5.0",
"jest-mock-extended": "^3.0.4",
"ts-node": "^10.9.1",
Expand Down
18 changes: 15 additions & 3 deletions yarn-project/simulator/src/public/avm/avm_contract_call_result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@ export class AvmContractCallResult {
public output: Fr[],
public gasLeft: AvmGas,
public revertReason?: AvmRevertReason,
public totalInstructions: number = 0, // including nested calls
Comment thread
dbanks12 marked this conversation as resolved.
) {}

toString(): string {
let resultsStr = `reverted: ${this.reverted}, output: ${this.output}, gasLeft: ${inspect(this.gasLeft)}`;
let resultsStr = `reverted: ${this.reverted}, output: ${this.output}, gasLeft: ${inspect(
this.gasLeft,
)}, totalInstructions: ${this.totalInstructions}`;
if (this.revertReason) {
resultsStr += `, revertReason: ${this.revertReason}`;
}
Expand All @@ -29,7 +32,13 @@ export class AvmContractCallResult {

finalize(): AvmFinalizedCallResult {
const revertReason = this.revertReason ? createSimulationError(this.revertReason, this.output) : undefined;
return new AvmFinalizedCallResult(this.reverted, this.output, Gas.from(this.gasLeft), revertReason);
return new AvmFinalizedCallResult(
this.reverted,
this.output,
Gas.from(this.gasLeft),
revertReason,
this.totalInstructions,
);
}
}

Expand All @@ -43,10 +52,13 @@ export class AvmFinalizedCallResult {
public output: Fr[],
public gasLeft: Gas,
public revertReason?: SimulationError,
public totalInstructions: number = 0, // including nested calls
) {}

toString(): string {
let resultsStr = `reverted: ${this.reverted}, output: ${this.output}, gasLeft: ${inspect(this.gasLeft)}`;
let resultsStr = `reverted: ${this.reverted}, output: ${this.output}, gasLeft: ${inspect(
this.gasLeft,
)}, totalInstructions: ${this.totalInstructions}`;
if (this.revertReason) {
resultsStr += `, revertReason: ${this.revertReason}`;
}
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/simulator/src/public/avm/avm_machine_state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export class AvmMachineState {
public pc: number = 0;
/** program counter of the next instruction, byte based */
public nextPc: number = 0;
/** instruction counter, including nested calls */
public instrCounter: number = 0;
Comment thread
dbanks12 marked this conversation as resolved.
Outdated
/** return/revertdata of the last nested call. */
public nestedReturndata: Fr[] = [];
/** Tracks whether the last external call was successful */
Expand Down
34 changes: 23 additions & 11 deletions yarn-project/simulator/src/public/avm/avm_simulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ export class AvmSimulator {
// only. Otherwise, use build() below.
constructor(
private context: AvmContext,
private instructionSet: InstructionSet = INSTRUCTION_SET,
enableTallying = false,
private instructionSet: InstructionSet = INSTRUCTION_SET,
) {
// This will be used by the CALL opcode to create a new simulator. It is required to
// avoid a dependency cycle.
Expand All @@ -66,8 +66,8 @@ export class AvmSimulator {

// Factory to have a proper function name in the logger. Retrieving the name is asynchronous and
// cannot be done as part of the constructor.
public static async build(context: AvmContext): Promise<AvmSimulator> {
const simulator = new AvmSimulator(context);
public static async build(context: AvmContext, enableTallying: boolean = false): Promise<AvmSimulator> {
const simulator = new AvmSimulator(context, enableTallying);
Comment thread
dbanks12 marked this conversation as resolved.
Outdated
const fnName = await context.persistableState.getPublicFunctionDebugName(context.environment);
simulator.log = createLogger(`simulator:avm(f:${fnName})`);

Expand All @@ -83,6 +83,7 @@ export class AvmSimulator {
isStaticCall: boolean,
calldata: Fr[],
allocatedGas: Gas,
enableTallying: boolean = true,
) {
const avmExecutionEnv = new AvmExecutionEnvironment(
address,
Expand All @@ -96,7 +97,7 @@ export class AvmSimulator {

const avmMachineState = new AvmMachineState(allocatedGas);
const avmContext = new AvmContext(stateManager, avmExecutionEnv, avmMachineState);
return await AvmSimulator.build(avmContext);
return await AvmSimulator.build(avmContext, enableTallying);
}

/**
Expand Down Expand Up @@ -147,7 +148,6 @@ export class AvmSimulator {
try {
// Execute instruction pointed to by the current program counter
// continuing until the machine state signifies a halt
let instrCounter = 0;
while (!machineState.getHalted()) {
// Get the instruction from cache, or deserialize for the first time
let cachedInstruction = this.deserializedInstructionsCache.get(machineState.pc);
Expand All @@ -163,13 +163,11 @@ export class AvmSimulator {
if (this.log.isLevelEnabled('trace')) {
// Skip this entirely to avoid toStringing etc if trace is not enabled
this.log.trace(
`[PC:${machineState.pc}] [IC:${instrCounter}] ${instruction.toString()} (gasLeft l2=${
`[PC:${machineState.pc}] [IC:${machineState.instrCounter}] ${instruction.toString()} (gasLeft l2=${
machineState.l2GasLeft
} da=${machineState.daGasLeft})`,
);
}
instrCounter++;

machineState.nextPc = machineState.pc + bytesRead;

// Execute the instruction.
Expand All @@ -181,6 +179,8 @@ export class AvmSimulator {
machineState.pc += bytesRead;
}

machineState.instrCounter++;

// gas used by this instruction - used for profiling/tallying
const gasUsed: Gas = {
l2Gas: instrStartGas.l2Gas - machineState.l2GasLeft,
Expand All @@ -197,13 +197,19 @@ export class AvmSimulator {
const output = machineState.getOutput();
const reverted = machineState.getReverted();
const revertReason = reverted ? await revertReasonFromExplicitRevert(output, this.context) : undefined;
const results = new AvmContractCallResult(reverted, output, machineState.gasLeft, revertReason);
const results = new AvmContractCallResult(
reverted,
output,
machineState.gasLeft,
revertReason,
machineState.instrCounter,
);
this.log.debug(`Context execution results: ${results.toString()}`);
const totalGasUsed: Gas = {
l2Gas: callStartGas.l2Gas - machineState.l2GasLeft,
daGas: callStartGas.daGas - machineState.daGasLeft,
};
this.log.debug(`Executed ${instrCounter} instructions and consumed ${totalGasUsed.l2Gas} L2 Gas`);
this.log.debug(`Executed ${machineState.instrCounter} instructions and consumed ${totalGasUsed.l2Gas} L2 Gas`);

this.tallyPrintFunction();

Expand Down Expand Up @@ -233,7 +239,13 @@ export class AvmSimulator {
// Exceptional halts consume all allocated gas
const noGasLeft = { l2Gas: 0, daGas: 0 };
// Note: "exceptional halts" cannot return data, hence [].
const results = new AvmContractCallResult(/*reverted=*/ true, /*output=*/ [], noGasLeft, revertReason);
const results = new AvmContractCallResult(
/*reverted=*/ true,
/*output=*/ [],
noGasLeft,
revertReason,
machineState.instrCounter,
);
this.log.debug(`Context execution results: ${results.toString()}`);

this.tallyPrintFunction();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ abstract class ExternalCall extends Instruction {
// Track the success status directly
context.machineState.nestedCallSuccess = success;

// Account for all instructions executed in the nested call
context.machineState.instrCounter += nestedCallResults.totalInstructions;

// If the nested call reverted, we try to save the reason and the revert data.
// This will be used by the caller to try to reconstruct the call stack.
// This is only a heuristic and may not always work. It is intended to work
Expand Down
30 changes: 29 additions & 1 deletion yarn-project/simulator/src/public/executor_metrics.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createLogger } from '@aztec/foundation/log';
import {
Attributes,
type Histogram,
Expand All @@ -13,6 +14,8 @@ export class ExecutorMetrics {
private fnCount: UpDownCounter;
private fnDuration: Histogram;
private manaPerSecond: Histogram;
private manaUsed: UpDownCounter;
private totalInstructions: UpDownCounter;
private privateEffectsInsertions: Histogram;

constructor(client: TelemetryClient, name = 'PublicExecutor') {
Expand All @@ -35,18 +38,43 @@ export class ExecutorMetrics {
valueType: ValueType.INT,
});

this.manaUsed = meter.createUpDownCounter(Metrics.PUBLIC_EXECUTOR_SIMULATION_MANA_USED, {
description: 'Total mana used',
unit: 'mana',
valueType: ValueType.INT,
});

this.totalInstructions = meter.createUpDownCounter(Metrics.PUBLIC_EXECUTOR_SIMULATION_TOTAL_INSTRUCTIONS, {
description: 'Total number of instructions executed',
unit: 'instructions',
valueType: ValueType.INT,
});
Comment thread
dbanks12 marked this conversation as resolved.
Outdated

this.privateEffectsInsertions = meter.createHistogram(Metrics.PUBLIC_EXECUTION_PRIVATE_EFFECTS_INSERTION, {
description: 'Private effects insertion time',
unit: 'us',
valueType: ValueType.INT,
});
}

recordFunctionSimulation(durationMs: number, manaUsed: number, fnName: string) {
recordFunctionSimulation(durationMs: number, manaUsed: number, fnName: string, totalInstructions: number) {
const logger = createLogger('executor-metrics');
logger.error(`Recording function simulation: ${fnName}`, {
durationMs,
manaUsed,
totalInstructions,
});
this.fnCount.add(1, {
[Attributes.OK]: true,
[Attributes.APP_CIRCUIT_NAME]: fnName,
[Attributes.MANA_USED]: manaUsed,
[Attributes.TOTAL_INSTRUCTIONS]: totalInstructions,
});
this.manaUsed.add(manaUsed, {
[Attributes.APP_CIRCUIT_NAME]: fnName,
});
this.totalInstructions.add(totalInstructions, {
[Attributes.APP_CIRCUIT_NAME]: fnName,
});
this.fnDuration.record(Math.ceil(durationMs));
if (durationMs > 0 && manaUsed > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { GasFees } from '@aztec/stdlib/gas';
import type { MerkleTreeWriteOperations } from '@aztec/stdlib/interfaces/server';
import { PublicCallRequest } from '@aztec/stdlib/kernel';
import { GlobalVariables, PublicCallRequestWithCalldata, type Tx } from '@aztec/stdlib/tx';
import type { TelemetryClient } from '@aztec/telemetry-client';
import { NativeWorldStateService } from '@aztec/world-state';

import { BaseAvmSimulationTester } from '../avm/fixtures/base_avm_simulation_tester.js';
Expand Down Expand Up @@ -35,14 +36,18 @@ export type TestEnqueuedCall = {
export class PublicTxSimulationTester extends BaseAvmSimulationTester {
private txCount = 0;

constructor(private merkleTree: MerkleTreeWriteOperations, contractDataSource: SimpleContractDataSource) {
constructor(
private merkleTree: MerkleTreeWriteOperations,
contractDataSource: SimpleContractDataSource,
private telemetryClient?: TelemetryClient,
) {
super(contractDataSource, merkleTree);
}

public static async create(): Promise<PublicTxSimulationTester> {
public static async create(telemetryClient?: TelemetryClient): Promise<PublicTxSimulationTester> {
const contractDataSource = new SimpleContractDataSource();
const merkleTree = await (await NativeWorldStateService.tmp()).fork();
return new PublicTxSimulationTester(merkleTree, contractDataSource);
return new PublicTxSimulationTester(merkleTree, contractDataSource, telemetryClient);
}

public async createTx(
Expand Down Expand Up @@ -72,17 +77,26 @@ export class PublicTxSimulationTester extends BaseAvmSimulationTester {
/* need some unique first nullifier for note-nonce computations */
firstNullifier = new Fr(420000 + this.txCount++),
globals = defaultGlobals(),
metricsTag?: string,
): Promise<PublicTxResult> {
const tx = await this.createTx(sender, setupCalls, appCalls, teardownCall, feePayer, firstNullifier);

await this.setFeePayerBalance(feePayer);

const treesDB = new PublicTreesDB(this.merkleTree);
const contractsDB = new PublicContractsDB(this.contractDataSource);
const simulator = new PublicTxSimulator(treesDB, contractsDB, globals, /*doMerkleOperations=*/ true);
const simulator = new PublicTxSimulator(
treesDB,
contractsDB,
globals,
/*doMerkleOperations=*/ true,
/*skipFeeEnforcement=*/ false,
this.telemetryClient,
/*enableCoreSimulationMetrics=*/ true,
);

const startTime = performance.now();
const avmResult = await simulator.simulate(tx);
const avmResult = await simulator.simulate(tx, metricsTag);
const endTime = performance.now();
this.logger.debug(`Public transaction simulation took ${endTime - startTime}ms`);

Expand Down
Loading