Skip to content

Commit aaf0d8c

Browse files
authored
chore: benchmark sha256 number of instructions executed in AVM (#11253)
Also move hash stuff into another AVM test contract. This lets us get stats like below, although note that this isn't directly an output from the yarn tests: For a `sha256` of N bytes, how many AVM/Brillig instructions are executed and how much gas does it consume? ```N= 10 bytes: 3998 instructions, 69006 L2 Gas N= 20 bytes: 4513 instructions, 77778 L2 Gas N= 30 bytes: 5328 instructions, 91326 L2 Gas N= 40 bytes: 5843 instructions, 100098 L2 Gas N= 50 bytes: 6672 instructions, 113877 L2 Gas N= 60 bytes: 7490 instructions, 127626 L2 Gas N= 70 bytes: 8662 instructions, 147936 L2 Gas N= 80 bytes: 9207 instructions, 157368 L2 Gas N= 90 bytes: 10052 instructions, 171576 L2 Gas N= 100 bytes: 10597 instructions, 181008 L2 Gas N= 255 bytes: 23046 instructions, 392055 L2 Gas N= 256 bytes: 23022 instructions, 392121 L2 Gas N= 511 bytes: 43107 instructions, 732336 L2 Gas N= 512 bytes: 42978 instructions, 731004 L2 Gas N=2048 bytes: 162801 instructions, 2765820 L2 Gas```
1 parent 2ba1e71 commit aaf0d8c

File tree

6 files changed

+177
-8
lines changed

6 files changed

+177
-8
lines changed

noir-projects/noir-contracts/Nargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"contracts/auth_contract",
66
"contracts/auth_registry_contract",
77
"contracts/auth_wit_test_contract",
8+
"contracts/avm_gadgets_test_contract",
89
"contracts/avm_initializer_test_contract",
910
"contracts/avm_test_contract",
1011
"contracts/fpc_contract",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[package]
2+
name = "avm_gadgets_test_contract"
3+
authors = [""]
4+
compiler_version = ">=0.25.0"
5+
type = "contract"
6+
7+
[dependencies]
8+
aztec = { path = "../../../aztec-nr/aztec" }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use dep::aztec::macros::aztec;
2+
3+
#[aztec]
4+
contract AvmGadgetsTest {
5+
use dep::aztec::macros::functions::public;
6+
7+
#[public]
8+
fn keccak_hash(data: [u8; 10]) -> [u8; 32] {
9+
std::hash::keccak256(data, data.len() as u32)
10+
}
11+
12+
#[public]
13+
fn keccak_f1600(data: [u64; 25]) -> [u64; 25] {
14+
std::hash::keccak::keccakf1600(data)
15+
}
16+
17+
#[public]
18+
fn poseidon2_hash(data: [Field; 10]) -> Field {
19+
std::hash::poseidon2::Poseidon2::hash(data, data.len())
20+
}
21+
22+
#[public]
23+
fn sha256_hash_10(data: [u8; 10]) -> [u8; 32] {
24+
std::hash::sha256(data)
25+
}
26+
#[public]
27+
fn sha256_hash_20(data: [u8; 20]) -> [u8; 32] {
28+
std::hash::sha256(data)
29+
}
30+
#[public]
31+
fn sha256_hash_30(data: [u8; 30]) -> [u8; 32] {
32+
std::hash::sha256(data)
33+
}
34+
#[public]
35+
fn sha256_hash_40(data: [u8; 40]) -> [u8; 32] {
36+
std::hash::sha256(data)
37+
}
38+
#[public]
39+
fn sha256_hash_50(data: [u8; 50]) -> [u8; 32] {
40+
std::hash::sha256(data)
41+
}
42+
#[public]
43+
fn sha256_hash_60(data: [u8; 60]) -> [u8; 32] {
44+
std::hash::sha256(data)
45+
}
46+
#[public]
47+
fn sha256_hash_70(data: [u8; 70]) -> [u8; 32] {
48+
std::hash::sha256(data)
49+
}
50+
#[public]
51+
fn sha256_hash_80(data: [u8; 80]) -> [u8; 32] {
52+
std::hash::sha256(data)
53+
}
54+
#[public]
55+
fn sha256_hash_90(data: [u8; 90]) -> [u8; 32] {
56+
std::hash::sha256(data)
57+
}
58+
#[public]
59+
fn sha256_hash_100(data: [u8; 100]) -> [u8; 32] {
60+
std::hash::sha256(data)
61+
}
62+
#[public]
63+
fn sha256_hash_255(data: [u8; 255]) -> [u8; 32] {
64+
std::hash::sha256(data)
65+
}
66+
#[public]
67+
fn sha256_hash_256(data: [u8; 256]) -> [u8; 32] {
68+
std::hash::sha256(data)
69+
}
70+
#[public]
71+
fn sha256_hash_511(data: [u8; 511]) -> [u8; 32] {
72+
std::hash::sha256(data)
73+
}
74+
#[public]
75+
fn sha256_hash_512(data: [u8; 512]) -> [u8; 32] {
76+
std::hash::sha256(data)
77+
}
78+
79+
#[public]
80+
fn sha256_hash_2048(data: [u8; 2048]) -> [u8; 32] {
81+
std::hash::sha256(data)
82+
}
83+
84+
#[public]
85+
fn pedersen_hash(data: [Field; 10]) -> Field {
86+
std::hash::pedersen_hash(data)
87+
}
88+
89+
#[public]
90+
fn pedersen_hash_with_index(data: [Field; 10]) -> Field {
91+
std::hash::pedersen_hash_with_separator(data, /*index=*/ 20)
92+
}
93+
}

noir-projects/noir-contracts/contracts/avm_test_contract/src/main.nr

+8-5
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,8 @@ pub contract AvmTest {
297297
}
298298

299299
/************************************************************************
300+
<<<<<<< HEAD
301+
=======
300302
* Hashing functions
301303
************************************************************************/
302304
#[public]
@@ -330,6 +332,7 @@ pub contract AvmTest {
330332
}
331333

332334
/************************************************************************
335+
>>>>>>> master
333336
* Contract instance
334337
************************************************************************/
335338
#[public]
@@ -660,15 +663,15 @@ pub contract AvmTest {
660663
dep::aztec::oracle::debug_log::debug_log("read_storage_map");
661664
let _ = read_storage_map(context.this_address());
662665
dep::aztec::oracle::debug_log::debug_log("keccak_hash");
663-
let _ = keccak_hash(args_u8);
666+
let _ = std::hash::keccak256(args_u8, args_u8.len() as u32);
664667
dep::aztec::oracle::debug_log::debug_log("sha256_hash");
665-
let _ = sha256_hash(args_u8);
668+
let _ = std::hash::sha256(args_u8);
666669
dep::aztec::oracle::debug_log::debug_log("poseidon2_hash");
667-
let _ = poseidon2_hash(args_field);
670+
let _ = std::hash::poseidon2::Poseidon2::hash(args_field, args_field.len());
668671
dep::aztec::oracle::debug_log::debug_log("pedersen_hash");
669-
let _ = pedersen_hash(args_field);
672+
let _ = std::hash::pedersen_hash(args_field);
670673
dep::aztec::oracle::debug_log::debug_log("pedersen_hash_with_index");
671-
let _ = pedersen_hash_with_index(args_field);
674+
let _ = std::hash::pedersen_hash_with_separator(args_field, /*index=*/ 20);
672675
dep::aztec::oracle::debug_log::debug_log("test_get_contract_instance");
673676
test_get_contract_instance_matches(
674677
get_instance_for_address,

yarn-project/simulator/src/avm/avm_simulator.test.ts

+28-3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { AvmSimulator } from './avm_simulator.js';
3636
import { AvmEphemeralForest } from './avm_tree.js';
3737
import { isAvmBytecode, markBytecodeAsAvm } from './bytecode_utils.js';
3838
import {
39+
getAvmGadgetsTestContractBytecode,
3940
getAvmTestContractArtifact,
4041
getAvmTestContractBytecode,
4142
initContext,
@@ -384,19 +385,43 @@ describe('AVM simulator: transpiled Noir contracts', () => {
384385
});
385386
});
386387

388+
/*
389+
* Can run these as follows to measure sha256 instruction execution counts:
390+
* for i in 10 20 30 40 50 60 70 80 90 100 255 256 511 512 2048; do
391+
* echo sha-ing $i...;
392+
* LOG_LEVEL=debug yarn test src/avm/avm_simulator.test.ts -t "sha256_hash_$i " &> sha$i.log;
393+
* done
394+
* for i in 10 20 30 40 50 60 70 80 90 100 255 256 511 512 2048; do
395+
* echo sha256 of $i bytes $(grep -Eo 'Executed .* instructions.* Gas' sha$i.log);
396+
* done
397+
*/
387398
describe.each([
388-
['sha256_hash', /*input=*/ randomMemoryBytes(10), /*output=*/ sha256FromMemoryBytes],
399+
['sha256_hash_10', /*input=*/ randomMemoryBytes(10), /*output=*/ sha256FromMemoryBytes],
400+
['sha256_hash_20', /*input=*/ randomMemoryBytes(20), /*output=*/ sha256FromMemoryBytes],
401+
['sha256_hash_30', /*input=*/ randomMemoryBytes(30), /*output=*/ sha256FromMemoryBytes],
402+
['sha256_hash_40', /*input=*/ randomMemoryBytes(40), /*output=*/ sha256FromMemoryBytes],
403+
['sha256_hash_50', /*input=*/ randomMemoryBytes(50), /*output=*/ sha256FromMemoryBytes],
404+
['sha256_hash_60', /*input=*/ randomMemoryBytes(60), /*output=*/ sha256FromMemoryBytes],
405+
['sha256_hash_70', /*input=*/ randomMemoryBytes(70), /*output=*/ sha256FromMemoryBytes],
406+
['sha256_hash_80', /*input=*/ randomMemoryBytes(80), /*output=*/ sha256FromMemoryBytes],
407+
['sha256_hash_90', /*input=*/ randomMemoryBytes(90), /*output=*/ sha256FromMemoryBytes],
408+
['sha256_hash_100', /*input=*/ randomMemoryBytes(100), /*output=*/ sha256FromMemoryBytes],
409+
['sha256_hash_255', /*input=*/ randomMemoryBytes(255), /*output=*/ sha256FromMemoryBytes],
410+
['sha256_hash_256', /*input=*/ randomMemoryBytes(256), /*output=*/ sha256FromMemoryBytes],
411+
['sha256_hash_511', /*input=*/ randomMemoryBytes(511), /*output=*/ sha256FromMemoryBytes],
412+
['sha256_hash_512', /*input=*/ randomMemoryBytes(512), /*output=*/ sha256FromMemoryBytes],
413+
['sha256_hash_2048', /*input=*/ randomMemoryBytes(2048), /*output=*/ sha256FromMemoryBytes],
389414
['keccak_hash', /*input=*/ randomMemoryBytes(10), /*output=*/ keccak256FromMemoryBytes],
390415
['keccak_f1600', /*input=*/ randomMemoryUint64s(25), /*output=*/ keccakF1600FromMemoryUint64s],
391416
['poseidon2_hash', /*input=*/ randomMemoryFields(10), /*output=*/ poseidon2FromMemoryFields],
392417
['pedersen_hash', /*input=*/ randomMemoryFields(10), /*output=*/ pedersenFromMemoryFields],
393418
['pedersen_hash_with_index', /*input=*/ randomMemoryFields(10), /*output=*/ indexedPedersenFromMemoryFields],
394419
])('Hashes in noir contracts', (name: string, input: MemoryValue[], output: (msg: any[]) => Promise<Fr[]>) => {
395-
it(`Should execute contract function that performs ${name}`, async () => {
420+
it(`Should execute contract function that performs ${name} on input of length ${input.length}`, async () => {
396421
const calldata = input.map(e => e.toFr());
397422

398423
const context = initContext({ env: initExecutionEnvironment({ calldata }) });
399-
const bytecode = getAvmTestContractBytecode(name);
424+
const bytecode = getAvmGadgetsTestContractBytecode(name);
400425
const results = await new AvmSimulator(context).executeBytecode(bytecode);
401426

402427
expect(results.reverted).toBe(false);

yarn-project/simulator/src/avm/fixtures/index.ts

+39
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { type ContractArtifact, type FunctionArtifact, FunctionSelector } from '
44
import { AztecAddress } from '@aztec/foundation/aztec-address';
55
import { EthAddress } from '@aztec/foundation/eth-address';
66
import { Fr } from '@aztec/foundation/fields';
7+
import { AvmGadgetsTestContractArtifact } from '@aztec/noir-contracts.js/AvmGadgetsTest';
78
import { AvmTestContractArtifact } from '@aztec/noir-contracts.js/AvmTest';
89

910
import { strict as assert } from 'assert';
@@ -169,6 +170,13 @@ export function getAvmTestContractFunctionSelector(functionName: string): Promis
169170
return getFunctionSelector(functionName, AvmTestContractArtifact);
170171
}
171172

173+
export function getAvmGadgetsTestContractFunctionSelector(functionName: string): Promise<FunctionSelector> {
174+
const artifact = AvmGadgetsTestContractArtifact.functions.find(f => f.name === functionName)!;
175+
assert(!!artifact, `Function ${functionName} not found in AvmGadgetsTestContractArtifact`);
176+
const params = artifact.parameters;
177+
return FunctionSelector.fromNameAndParameters(artifact.name, params);
178+
}
179+
172180
export function getAvmTestContractArtifact(functionName: string): FunctionArtifact {
173181
const artifact = getContractFunctionArtifact(functionName, AvmTestContractArtifact);
174182
assert(
@@ -178,15 +186,46 @@ export function getAvmTestContractArtifact(functionName: string): FunctionArtifa
178186
return artifact;
179187
}
180188

189+
export function getAvmGadgetsTestContractArtifact(functionName: string): FunctionArtifact {
190+
const artifact = AvmGadgetsTestContractArtifact.functions.find(f => f.name === functionName)!;
191+
assert(
192+
!!artifact?.bytecode,
193+
`No bytecode found for function ${functionName}. Try re-running bootstrap.sh on the repository root.`,
194+
);
195+
return artifact;
196+
}
197+
181198
export function getAvmTestContractBytecode(functionName: string): Buffer {
182199
const artifact = getAvmTestContractArtifact(functionName);
183200
return artifact.bytecode;
184201
}
185202

203+
export function getAvmGadgetsTestContractBytecode(functionName: string): Buffer {
204+
const artifact = getAvmGadgetsTestContractArtifact(functionName);
205+
return artifact.bytecode;
206+
}
207+
186208
export function resolveAvmTestContractAssertionMessage(
187209
functionName: string,
188210
revertReason: AvmRevertReason,
189211
output: Fr[],
190212
): string | undefined {
191213
return resolveContractAssertionMessage(functionName, revertReason, output, AvmTestContractArtifact);
192214
}
215+
216+
export function resolveAvmGadgetsTestContractAssertionMessage(
217+
functionName: string,
218+
revertReason: AvmRevertReason,
219+
output: Fr[],
220+
): string | undefined {
221+
traverseCauseChain(revertReason, cause => {
222+
revertReason = cause as AvmRevertReason;
223+
});
224+
225+
const functionArtifact = AvmGadgetsTestContractArtifact.functions.find(f => f.name === functionName);
226+
if (!functionArtifact || !revertReason.noirCallStack || !isNoirCallStackUnresolved(revertReason.noirCallStack)) {
227+
return undefined;
228+
}
229+
230+
return resolveAssertionMessageFromRevertData(output, functionArtifact);
231+
}

0 commit comments

Comments
 (0)