Skip to content

Commit fd7254e

Browse files
authored
feat: IVC bench (AztecProtocol#4515)
Adds benchmark suite for the PG-Goblin client IVC scheme. The only real caveat is that we only consider function circuits of size 2^17 since our PG code doesn't support folding unequal sizes. Results at present: ``` ----------------------------------------------------------------- Benchmark Time CPU Iterations ----------------------------------------------------------------- IvcBench/Full/6 66891 ms 63569 ms 1 ``` ---------
1 parent 3d54638 commit fd7254e

File tree

9 files changed

+288
-21
lines changed

9 files changed

+288
-21
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
add_subdirectory(basics_bench)
12
add_subdirectory(decrypt_bench)
3+
add_subdirectory(goblin_bench)
24
add_subdirectory(ipa_bench)
5+
add_subdirectory(ivc_bench)
36
add_subdirectory(pippenger_bench)
47
add_subdirectory(plonk_bench)
5-
add_subdirectory(ultra_bench)
6-
add_subdirectory(goblin_bench)
7-
add_subdirectory(basics_bench)
8+
add_subdirectory(protogalaxy_bench)
89
add_subdirectory(relations_bench)
9-
add_subdirectory(widgets_bench)
10-
add_subdirectory(protogalaxy_bench)
10+
add_subdirectory(ultra_bench)
11+
add_subdirectory(widgets_bench)

barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class GoblinBench : public benchmark::Fixture {
4949
// Construct and accumulate the mock kernel circuit
5050
// Note: in first round, kernel_accum is empty since there is no previous kernel to recursively verify
5151
GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue };
52-
GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, function_accum, kernel_accum);
52+
GoblinMockCircuits::construct_mock_recursion_kernel_circuit(circuit_builder, function_accum, kernel_accum);
5353
kernel_accum = goblin.accumulate(circuit_builder);
5454
}
5555
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
barretenberg_module(ivc_bench client_ivc stdlib_recursion stdlib_sha256 stdlib_merkle_tree stdlib_primitives)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
2+
#include <benchmark/benchmark.h>
3+
4+
#include "barretenberg/benchmark/ultra_bench/mock_proofs.hpp"
5+
#include "barretenberg/client_ivc/client_ivc.hpp"
6+
#include "barretenberg/common/op_count_google_bench.hpp"
7+
#include "barretenberg/goblin/goblin.hpp"
8+
#include "barretenberg/goblin/mock_circuits.hpp"
9+
#include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp"
10+
#include "barretenberg/ultra_honk/ultra_composer.hpp"
11+
12+
using namespace benchmark;
13+
using namespace bb;
14+
15+
namespace {
16+
17+
/**
18+
* @brief Benchmark suite for the aztec client PG-Goblin IVC scheme
19+
*
20+
*/
21+
class IvcBench : public benchmark::Fixture {
22+
public:
23+
using Builder = GoblinUltraCircuitBuilder;
24+
25+
// Number of function circuits to accumulate(based on Zacs target numbers)
26+
static constexpr size_t NUM_ITERATIONS_MEDIUM_COMPLEXITY = 6;
27+
28+
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
29+
{
30+
bb::srs::init_crs_factory("../srs_db/ignition");
31+
bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin");
32+
}
33+
34+
/**
35+
* @brief Perform a specified number of function circuit accumulation rounds
36+
* @details Each round "accumulates" a mock function circuit and a mock kernel circuit. Each round thus consists of
37+
* the generation of two circuits, two folding proofs and two Merge proofs. To match the sizes called out in the
38+
* spec
39+
* (https://github.com/AztecProtocol/aztec-packages/blob/master/yellow-paper/docs/cryptography/performance-targets.md)
40+
* we set the size of the function circuit to be 2^17. The first one should be 2^19 but we can't currently support
41+
* folding circuits of unequal size.
42+
*
43+
*/
44+
static void perform_ivc_accumulation_rounds(State& state, ClientIVC& ivc)
45+
{
46+
// Initialize IVC with function circuit
47+
Builder function_circuit{ ivc.goblin.op_queue };
48+
GoblinMockCircuits::construct_mock_function_circuit(function_circuit);
49+
ivc.initialize(function_circuit);
50+
51+
// Accumulate kernel circuit (first kernel mocked as simple circuit since no folding proofs yet)
52+
Builder kernel_circuit{ ivc.goblin.op_queue };
53+
GoblinMockCircuits::construct_mock_function_circuit(kernel_circuit);
54+
auto kernel_fold_proof = ivc.accumulate(kernel_circuit);
55+
56+
auto NUM_CIRCUITS = static_cast<size_t>(state.range(0));
57+
NUM_CIRCUITS -= 1; // Subtract one to account for the "initialization" round above
58+
for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) {
59+
60+
// Accumulate function circuit
61+
Builder function_circuit{ ivc.goblin.op_queue };
62+
GoblinMockCircuits::construct_mock_function_circuit(function_circuit);
63+
auto function_fold_proof = ivc.accumulate(function_circuit);
64+
65+
// Accumulate kernel circuit
66+
Builder kernel_circuit{ ivc.goblin.op_queue };
67+
GoblinMockCircuits::construct_mock_folding_kernel(kernel_circuit, function_fold_proof, kernel_fold_proof);
68+
auto kernel_fold_proof = ivc.accumulate(kernel_circuit);
69+
}
70+
}
71+
};
72+
73+
/**
74+
* @brief Benchmark the prover work for the full PG-Goblin IVC protocol
75+
*
76+
*/
77+
BENCHMARK_DEFINE_F(IvcBench, Full)(benchmark::State& state)
78+
{
79+
ClientIVC ivc;
80+
81+
for (auto _ : state) {
82+
BB_REPORT_OP_COUNT_IN_BENCH(state);
83+
// Perform a specified number of iterations of function/kernel accumulation
84+
perform_ivc_accumulation_rounds(state, ivc);
85+
86+
// Construct IVC scheme proof (fold, decider, merge, eccvm, translator)
87+
ivc.prove();
88+
}
89+
}
90+
91+
/**
92+
* @brief Benchmark only the accumulation rounds
93+
*
94+
*/
95+
BENCHMARK_DEFINE_F(IvcBench, Accumulate)(benchmark::State& state)
96+
{
97+
ClientIVC ivc;
98+
99+
// Perform a specified number of iterations of function/kernel accumulation
100+
for (auto _ : state) {
101+
perform_ivc_accumulation_rounds(state, ivc);
102+
}
103+
}
104+
105+
/**
106+
* @brief Benchmark only the Decider component
107+
*
108+
*/
109+
BENCHMARK_DEFINE_F(IvcBench, Decide)(benchmark::State& state)
110+
{
111+
ClientIVC ivc;
112+
113+
BB_REPORT_OP_COUNT_IN_BENCH(state);
114+
// Perform a specified number of iterations of function/kernel accumulation
115+
perform_ivc_accumulation_rounds(state, ivc);
116+
117+
// Construct eccvm proof, measure only translator proof construction
118+
for (auto _ : state) {
119+
ivc.decider_prove();
120+
}
121+
}
122+
123+
/**
124+
* @brief Benchmark only the ECCVM component
125+
*
126+
*/
127+
BENCHMARK_DEFINE_F(IvcBench, ECCVM)(benchmark::State& state)
128+
{
129+
ClientIVC ivc;
130+
131+
BB_REPORT_OP_COUNT_IN_BENCH(state);
132+
// Perform a specified number of iterations of function/kernel accumulation
133+
perform_ivc_accumulation_rounds(state, ivc);
134+
135+
// Construct and measure eccvm only
136+
for (auto _ : state) {
137+
ivc.goblin.prove_eccvm();
138+
}
139+
}
140+
141+
/**
142+
* @brief Benchmark only the Translator component
143+
*
144+
*/
145+
BENCHMARK_DEFINE_F(IvcBench, Translator)(benchmark::State& state)
146+
{
147+
ClientIVC ivc;
148+
149+
BB_REPORT_OP_COUNT_IN_BENCH(state);
150+
// Perform a specified number of iterations of function/kernel accumulation
151+
perform_ivc_accumulation_rounds(state, ivc);
152+
153+
// Construct eccvm proof, measure only translator proof construction
154+
ivc.goblin.prove_eccvm();
155+
for (auto _ : state) {
156+
ivc.goblin.prove_translator();
157+
}
158+
}
159+
160+
#define ARGS \
161+
Arg(IvcBench::NUM_ITERATIONS_MEDIUM_COMPLEXITY) \
162+
->Arg(1 << 0) \
163+
->Arg(1 << 1) \
164+
->Arg(1 << 2) \
165+
->Arg(1 << 3) \
166+
->Arg(1 << 4) \
167+
->Arg(1 << 5) \
168+
->Arg(1 << 6)
169+
170+
BENCHMARK_REGISTER_F(IvcBench, Full)->Unit(benchmark::kMillisecond)->ARGS;
171+
BENCHMARK_REGISTER_F(IvcBench, Accumulate)->Unit(benchmark::kMillisecond)->ARGS;
172+
BENCHMARK_REGISTER_F(IvcBench, Decide)->Unit(benchmark::kMillisecond)->ARGS;
173+
BENCHMARK_REGISTER_F(IvcBench, ECCVM)->Unit(benchmark::kMillisecond)->ARGS;
174+
BENCHMARK_REGISTER_F(IvcBench, Translator)->Unit(benchmark::kMillisecond)->ARGS;
175+
176+
} // namespace
177+
178+
BENCHMARK_MAIN();

barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp

+14-8
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,7 @@ ClientIVC::FoldProof ClientIVC::accumulate(ClientCircuit& circuit)
4646
*/
4747
ClientIVC::Proof ClientIVC::prove()
4848
{
49-
// Construct Goblin proof (merge, eccvm, translator)
50-
auto goblin_proof = goblin.prove();
51-
52-
// Construct decider proof for the final accumulator
53-
Composer composer;
54-
auto decider_prover = composer.create_decider_prover(fold_output.accumulator);
55-
auto decider_proof = decider_prover.construct_proof();
56-
return { goblin_proof, fold_output.folding_data, decider_proof };
49+
return { fold_output.folding_data, decider_prove(), goblin.prove() };
5750
}
5851

5952
/**
@@ -76,4 +69,17 @@ bool ClientIVC::verify(Proof& proof)
7669
bool decision = decider_verifier.verify_proof(proof.decider_proof);
7770
return goblin_verified && folding_verified && decision;
7871
}
72+
73+
/**
74+
* @brief Internal method for constructing a decider proof
75+
*
76+
* @return HonkProof
77+
*/
78+
HonkProof ClientIVC::decider_prove() const
79+
{
80+
Composer composer;
81+
auto decider_prover = composer.create_decider_prover(fold_output.accumulator);
82+
return decider_prover.construct_proof();
83+
}
84+
7985
} // namespace bb

barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ class ClientIVC {
2323

2424
// A full proof for the IVC scheme
2525
struct Proof {
26-
Goblin::Proof goblin_proof;
2726
FoldProof fold_proof; // final fold proof
2827
HonkProof decider_proof;
28+
Goblin::Proof goblin_proof;
2929
};
3030

3131
private:
@@ -46,5 +46,7 @@ class ClientIVC {
4646
Proof prove();
4747

4848
bool verify(Proof& proof);
49+
50+
HonkProof decider_prove() const;
4951
};
5052
} // namespace bb
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include "barretenberg/client_ivc/client_ivc.hpp"
2+
#include "barretenberg/goblin/mock_circuits.hpp"
3+
#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp"
4+
#include "barretenberg/ultra_honk/ultra_composer.hpp"
5+
#include <gtest/gtest.h>
6+
7+
using namespace bb;
8+
9+
/**
10+
* @brief For benchmarking, we want to be sure that our mocking functions create circuits of a known size. We control
11+
* this, to the degree that matters for proof construction time, using these "pinning tests" that fix values.
12+
*
13+
*/
14+
class MockKernelTest : public ::testing::Test {
15+
protected:
16+
static void SetUpTestSuite() { srs::init_crs_factory("../srs_db/ignition"); }
17+
};
18+
19+
TEST_F(MockKernelTest, PinFoldingKernelSizes)
20+
{
21+
ClientIVC ivc;
22+
23+
// Accumulate three circuits to generate two folding proofs for input to folding kernel
24+
GoblinUltraCircuitBuilder circuit_1{ ivc.goblin.op_queue };
25+
GoblinMockCircuits::construct_mock_function_circuit(circuit_1);
26+
ivc.initialize(circuit_1);
27+
28+
GoblinUltraCircuitBuilder circuit_2{ ivc.goblin.op_queue };
29+
GoblinMockCircuits::construct_mock_function_circuit(circuit_2);
30+
auto fold_proof_1 = ivc.accumulate(circuit_2);
31+
32+
GoblinUltraCircuitBuilder circuit_3{ ivc.goblin.op_queue };
33+
GoblinMockCircuits::construct_mock_function_circuit(circuit_3);
34+
auto fold_proof_2 = ivc.accumulate(circuit_3);
35+
36+
// Construct kernel circuit
37+
GoblinUltraCircuitBuilder kernel_circuit{ ivc.goblin.op_queue };
38+
GoblinMockCircuits::construct_mock_folding_kernel(kernel_circuit, fold_proof_1, fold_proof_2);
39+
GoblinUltraComposer composer;
40+
auto instance = composer.create_instance(kernel_circuit);
41+
EXPECT_EQ(instance->proving_key->log_circuit_size, 17);
42+
}

barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp

+41-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "barretenberg/stdlib/merkle_tree/merkle_tree.hpp"
1414
#include "barretenberg/stdlib/primitives/curves/secp256k1.hpp"
1515
#include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp"
16+
#include "barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.hpp"
1617
#include "barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp"
1718

1819
namespace bb {
@@ -164,7 +165,7 @@ class GoblinMockCircuits {
164165
}
165166

166167
/**
167-
* @brief Construct a size 2^17 mock kernel circuit for benchmarking
168+
* @brief Construct a size 2^17 mock kernel circuit based on vanilla recursion for benchmarking
168169
* @details This circuit contains (1) some arbitrary operations representing general kernel logic, (2) recursive
169170
* verification of a function circuit proof, and optionally (3) recursive verification of a previous kernel circuit
170171
* proof. The arbitrary kernel logic is structured to bring the final dyadic circuit size of the kernel to 2^17.
@@ -174,9 +175,9 @@ class GoblinMockCircuits {
174175
* @param function_accum {proof, vkey} for function circuit to be recursively verified
175176
* @param prev_kernel_accum {proof, vkey} for previous kernel circuit to be recursively verified
176177
*/
177-
static void construct_mock_kernel_circuit(GoblinUltraBuilder& builder,
178-
const KernelInput& function_accum,
179-
const KernelInput& prev_kernel_accum)
178+
static void construct_mock_recursion_kernel_circuit(GoblinUltraBuilder& builder,
179+
const KernelInput& function_accum,
180+
const KernelInput& prev_kernel_accum)
180181
{
181182
// Add operations representing general kernel logic e.g. state updates. Note: these are structured to make the
182183
// kernel "full" within the dyadic size 2^17 (130914 gates)
@@ -198,6 +199,42 @@ class GoblinMockCircuits {
198199
}
199200
}
200201

202+
/**
203+
* @brief Construct a mock kernel circuit based on folding
204+
* @details This circuit contains (1) some arbitrary operations representing general kernel logic, (2) recursive
205+
* folding verification of a function circuit folding proof, and (3) recursive folding verification of a previous
206+
* kernel circuit folding proof. The arbitrary kernel logic is structured to bring the final dyadic circuit size of
207+
* the kernel to 2^17.
208+
*
209+
* @param builder
210+
* @param function_fold_proof
211+
* @param kernel_fold_proof
212+
*/
213+
static void construct_mock_folding_kernel(GoblinUltraBuilder& builder,
214+
const std::vector<FF>& function_fold_proof,
215+
const std::vector<FF>& kernel_fold_proof)
216+
{
217+
using GURecursiveFlavor = GoblinUltraRecursiveFlavor_<GoblinUltraBuilder>;
218+
using RecursiveVerifierInstances = ::bb::VerifierInstances_<GURecursiveFlavor, 2>;
219+
using FoldingRecursiveVerifier =
220+
bb::stdlib::recursion::honk::ProtoGalaxyRecursiveVerifier_<RecursiveVerifierInstances>;
221+
222+
// Add operations representing general kernel logic e.g. state updates. Note: these are structured to make the
223+
// kernel "full" within the dyadic size 2^17 (130914 gates)
224+
const size_t NUM_MERKLE_CHECKS = 20;
225+
const size_t NUM_ECDSA_VERIFICATIONS = 1;
226+
const size_t NUM_SHA_HASHES = 1;
227+
stdlib::generate_merkle_membership_test_circuit(builder, NUM_MERKLE_CHECKS);
228+
stdlib::generate_ecdsa_verification_test_circuit(builder, NUM_ECDSA_VERIFICATIONS);
229+
stdlib::generate_sha256_test_circuit(builder, NUM_SHA_HASHES);
230+
231+
FoldingRecursiveVerifier verifier_1{ &builder };
232+
verifier_1.verify_folding_proof(function_fold_proof);
233+
234+
FoldingRecursiveVerifier verifier_2{ &builder };
235+
verifier_2.verify_folding_proof(kernel_fold_proof);
236+
}
237+
201238
/**
202239
* @brief A minimal version of the mock kernel (recursive verifiers only) for faster testing
203240
*

barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ TEST_F(MockCircuits, PinFunctionSizes)
3434
run_test(false);
3535
}
3636

37-
TEST_F(MockCircuits, PinKernelSizes)
37+
TEST_F(MockCircuits, PinRecursionKernelSizes)
3838
{
3939
const auto run_test = [](bool large) {
4040
{
@@ -45,7 +45,7 @@ TEST_F(MockCircuits, PinKernelSizes)
4545
GoblinMockCircuits::construct_mock_function_circuit(app_circuit, large);
4646
auto function_accum = goblin.accumulate(app_circuit);
4747
GoblinUltraCircuitBuilder kernel_circuit{ goblin.op_queue };
48-
GoblinMockCircuits::construct_mock_kernel_circuit(kernel_circuit, function_accum, kernel_accum);
48+
GoblinMockCircuits::construct_mock_recursion_kernel_circuit(kernel_circuit, function_accum, kernel_accum);
4949
GoblinUltraComposer composer;
5050
auto instance = composer.create_instance(kernel_circuit);
5151
if (large) {

0 commit comments

Comments
 (0)