From bd56b990ac7cddd59942fc91a37d775cd2115685 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Tue, 16 Jan 2024 11:04:53 +0000 Subject: [PATCH 1/4] feat: track hashing --- .../circuit-types/src/stats/metrics.ts | 48 +++++++++++++++++++ yarn-project/circuit-types/src/stats/stats.ts | 4 ++ .../merkle-tree/src/hasher_with_stats.ts | 37 ++++++++++++++ .../standard_indexed_tree.ts | 3 ++ .../src/standard_tree/standard_tree.ts | 3 ++ yarn-project/merkle-tree/src/tree_base.ts | 7 ++- .../scripts/src/benchmarks/aggregate.ts | 8 ++++ 7 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 yarn-project/merkle-tree/src/hasher_with_stats.ts diff --git a/yarn-project/circuit-types/src/stats/metrics.ts b/yarn-project/circuit-types/src/stats/metrics.ts index 479a3a87eb9d..a3a72a21ab16 100644 --- a/yarn-project/circuit-types/src/stats/metrics.ts +++ b/yarn-project/circuit-types/src/stats/metrics.ts @@ -169,6 +169,54 @@ export const Metrics = [ description: 'Time to insert a batch of leaves into an indexed tree', events: ['tree-insertion'], }, + { + name: 'hash_append_only_tree_16_depth_count', + groupBy: 'leaf-count', + description: 'The number of hashes necessary to insert a batch of leaves into', + events: ['tree-insertion'], + }, + { + name: 'hash_append_only_tree_32_depth_count', + groupBy: 'leaf-count', + description: 'The number of hashes necessary to insert a batch of leaves into', + events: ['tree-insertion'], + }, + { + name: 'hash_duration_append_only_tree_16_depth_ms', + groupBy: 'leaf-count', + description: 'Average duration for a hash operation', + events: ['tree-insertion'], + }, + { + name: 'hash_duration_append_only_tree_32_depth_ms', + groupBy: 'leaf-count', + description: 'Average duration for a hash operation', + events: ['tree-insertion'], + }, + { + name: 'hash_indexed_tree_20_depth_count', + groupBy: 'leaf-count', + description: 'The number of hashes necessary to insert a batch of leaves into', + events: ['tree-insertion'], + }, + { + name: 'hash_indexed_tree_40_depth_count', + groupBy: 'leaf-count', + description: 'The number of hashes necessary to insert a batch of leaves into', + events: ['tree-insertion'], + }, + { + name: 'hash_duration_indexed_tree_20_depth_ms', + groupBy: 'leaf-count', + description: 'Average duration for a hash operation', + events: ['tree-insertion'], + }, + { + name: 'hash_duration_indexed_tree_40_depth_ms', + groupBy: 'leaf-count', + description: 'Average duration for a hash operation', + events: ['tree-insertion'], + }, ] as const satisfies readonly Metric[]; /** Metric definitions to track from benchmarks. */ diff --git a/yarn-project/circuit-types/src/stats/stats.ts b/yarn-project/circuit-types/src/stats/stats.ts index b78b98ea0bea..74ca04d4005f 100644 --- a/yarn-project/circuit-types/src/stats/stats.ts +++ b/yarn-project/circuit-types/src/stats/stats.ts @@ -184,6 +184,10 @@ export type TreeInsertionStats = { treeDepth: number; /** Tree type */ treeType: 'append-only' | 'indexed'; + /** Number of hashes performed */ + hashCount: number; + /** Average duration of a hash operation */ + hashDuration: number; }; /** A new tx was added to the tx pool. */ diff --git a/yarn-project/merkle-tree/src/hasher_with_stats.ts b/yarn-project/merkle-tree/src/hasher_with_stats.ts new file mode 100644 index 000000000000..56c1cfad6b79 --- /dev/null +++ b/yarn-project/merkle-tree/src/hasher_with_stats.ts @@ -0,0 +1,37 @@ +import { Hasher } from '@aztec/types/interfaces'; + +import { createHistogram, performance } from 'perf_hooks'; + +/** + * A helper class to track stats for a Hasher + */ +export class HasherWithStats implements Hasher { + hashCount = 0; + hashHistogram = createHistogram(); + + hash: Hasher['hash']; + hashInputs: Hasher['hashInputs']; + + constructor(hasher: Hasher) { + this.hash = performance.timerify((lhs, rhs) => { + this.hashCount++; + return hasher.hash(lhs, rhs); + }); + this.hashInputs = performance.timerify((inputs: Buffer[]) => { + this.hashCount++; + return hasher.hashInputs(inputs); + }); + } + + stats() { + return { + count: this.hashCount, + averageDuration: this.hashHistogram.mean, + }; + } + + reset() { + this.hashCount = 0; + this.hashHistogram.reset(); + } +} diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index 22297f55da9f..ed211c40fa29 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -510,6 +510,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { leaves: Buffer[], subtreeHeight: SubtreeHeight, ): Promise> { + this.hasher.reset(); const timer = new Timer(); const insertedKeys = new Map(); const emptyLowLeafWitness = getEmptyLowLeafWitness(this.getDepth() as TreeHeight, this.leafPreimageFactory); @@ -614,6 +615,8 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { treeName: this.getName(), treeDepth: this.getDepth(), treeType: 'indexed', + hashCount: this.hasher.stats().count, + hashDuration: this.hasher.stats().averageDuration, } satisfies TreeInsertionStats); return { diff --git a/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts b/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts index cc9af7e28bf4..3b03d2224044 100644 --- a/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts +++ b/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts @@ -17,6 +17,7 @@ export class StandardTree extends TreeBase implements AppendOnlyTree { * @returns Empty promise. */ public async appendLeaves(leaves: Buffer[]): Promise { + this.hasher.reset(); const timer = new Timer(); await super.appendLeaves(leaves); this.log(`Inserted ${leaves.length} leaves into ${this.getName()} tree`, { @@ -26,6 +27,8 @@ export class StandardTree extends TreeBase implements AppendOnlyTree { treeName: this.getName(), treeDepth: this.getDepth(), treeType: 'append-only', + hashCount: this.hasher.stats().count, + hashDuration: this.hasher.stats().averageDuration, } satisfies TreeInsertionStats); } diff --git a/yarn-project/merkle-tree/src/tree_base.ts b/yarn-project/merkle-tree/src/tree_base.ts index 2d32b032279a..36f9b12b4443 100644 --- a/yarn-project/merkle-tree/src/tree_base.ts +++ b/yarn-project/merkle-tree/src/tree_base.ts @@ -5,6 +5,7 @@ import { SiblingPath } from '@aztec/types/membership'; import { LevelUp, LevelUpChain } from 'levelup'; +import { HasherWithStats } from './hasher_with_stats.js'; import { MerkleTree } from './interfaces/merkle_tree.js'; const MAX_DEPTH = 254; @@ -40,9 +41,11 @@ export abstract class TreeBase implements MerkleTree { private cache: { [key: string]: Buffer } = {}; protected log: DebugLogger; + protected hasher: HasherWithStats; + public constructor( protected db: LevelUp, - protected hasher: Hasher, + hasher: Hasher, private name: string, private depth: number, protected size: bigint = 0n, @@ -52,6 +55,8 @@ export abstract class TreeBase implements MerkleTree { throw Error('Invalid depth'); } + this.hasher = new HasherWithStats(hasher); + // Compute the zero values at each layer. let current = INITIAL_LEAF; for (let i = depth - 1; i >= 0; --i) { diff --git a/yarn-project/scripts/src/benchmarks/aggregate.ts b/yarn-project/scripts/src/benchmarks/aggregate.ts index 04065d63aa3e..52ab8f6a8f6d 100644 --- a/yarn-project/scripts/src/benchmarks/aggregate.ts +++ b/yarn-project/scripts/src/benchmarks/aggregate.ts @@ -171,14 +171,22 @@ function processTreeInsertion(entry: TreeInsertionStats, results: BenchmarkColle if (entry.treeType === 'append-only') { if (depth === 16) { append(results, 'batch_insert_into_append_only_tree_16_depth_ms', bucket, entry.duration); + append(results, 'hash_append_only_tree_16_depth_count', bucket, entry.hashCount); + append(results, 'hash_duration_append_only_tree_16_depth_ms', bucket, entry.hashDuration); } else if (depth === 32) { append(results, 'batch_insert_into_append_only_tree_32_depth_ms', bucket, entry.duration); + append(results, 'hash_append_only_tree_32_depth_count', bucket, entry.hashCount); + append(results, 'hash_duration_append_only_tree_32_depth_ms', bucket, entry.hashDuration); } } else if (entry.treeType === 'indexed') { if (depth === 20) { append(results, 'batch_insert_into_indexed_tree_20_depth_ms', bucket, entry.duration); + append(results, 'hash_indexed_tree_20_depth_count', bucket, entry.hashCount); + append(results, 'hash_duration_indexed_tree_20_depth_ms', bucket, entry.hashDuration); } else if (depth === 40) { append(results, 'batch_insert_into_indexed_tree_40_depth_ms', bucket, entry.duration); + append(results, 'hash_indexed_tree_40_depth_count', bucket, entry.hashCount); + append(results, 'hash_duration_indexed_tree_40_depth_ms', bucket, entry.hashDuration); } } } From fe63f32473912d1952b905b3e25fb160f97021d2 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Tue, 16 Jan 2024 11:32:09 +0000 Subject: [PATCH 2/4] fix: actually track hash duration --- .../merkle-tree/src/hasher_with_stats.ts | 34 +++++++++++++------ .../standard_indexed_tree.ts | 3 +- .../src/standard_tree/standard_tree.ts | 3 +- 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/yarn-project/merkle-tree/src/hasher_with_stats.ts b/yarn-project/merkle-tree/src/hasher_with_stats.ts index 56c1cfad6b79..a7611e023092 100644 --- a/yarn-project/merkle-tree/src/hasher_with_stats.ts +++ b/yarn-project/merkle-tree/src/hasher_with_stats.ts @@ -7,31 +7,45 @@ import { createHistogram, performance } from 'perf_hooks'; */ export class HasherWithStats implements Hasher { hashCount = 0; + hashInputsCount = 0; hashHistogram = createHistogram(); + hashInputsHistogram = createHistogram(); hash: Hasher['hash']; hashInputs: Hasher['hashInputs']; constructor(hasher: Hasher) { - this.hash = performance.timerify((lhs, rhs) => { - this.hashCount++; - return hasher.hash(lhs, rhs); - }); - this.hashInputs = performance.timerify((inputs: Buffer[]) => { - this.hashCount++; - return hasher.hashInputs(inputs); - }); + this.hash = performance.timerify( + (lhs, rhs) => { + this.hashCount++; + return hasher.hash(lhs, rhs); + }, + { histogram: this.hashHistogram }, + ); + this.hashInputs = performance.timerify( + (inputs: Buffer[]) => { + this.hashInputsCount++; + return hasher.hashInputs(inputs); + }, + { histogram: this.hashInputsHistogram }, + ); } stats() { return { - count: this.hashCount, - averageDuration: this.hashHistogram.mean, + hashCount: this.hashCount, + // timerify records in ns, convert to ms + hashDuration: this.hashHistogram.mean / 10e6, + hashInputsCount: this.hashInputsCount, + hashInputsDuration: this.hashInputsHistogram.mean / 10e6, }; } reset() { this.hashCount = 0; this.hashHistogram.reset(); + + this.hashInputsCount = 0; + this.hashInputsHistogram.reset(); } } diff --git a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts index ed211c40fa29..dcd56f31839d 100644 --- a/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts +++ b/yarn-project/merkle-tree/src/standard_indexed_tree/standard_indexed_tree.ts @@ -615,8 +615,7 @@ export class StandardIndexedTree extends TreeBase implements IndexedTree { treeName: this.getName(), treeDepth: this.getDepth(), treeType: 'indexed', - hashCount: this.hasher.stats().count, - hashDuration: this.hasher.stats().averageDuration, + ...this.hasher.stats(), } satisfies TreeInsertionStats); return { diff --git a/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts b/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts index 3b03d2224044..cc587ee3a3d1 100644 --- a/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts +++ b/yarn-project/merkle-tree/src/standard_tree/standard_tree.ts @@ -27,8 +27,7 @@ export class StandardTree extends TreeBase implements AppendOnlyTree { treeName: this.getName(), treeDepth: this.getDepth(), treeType: 'append-only', - hashCount: this.hasher.stats().count, - hashDuration: this.hasher.stats().averageDuration, + ...this.hasher.stats(), } satisfies TreeInsertionStats); } From 394769887722b07d29651bfb97ed05cac4aa0bdb Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Tue, 16 Jan 2024 13:26:24 +0000 Subject: [PATCH 3/4] fix: divide by 1e6 to convert ns to ms (off by one) --- yarn-project/merkle-tree/src/hasher_with_stats.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/yarn-project/merkle-tree/src/hasher_with_stats.ts b/yarn-project/merkle-tree/src/hasher_with_stats.ts index a7611e023092..34d00d6b41ae 100644 --- a/yarn-project/merkle-tree/src/hasher_with_stats.ts +++ b/yarn-project/merkle-tree/src/hasher_with_stats.ts @@ -35,9 +35,9 @@ export class HasherWithStats implements Hasher { return { hashCount: this.hashCount, // timerify records in ns, convert to ms - hashDuration: this.hashHistogram.mean / 10e6, + hashDuration: this.hashHistogram.mean / 1e6, hashInputsCount: this.hashInputsCount, - hashInputsDuration: this.hashInputsHistogram.mean / 10e6, + hashInputsDuration: this.hashInputsHistogram.mean / 1e6, }; } From 30aa06fcc260a7cec788f77b9ac86dffb5047e05 Mon Sep 17 00:00:00 2001 From: Alex Gherghisan Date: Tue, 16 Jan 2024 13:42:00 +0000 Subject: [PATCH 4/4] refactor: rename and re-arrange hash metrics --- .../circuit-types/src/stats/metrics.ts | 40 +++++++++---------- .../scripts/src/benchmarks/aggregate.ts | 16 ++++---- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/yarn-project/circuit-types/src/stats/metrics.ts b/yarn-project/circuit-types/src/stats/metrics.ts index a3a72a21ab16..347732a591b7 100644 --- a/yarn-project/circuit-types/src/stats/metrics.ts +++ b/yarn-project/circuit-types/src/stats/metrics.ts @@ -152,67 +152,67 @@ export const Metrics = [ events: ['tree-insertion'], }, { - name: 'batch_insert_into_append_only_tree_32_depth_ms', + name: 'batch_insert_into_append_only_tree_16_depth_hash_count', groupBy: 'leaf-count', - description: 'Time to insert a batch of leaves into an append-only tree', + description: 'The number of hashes necessary to insert a batch of leaves into', events: ['tree-insertion'], }, { - name: 'batch_insert_into_indexed_tree_20_depth_ms', + name: 'batch_insert_into_append_only_tree_16_depth_hash_ms', groupBy: 'leaf-count', - description: 'Time to insert a batch of leaves into an indexed tree', + description: 'Average duration for a hash operation', events: ['tree-insertion'], }, { - name: 'batch_insert_into_indexed_tree_40_depth_ms', + name: 'batch_insert_into_append_only_tree_32_depth_ms', groupBy: 'leaf-count', - description: 'Time to insert a batch of leaves into an indexed tree', + description: 'Time to insert a batch of leaves into an append-only tree', events: ['tree-insertion'], }, { - name: 'hash_append_only_tree_16_depth_count', + name: 'batch_insert_into_append_only_tree_32_depth_hash_count', groupBy: 'leaf-count', description: 'The number of hashes necessary to insert a batch of leaves into', events: ['tree-insertion'], }, { - name: 'hash_append_only_tree_32_depth_count', + name: 'batch_insert_into_append_only_tree_32_depth_hash_ms', groupBy: 'leaf-count', - description: 'The number of hashes necessary to insert a batch of leaves into', + description: 'Average duration for a hash operation', events: ['tree-insertion'], }, { - name: 'hash_duration_append_only_tree_16_depth_ms', + name: 'batch_insert_into_indexed_tree_20_depth_ms', groupBy: 'leaf-count', - description: 'Average duration for a hash operation', + description: 'Time to insert a batch of leaves into an indexed tree', events: ['tree-insertion'], }, { - name: 'hash_duration_append_only_tree_32_depth_ms', + name: 'batch_insert_into_indexed_tree_20_depth_hash_count', groupBy: 'leaf-count', - description: 'Average duration for a hash operation', + description: 'The number of hashes necessary to insert a batch of leaves into', events: ['tree-insertion'], }, { - name: 'hash_indexed_tree_20_depth_count', + name: 'batch_insert_into_indexed_tree_20_depth_hash_ms', groupBy: 'leaf-count', - description: 'The number of hashes necessary to insert a batch of leaves into', + description: 'Average duration for a hash operation', events: ['tree-insertion'], }, { - name: 'hash_indexed_tree_40_depth_count', + name: 'batch_insert_into_indexed_tree_40_depth_ms', groupBy: 'leaf-count', - description: 'The number of hashes necessary to insert a batch of leaves into', + description: 'Time to insert a batch of leaves into an indexed tree', events: ['tree-insertion'], }, { - name: 'hash_duration_indexed_tree_20_depth_ms', + name: 'batch_insert_into_indexed_tree_40_depth_hash_count', groupBy: 'leaf-count', - description: 'Average duration for a hash operation', + description: 'The number of hashes necessary to insert a batch of leaves into', events: ['tree-insertion'], }, { - name: 'hash_duration_indexed_tree_40_depth_ms', + name: 'batch_insert_into_indexed_tree_40_depth_hash_ms', groupBy: 'leaf-count', description: 'Average duration for a hash operation', events: ['tree-insertion'], diff --git a/yarn-project/scripts/src/benchmarks/aggregate.ts b/yarn-project/scripts/src/benchmarks/aggregate.ts index 52ab8f6a8f6d..1b2235765d9e 100644 --- a/yarn-project/scripts/src/benchmarks/aggregate.ts +++ b/yarn-project/scripts/src/benchmarks/aggregate.ts @@ -171,22 +171,22 @@ function processTreeInsertion(entry: TreeInsertionStats, results: BenchmarkColle if (entry.treeType === 'append-only') { if (depth === 16) { append(results, 'batch_insert_into_append_only_tree_16_depth_ms', bucket, entry.duration); - append(results, 'hash_append_only_tree_16_depth_count', bucket, entry.hashCount); - append(results, 'hash_duration_append_only_tree_16_depth_ms', bucket, entry.hashDuration); + append(results, 'batch_insert_into_append_only_tree_16_depth_hash_count', bucket, entry.hashCount); + append(results, 'batch_insert_into_append_only_tree_16_depth_hash_ms', bucket, entry.hashDuration); } else if (depth === 32) { append(results, 'batch_insert_into_append_only_tree_32_depth_ms', bucket, entry.duration); - append(results, 'hash_append_only_tree_32_depth_count', bucket, entry.hashCount); - append(results, 'hash_duration_append_only_tree_32_depth_ms', bucket, entry.hashDuration); + append(results, 'batch_insert_into_append_only_tree_32_depth_hash_count', bucket, entry.hashCount); + append(results, 'batch_insert_into_append_only_tree_32_depth_hash_ms', bucket, entry.hashDuration); } } else if (entry.treeType === 'indexed') { if (depth === 20) { append(results, 'batch_insert_into_indexed_tree_20_depth_ms', bucket, entry.duration); - append(results, 'hash_indexed_tree_20_depth_count', bucket, entry.hashCount); - append(results, 'hash_duration_indexed_tree_20_depth_ms', bucket, entry.hashDuration); + append(results, 'batch_insert_into_indexed_tree_20_depth_hash_count', bucket, entry.hashCount); + append(results, 'batch_insert_into_indexed_tree_20_depth_hash_ms', bucket, entry.hashDuration); } else if (depth === 40) { append(results, 'batch_insert_into_indexed_tree_40_depth_ms', bucket, entry.duration); - append(results, 'hash_indexed_tree_40_depth_count', bucket, entry.hashCount); - append(results, 'hash_duration_indexed_tree_40_depth_ms', bucket, entry.hashDuration); + append(results, 'batch_insert_into_indexed_tree_40_depth_hash_count', bucket, entry.hashCount); + append(results, 'batch_insert_into_indexed_tree_40_depth_hash_ms', bucket, entry.hashDuration); } } }