Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Benchmark tx sizes in p2p pool #2810

Merged
merged 2 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions yarn-project/end-to-end/src/benchmarks/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,20 @@ Tests in this folder are meant to used for benchmarking. Stats are collected by

These stats are emitted to jsonl files named after the test being run if the `BENCHMARK` flag is enabled or if running on `CI`. This setup happens when calling the `setup` helper of e2e tests in `yarn-project/end-to-end/src/fixtures/logging.ts`. Note that by default stats from all e2e tests are collected on the CI, and are uploaded to S3 using the `upload_logs_to_s3.sh` script called at the end of `run_tests_local`. All jsonl files are uploaded to the `aztec-ci-artifacts` bucket under the `logs` folder. Tests run in master are uploaded to `logs/master/COMMIT_HASH`, while tests from a PR are uploaded to `logs/pulls/PULL_REQUEST_NUMBER`.

## Benchmark summaries

After all benchmark tests are executed, a `bench-summary` CI job takes care of aggregating them, using the scripts in `yarn-project/scripts/benchmarks` orchestrated by `scripts/ci/assemble_e2e_benchmark.sh`. This script downloads all jsonl files, extracts metrics grouped by block size or chain length, and outputs an aggregated benchmark json file which is uploaded to S3. This file is uploaded to the same `aztec-ci-artifacts` bucket but under the `benchmarks` folder.

Metrics are strongly typed as well and defined in `yarn-project/types/src/stats/metrics.ts`, while the `yarn-project/scripts/src/benchmarks/aggregate.ts` script takes care of generating them out of the collected stats from the jsonl files.

Once the summary is generated, if the benchmark run is on a PR, then the summary job will also download the latest benchmark from master, compare it against the current run, generate a markdown summary, and post it to the pull request on github. This uses the `AZTEC_BOT_COMMENTER_GITHUB_TOKEN`, which is a fine-grained personal access token from the `AztecBot` github user with rw permissions on issues and pull requests.

## Local development

To test locally, first run one or more benchmark e2e tests locally from the `yarn-project/end-to-end` folder. It may be a good idea to shorten them so they run faster, for example:

```
$ BENCHMARK_BLOCK_SIZES=4 BENCHMARK=1 yarn test bench_publish
```

This should've generated one or more jsonl files in `yarn-project/end-to-end/log`
6 changes: 5 additions & 1 deletion yarn-project/p2p/src/tx_pool/memory_tx_pool.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createDebugLogger } from '@aztec/foundation/log';
import { Tx, TxHash } from '@aztec/types';
import { TxAddedToPoolStats } from '@aztec/types/stats';

import { TxPool } from './index.js';

Expand Down Expand Up @@ -38,7 +39,10 @@ export class InMemoryTxPool implements TxPool {
public async addTxs(txs: Tx[]): Promise<void> {
for (const tx of txs) {
const txHash = await tx.getTxHash();
this.log(`Adding tx with id ${txHash.toString()}`);
this.log(`Adding tx with id ${txHash.toString()}`, {
eventName: 'tx-added-to-pool',
...tx.getStats(),
} satisfies TxAddedToPoolStats);
this.txs.set(txHash.toBigInt(), tx);
}
}
Expand Down
23 changes: 9 additions & 14 deletions yarn-project/pxe/src/pxe_service/pxe_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
AztecAddress,
CircuitsWasm,
CompleteAddress,
EthAddress,
FunctionData,
GrumpkinPrivateKey,
KernelCircuitPublicInputsFinal,
Expand All @@ -21,7 +20,7 @@ import {
import { computeCommitmentNonce, siloNullifier } from '@aztec/circuits.js/abis';
import { encodeArguments } from '@aztec/foundation/abi';
import { padArrayEnd } from '@aztec/foundation/collection';
import { Fr, Point } from '@aztec/foundation/fields';
import { Fr } from '@aztec/foundation/fields';
import { DebugLogger, createDebugLogger } from '@aztec/foundation/log';
import NoirVersion from '@aztec/noir-compiler/noir-version';
import {
Expand Down Expand Up @@ -548,22 +547,18 @@ export class PXEService implements PXE {
this.log(`Executing kernel prover...`);
const { proof, publicInputs } = await kernelProver.prove(txExecutionRequest.toTxRequest(), executionResult);

const newContractPublicFunctions = newContract ? getNewContractPublicFunctions(newContract) : [];

const encryptedLogs = new TxL2Logs(collectEncryptedLogs(executionResult));
const unencryptedLogs = new TxL2Logs(collectUnencryptedLogs(executionResult));
const enqueuedPublicFunctions = collectEnqueuedPublicFunctionCalls(executionResult);

const contractData = new ContractData(
newContract?.completeAddress.address ?? AztecAddress.ZERO,
newContract?.portalContract ?? EthAddress.ZERO,
);
const extendedContractData = new ExtendedContractData(
contractData,
newContractPublicFunctions,
newContract?.completeAddress.partialAddress ?? Fr.ZERO,
newContract?.completeAddress.publicKey ?? Point.ZERO,
);
const extendedContractData = newContract
? new ExtendedContractData(
new ContractData(newContract.completeAddress.address, newContract.portalContract),
getNewContractPublicFunctions(newContract),
newContract.completeAddress.partialAddress,
newContract.completeAddress.publicKey,
)
: ExtendedContractData.empty();

// HACK(#1639): Manually patches the ordering of the public call stack
// TODO(#757): Enforce proper ordering of enqueued public calls
Expand Down
1 change: 1 addition & 0 deletions yarn-project/scripts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/tmp
8 changes: 8 additions & 0 deletions yarn-project/scripts/src/benchmarks/aggregate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
NodeSyncedChainHistoryStats,
NoteProcessorCaughtUpStats,
Stats,
TxAddedToPoolStats,
} from '@aztec/types/stats';

import * as fs from 'fs';
Expand Down Expand Up @@ -126,6 +127,11 @@ function processNodeSyncedChain(entry: NodeSyncedChainHistoryStats, results: Ben
append(results, 'node_database_size_in_bytes', bucket, entry.dbSize);
}

/** Processes entries for events tx-added-to-pool, with grouping by deployed contract count. */
function processTxAddedToPool(entry: TxAddedToPoolStats, results: BenchmarkCollectedResults) {
append(results, 'tx_size_in_bytes', entry.newContractCount, entry.size);
}

/** Processes a parsed entry from a logfile and updates results */
function processEntry(entry: Stats, results: BenchmarkCollectedResults) {
switch (entry.eventName) {
Expand All @@ -141,6 +147,8 @@ function processEntry(entry: Stats, results: BenchmarkCollectedResults) {
return processL2BlockBuilt(entry, results);
case 'node-synced-chain-history':
return processNodeSyncedChain(entry, results);
case 'tx-added-to-pool':
return processTxAddedToPool(entry, results);
default:
return;
}
Expand Down
6 changes: 6 additions & 0 deletions yarn-project/scripts/src/benchmarks/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export function getMarkdown() {
const metricsByBlockSize = Metrics.filter(m => m.groupBy === 'block-size').map(m => m.name);
const metricsByChainLength = Metrics.filter(m => m.groupBy === 'chain-length').map(m => m.name);
const metricsByCircuitName = Metrics.filter(m => m.groupBy === 'circuit-name').map(m => m.name);
const metricsByContractCount = Metrics.filter(m => m.groupBy === 'contract-count').map(m => m.name);

const baseHash = process.env.BASE_COMMIT_HASH;
const baseUrl = baseHash && `[\`${baseHash.slice(0, 8)}\`](${S3_URL}/benchmarks-v1/master/${baseHash}.json)`;
Expand Down Expand Up @@ -142,6 +143,11 @@ ${getTableContent(pick(benchmark, metricsByChainLength), baseBenchmark, 'blocks'
Stats on running time and I/O sizes collected for every circuit run across all benchmarks.
${getTableContent(transpose(pick(benchmark, metricsByCircuitName)), transpose(baseBenchmark), '', 'Circuit')}

### Miscellaneous

Transaction sizes based on how many contracts are deployed in the tx.
${getTableContent(pick(benchmark, metricsByContractCount), baseBenchmark, 'deployed contracts')}

${COMMENT_MARK}
`;
}
Expand Down
26 changes: 26 additions & 0 deletions yarn-project/types/src/contract_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,17 @@ export class ExtendedContractData {
return new CompleteAddress(this.contractData.contractAddress, this.publicKey, this.partialAddress);
}

/** True if this represents an empty instance. */
public isEmpty(): boolean {
return (
this.contractData.isEmpty() &&
this.publicFunctions.length === 0 &&
this.partialAddress.isZero() &&
this.publicKey.x.isZero() &&
this.publicKey.y.isZero()
);
}

/**
* Deserializes a contract data object from an encoded buffer, using 20 bytes for the eth address.
* @param buffer - Byte array resulting from calling toBuffer.
Expand Down Expand Up @@ -201,6 +212,11 @@ export class ExtendedContractData {
Point.random(),
);
}

/** Generates empty extended contract data. */
static empty(): ExtendedContractData {
return new ExtendedContractData(ContractData.empty(), [], Fr.ZERO, Point.ZERO);
}
}

/**
Expand Down Expand Up @@ -234,6 +250,11 @@ export class ContractData {
return this.toBuffer().toString('hex');
}

/** True if all data is zero. */
public isEmpty(): boolean {
return this.contractAddress.isZero() && this.portalContractAddress.isZero();
}

/**
* Deserializes a contract data object from an encoded buffer, using 20 bytes for the eth address.
* @param buffer - Byte array resulting from calling toBuffer.
Expand Down Expand Up @@ -262,4 +283,9 @@ export class ContractData {
static random(): ContractData {
return new ContractData(AztecAddress.random(), EthAddress.random());
}

/** Generates an empty ContractData. */
static empty(): ContractData {
return new ContractData(AztecAddress.ZERO, EthAddress.ZERO);
}
}
8 changes: 1 addition & 7 deletions yarn-project/types/src/logs/l2_block_l2_logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,7 @@ export class L2BlockL2Logs {
* Gets the total number of logs emitted from all the TxL2Logs.
*/
public getTotalLogCount(): number {
let count = 0;
for (const txLog of this.txLogs) {
for (const functionLog of txLog.functionLogs) {
count += functionLog.logs.length;
}
}
return count;
return this.txLogs.reduce((acc, logs) => acc + logs.getTotalLogCount(), 0);
}

/**
Expand Down
5 changes: 5 additions & 0 deletions yarn-project/types/src/logs/tx_l2_logs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export class TxL2Logs {
return this.functionLogs.reduce((acc, logs) => acc + logs.getSerializedLength(), 0) + 4;
}

/** Gets the total number of logs. */
public getTotalLogCount() {
return this.functionLogs.reduce((acc, logs) => acc + logs.logs.length, 0);
}

/**
* Adds function logs to the existing logs.
* @param functionLogs - The function logs to add
Expand Down
8 changes: 7 additions & 1 deletion yarn-project/types/src/stats/metrics.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { StatsEventName } from './stats.js';

/** How a metric is grouped in benchmarks: by block size, by length of chain processed, or by circuit name. */
export type MetricGroupBy = 'block-size' | 'chain-length' | 'circuit-name';
export type MetricGroupBy = 'block-size' | 'chain-length' | 'circuit-name' | 'contract-count';

/** Definition of a metric to track in benchmarks. */
export interface Metric {
Expand Down Expand Up @@ -121,6 +121,12 @@ export const Metrics = [
description: 'Size of the outputs (ie public inputs) from a circuit simulation.',
events: ['circuit-simulation'],
},
{
name: 'tx_size_in_bytes',
groupBy: 'contract-count',
description: 'Size of txs received in the mempool.',
events: ['tx-added-to-pool'],
},
] as const satisfies readonly Metric[];

/** Metric definitions to track from benchmarks. */
Expand Down
31 changes: 30 additions & 1 deletion yarn-project/types/src/stats/stats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,14 +118,43 @@ export type NoteProcessorStats = {
txs: number;
};

/** Stats for a tx. */
export type TxStats = {
/** Hash of the tx. */
txHash: string;
/** Total size in bytes. */
size: number;
/** Size of the proof. */
proofSize: number;
/** Number of encrypted logs. */
encryptedLogCount: number;
/** Number of unencrypted logs. */
unencryptedLogCount: number;
/** Serialised size of encrypted logs. */
encryptedLogSize: number;
/** Serialised size of unencrypted logs. */
unencryptedLogSize: number;
/** Serialised size of new contract data. */
newContractDataSize: number;
/** Number of new contracts deployed in this tx. */
newContractCount: number;
};

/** A new tx was added to the tx pool. */
export type TxAddedToPoolStats = {
/** Name of the event. */
eventName: 'tx-added-to-pool';
} & TxStats;

/** Stats emitted in structured logs with an `eventName` for tracking. */
export type Stats =
| L1PublishStats
| NodeSyncedChainHistoryStats
| CircuitSimulationStats
| L2BlockBuiltStats
| L2BlockHandledStats
| NoteProcessorCaughtUpStats;
| NoteProcessorCaughtUpStats
| TxAddedToPoolStats;

/** Set of event names across emitted stats. */
export type StatsEventName = Stats['eventName'];
16 changes: 16 additions & 0 deletions yarn-project/types/src/tx/tx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { BufferReader, Tuple } from '@aztec/foundation/serialize';

import { ExtendedContractData } from '../contract_data.js';
import { TxL2Logs } from '../logs/tx_l2_logs.js';
import { TxStats } from '../stats/stats.js';
import { TxHash } from './tx_hash.js';

/**
Expand Down Expand Up @@ -147,6 +148,21 @@ export class Tx {
return Promise.resolve(new TxHash(firstNullifier.toBuffer()));
}

/** Returns stats about this tx. */
getStats(): TxStats {
return {
txHash: this.data!.end.newNullifiers[0].toString(true),
encryptedLogCount: this.encryptedLogs.getTotalLogCount(),
unencryptedLogCount: this.unencryptedLogs.getTotalLogCount(),
encryptedLogSize: this.encryptedLogs.getSerializedLength(),
unencryptedLogSize: this.unencryptedLogs.getSerializedLength(),
newContractCount: this.newContracts.filter(c => !c.isEmpty()).length,
newContractDataSize: this.newContracts.map(c => c.toBuffer().length).reduce((a, b) => a + b, 0),
proofSize: this.proof.buffer.length,
size: this.toBuffer().length,
};
}

/**
* Convenience function to get a hash out of a tx or a tx-like.
* @param tx - Tx-like object.
Expand Down