-
Notifications
You must be signed in to change notification settings - Fork 598
feat: new test programs for wasm benchmarking #8389
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
Changes from all commits
92411e3
2277d6d
db809db
d54dc49
3b77e05
7d646fa
58b59c8
c5ca98e
d6f095a
4983af2
9826685
031a656
b29f9cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| #!/usr/bin/env node | ||
| import { Crs, Barretenberg, RawBuffer } from './index.js'; | ||
| import { GrumpkinCrs } from './crs/node/index.js'; | ||
| import createDebug from 'debug'; | ||
| import { readFileSync, writeFileSync } from 'fs'; | ||
| import { gunzipSync } from 'zlib'; | ||
|
|
@@ -9,13 +10,13 @@ import path from 'path'; | |
| createDebug.log = console.error.bind(console); | ||
| const debug = createDebug('bb.js'); | ||
|
|
||
| // Maximum we support in node and the browser is 2^19. | ||
| // This is because both node and browser use barretenberg.wasm. | ||
| // Maximum circuit size for plonk we support in node and the browser is 2^19. | ||
| // This is because both node and browser use barretenberg.wasm which has a 4GB memory limit. | ||
| // | ||
| // This is not a restriction in the bb binary and one should be | ||
| // aware of this discrepancy, when creating proofs in bb versus | ||
| // creating the same proofs in the node CLI. | ||
| const MAX_CIRCUIT_SIZE = 2 ** 19; | ||
| const MAX_ULTRAPLONK_CIRCUIT_SIZE_IN_WASM = 2 ** 19; | ||
| const threads = +process.env.HARDWARE_CONCURRENCY! || undefined; | ||
|
|
||
| function getBytecode(bytecodePath: string) { | ||
|
|
@@ -32,7 +33,7 @@ function getBytecode(bytecodePath: string) { | |
| return decompressed; | ||
| } | ||
|
|
||
| async function getGates(bytecodePath: string, honkRecursion: boolean, api: Barretenberg) { | ||
| async function getGatesUltra(bytecodePath: string, honkRecursion: boolean, api: Barretenberg) { | ||
| const { total } = await computeCircuitSize(bytecodePath, honkRecursion, api); | ||
| return total; | ||
| } | ||
|
|
@@ -50,33 +51,66 @@ async function computeCircuitSize(bytecodePath: string, honkRecursion: boolean, | |
| return { exact, total, subgroup }; | ||
| } | ||
|
|
||
| async function init(bytecodePath: string, crsPath: string, subgroupSizeOverride = -1, honkRecursion = false) { | ||
| async function initUltraPlonk(bytecodePath: string, crsPath: string, subgroupSizeOverride = -1, honkRecursion = false) { | ||
| const api = await Barretenberg.new({ threads }); | ||
|
|
||
| const circuitSize = await getGates(bytecodePath, honkRecursion, api); | ||
| const circuitSize = await getGatesUltra(bytecodePath, honkRecursion, api); | ||
| // TODO(https://github.com/AztecProtocol/barretenberg/issues/811): remove subgroupSizeOverride hack for goblin | ||
| const subgroupSize = Math.max(subgroupSizeOverride, Math.pow(2, Math.ceil(Math.log2(circuitSize)))); | ||
| if (subgroupSize > MAX_CIRCUIT_SIZE) { | ||
| throw new Error(`Circuit size of ${subgroupSize} exceeds max supported of ${MAX_CIRCUIT_SIZE}`); | ||
| } | ||
|
|
||
| if (subgroupSize > MAX_ULTRAPLONK_CIRCUIT_SIZE_IN_WASM) { | ||
| throw new Error(`Circuit size of ${subgroupSize} exceeds max supported of ${MAX_ULTRAPLONK_CIRCUIT_SIZE_IN_WASM}`); | ||
| } | ||
| debug(`circuit size: ${circuitSize}`); | ||
| debug(`subgroup size: ${subgroupSize}`); | ||
| debug('loading crs...'); | ||
| // Plus 1 needed! (Move +1 into Crs?) | ||
| const crs = await Crs.new(subgroupSize + 1, crsPath); | ||
|
|
||
| // Important to init slab allocator as first thing, to ensure maximum memory efficiency. | ||
| // Important to init slab allocator as first thing, to ensure maximum memory efficiency for Plonk. | ||
| await api.commonInitSlabAllocator(subgroupSize); | ||
|
|
||
| // Load CRS into wasm global CRS state. | ||
| // TODO: Make RawBuffer be default behavior, and have a specific Vector type for when wanting length prefixed. | ||
| await api.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data())); | ||
|
|
||
| const acirComposer = await api.acirNewAcirComposer(subgroupSize); | ||
| return { api, acirComposer, circuitSize, subgroupSize }; | ||
| } | ||
|
|
||
| async function initUltraHonk(bytecodePath: string, crsPath: string) { | ||
| const api = await Barretenberg.new({ threads }); | ||
|
|
||
| const circuitSize = await getGatesUltra(bytecodePath, /*honkRecursion=*/ true, api); | ||
| // TODO(https://github.com/AztecProtocol/barretenberg/issues/811): remove subgroupSizeOverride hack for goblin | ||
| const dyadicCircuitSize = Math.pow(2, Math.ceil(Math.log2(circuitSize))); | ||
|
|
||
| debug(`circuit size: ${circuitSize}`); | ||
| debug(`dyadic circuit size size: ${dyadicCircuitSize}`); | ||
| debug('loading crs...'); | ||
| // Plus 1 needed! (Move +1 into Crs?) | ||
| const crs = await Crs.new(dyadicCircuitSize + 1, crsPath); | ||
|
|
||
| // Load CRS into wasm global CRS state. | ||
| // TODO: Make RawBuffer be default behavior, and have a specific Vector type for when wanting length prefixed. | ||
| await api.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data())); | ||
| return { api, circuitSize, dyadicCircuitSize }; | ||
| } | ||
|
|
||
| async function initClientIVC(bytecodePath: string, crsPath: string) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. new function for ClientIVC that inits grumpkin SRS |
||
| const api = await Barretenberg.new({ threads }); | ||
|
|
||
| debug('loading BN254 and Grumpkin crs...'); | ||
| // Plus 1 needed! (Move +1 into Crs?) | ||
| const crs = await Crs.new(2 ** 18 + 1, crsPath); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we're going to need to figure out how to count gates for ClientIVC. It will probably just be a megaGates function, but I didn't create it now because it doesn't make sense with the current ClientIVC (which injects more gates from folding and merge verifiers during runtime). |
||
| const grumpkinCrs = await GrumpkinCrs.new(8192 + 1, crsPath); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I knew it was needed for grumpkin but dont recall the reason why we need it for bn254, yes maybe we should consider moving it somewhere in CRS initialisation
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you mean that we don't need the srs for bn254? |
||
|
|
||
| // Load CRS into wasm global CRS state. | ||
| // TODO: Make RawBuffer be default behavior, and have a specific Vector type for when wanting length prefixed. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. link issue?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh oops, this issue was copied from above. I don't even know what it means |
||
| await api.srsInitSrs(new RawBuffer(crs.getG1Data()), crs.numPoints, new RawBuffer(crs.getG2Data())); | ||
| await api.srsInitGrumpkinSrs(new RawBuffer(grumpkinCrs.getG1Data()), grumpkinCrs.numPoints); | ||
| return { api }; | ||
| } | ||
|
|
||
| async function initLite() { | ||
| const api = await Barretenberg.new({ threads: 1 }); | ||
|
|
||
|
|
@@ -94,7 +128,7 @@ export async function proveAndVerify(bytecodePath: string, witnessPath: string, | |
| /* eslint-disable camelcase */ | ||
| const acir_test = path.basename(process.cwd()); | ||
|
|
||
| const { api, acirComposer, circuitSize, subgroupSize } = await init(bytecodePath, crsPath); | ||
| const { api, acirComposer, circuitSize, subgroupSize } = await initUltraPlonk(bytecodePath, crsPath); | ||
| try { | ||
| debug(`creating proof...`); | ||
| const bytecode = getBytecode(bytecodePath); | ||
|
|
@@ -122,7 +156,7 @@ export async function proveAndVerify(bytecodePath: string, witnessPath: string, | |
|
|
||
| export async function proveAndVerifyUltraHonk(bytecodePath: string, witnessPath: string, crsPath: string) { | ||
| /* eslint-disable camelcase */ | ||
| const { api } = await init(bytecodePath, crsPath, -1, true); | ||
| const { api } = await initUltraHonk(bytecodePath, crsPath); | ||
| try { | ||
| const bytecode = getBytecode(bytecodePath); | ||
| const witness = getWitness(witnessPath); | ||
|
|
@@ -137,7 +171,7 @@ export async function proveAndVerifyUltraHonk(bytecodePath: string, witnessPath: | |
|
|
||
| export async function proveAndVerifyMegaHonk(bytecodePath: string, witnessPath: string, crsPath: string) { | ||
| /* eslint-disable camelcase */ | ||
| const { api } = await init(bytecodePath, crsPath); | ||
| const { api } = await initUltraPlonk(bytecodePath, crsPath); | ||
| try { | ||
| const bytecode = getBytecode(bytecodePath); | ||
| const witness = getWitness(witnessPath); | ||
|
|
@@ -152,12 +186,13 @@ export async function proveAndVerifyMegaHonk(bytecodePath: string, witnessPath: | |
|
|
||
| export async function foldAndVerifyProgram(bytecodePath: string, witnessPath: string, crsPath: string) { | ||
| /* eslint-disable camelcase */ | ||
| const { api } = await init(bytecodePath, crsPath); | ||
| const { api } = await initClientIVC(bytecodePath, crsPath); | ||
| try { | ||
| const bytecode = getBytecode(bytecodePath); | ||
| const witness = getWitness(witnessPath); | ||
|
|
||
| const verified = await api.acirFoldAndVerifyProgramStack(bytecode, witness); | ||
| debug(`verified: ${verified}`); | ||
| return verified; | ||
| } finally { | ||
| await api.destroy(); | ||
|
|
@@ -166,7 +201,7 @@ export async function foldAndVerifyProgram(bytecodePath: string, witnessPath: st | |
| } | ||
|
|
||
| export async function prove(bytecodePath: string, witnessPath: string, crsPath: string, outputPath: string) { | ||
| const { api, acirComposer } = await init(bytecodePath, crsPath); | ||
| const { api, acirComposer } = await initUltraPlonk(bytecodePath, crsPath); | ||
| try { | ||
| debug(`creating proof...`); | ||
| const bytecode = getBytecode(bytecodePath); | ||
|
|
@@ -186,10 +221,10 @@ export async function prove(bytecodePath: string, witnessPath: string, crsPath: | |
| } | ||
| } | ||
|
|
||
| export async function gateCount(bytecodePath: string, honkRecursion: boolean) { | ||
| export async function gateCountUltra(bytecodePath: string, honkRecursion: boolean) { | ||
| const api = await Barretenberg.new({ threads: 1 }); | ||
| try { | ||
| const numberOfGates = await getGates(bytecodePath, honkRecursion, api); | ||
| const numberOfGates = await getGatesUltra(bytecodePath, honkRecursion, api); | ||
| debug(`number of gates: : ${numberOfGates}`); | ||
| // Create an 8-byte buffer and write the number into it. | ||
| // Writing number directly to stdout will result in a variable sized | ||
|
|
@@ -234,7 +269,7 @@ export async function contract(outputPath: string, vkPath: string) { | |
| } | ||
|
|
||
| export async function writeVk(bytecodePath: string, crsPath: string, outputPath: string) { | ||
| const { api, acirComposer } = await init(bytecodePath, crsPath); | ||
| const { api, acirComposer } = await initUltraPlonk(bytecodePath, crsPath); | ||
| try { | ||
| debug('initing proving key...'); | ||
| const bytecode = getBytecode(bytecodePath); | ||
|
|
@@ -256,7 +291,7 @@ export async function writeVk(bytecodePath: string, crsPath: string, outputPath: | |
| } | ||
|
|
||
| export async function writePk(bytecodePath: string, crsPath: string, outputPath: string) { | ||
| const { api, acirComposer } = await init(bytecodePath, crsPath); | ||
| const { api, acirComposer } = await initUltraPlonk(bytecodePath, crsPath); | ||
| try { | ||
| debug('initing proving key...'); | ||
| const bytecode = getBytecode(bytecodePath); | ||
|
|
@@ -326,7 +361,7 @@ export async function vkAsFields(vkPath: string, vkeyOutputPath: string) { | |
| } | ||
|
|
||
| export async function proveUltraHonk(bytecodePath: string, witnessPath: string, crsPath: string, outputPath: string) { | ||
| const { api } = await init(bytecodePath, crsPath, -1, /* honkRecursion= */ true); | ||
| const { api } = await initUltraHonk(bytecodePath, crsPath); | ||
| try { | ||
| debug(`creating proof...`); | ||
| const bytecode = getBytecode(bytecodePath); | ||
|
|
@@ -347,7 +382,7 @@ export async function proveUltraHonk(bytecodePath: string, witnessPath: string, | |
| } | ||
|
|
||
| export async function writeVkUltraHonk(bytecodePath: string, crsPath: string, outputPath: string) { | ||
| const { api } = await init(bytecodePath, crsPath, -1, true); | ||
| const { api } = await initUltraHonk(bytecodePath, crsPath); | ||
| try { | ||
| const bytecode = getBytecode(bytecodePath); | ||
| debug('initing verification key...'); | ||
|
|
@@ -487,12 +522,12 @@ program | |
|
|
||
| program | ||
| .command('gates') | ||
| .description('Print gate count to standard output.') | ||
| .description('Print Ultra Builder gate count to standard output.') | ||
| .option('-b, --bytecode-path <path>', 'Specify the bytecode path', './target/program.json') | ||
| .option('-hr, --honk-recursion', 'Specify whether to use UltraHonk recursion', false) | ||
| .action(async ({ bytecodePath: bytecodePath, honkRecursion: honkRecursion }) => { | ||
| handleGlobalOptions(); | ||
| await gateCount(bytecodePath, honkRecursion); | ||
| await gateCountUltra(bytecodePath, honkRecursion); | ||
| }); | ||
|
|
||
| program | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| [package] | ||
| name = "bench_2_to_17" | ||
| type = "bin" | ||
| authors = [""] | ||
| compiler_version = ">=0.33.0" | ||
|
|
||
| [dependencies] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| x = "3" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| use std::hash::poseidon2; | ||
|
|
||
| global len = 2450 * 2; | ||
| fn main(x: Field) { | ||
| let ped_input = [x; len]; | ||
| let mut val = poseidon2::Poseidon2::hash(ped_input, len); | ||
| assert(val != 0); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| [package] | ||
| name = "fold_2_to_17" | ||
| type = "bin" | ||
| authors = [""] | ||
| compiler_version = ">=0.25.0" | ||
|
|
||
| [dependencies] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| x = "2" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| use std::hash::poseidon2; | ||
|
|
||
| global len = 2450 * 2 - 240; // for just under 2^17 gates | ||
| fn main(x: Field) { | ||
| let ped_input = [x; len]; | ||
| let mut val = poseidon2::Poseidon2::hash(ped_input, len); | ||
| let z = foo(x); | ||
| assert(val == z); | ||
| } | ||
|
|
||
| #[fold] | ||
| fn foo(x: Field) -> Field { | ||
| let ped_input = [x; len]; | ||
| let mut val = poseidon2::Poseidon2::hash(ped_input, len); | ||
| val | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| [package] | ||
| name = "single_verify_proof" | ||
| type = "bin" | ||
| authors = [""] | ||
| compiler_version = ">=0.24.0" | ||
|
|
||
| [dependencies] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we'll see if this works in CI. I un-disabled it and it worked on mainframe.