diff --git a/barretenberg/cpp/src/barretenberg/benchmark/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/CMakeLists.txt index 285f2bb5937..ae6fa95da27 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/CMakeLists.txt +++ b/barretenberg/cpp/src/barretenberg/benchmark/CMakeLists.txt @@ -1,10 +1,11 @@ +add_subdirectory(basics_bench) add_subdirectory(decrypt_bench) +add_subdirectory(goblin_bench) add_subdirectory(ipa_bench) +add_subdirectory(ivc_bench) add_subdirectory(pippenger_bench) add_subdirectory(plonk_bench) -add_subdirectory(ultra_bench) -add_subdirectory(goblin_bench) -add_subdirectory(basics_bench) +add_subdirectory(protogalaxy_bench) add_subdirectory(relations_bench) -add_subdirectory(widgets_bench) -add_subdirectory(protogalaxy_bench) \ No newline at end of file +add_subdirectory(ultra_bench) +add_subdirectory(widgets_bench) \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp index 44e68a5065c..8667c739dec 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/goblin.bench.cpp @@ -49,7 +49,7 @@ class GoblinBench : public benchmark::Fixture { // Construct and accumulate the mock kernel circuit // Note: in first round, kernel_accum is empty since there is no previous kernel to recursively verify GoblinUltraCircuitBuilder circuit_builder{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_kernel_circuit(circuit_builder, function_accum, kernel_accum); + GoblinMockCircuits::construct_mock_recursion_kernel_circuit(circuit_builder, function_accum, kernel_accum); kernel_accum = goblin.accumulate(circuit_builder); } } diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ivc_bench/CMakeLists.txt b/barretenberg/cpp/src/barretenberg/benchmark/ivc_bench/CMakeLists.txt new file mode 100644 index 00000000000..3f0c9245850 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/benchmark/ivc_bench/CMakeLists.txt @@ -0,0 +1 @@ +barretenberg_module(ivc_bench client_ivc stdlib_recursion stdlib_sha256 stdlib_merkle_tree stdlib_primitives) diff --git a/barretenberg/cpp/src/barretenberg/benchmark/ivc_bench/ivc.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/ivc_bench/ivc.bench.cpp new file mode 100644 index 00000000000..95eeb7d5b94 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/benchmark/ivc_bench/ivc.bench.cpp @@ -0,0 +1,178 @@ + +#include + +#include "barretenberg/benchmark/ultra_bench/mock_proofs.hpp" +#include "barretenberg/client_ivc/client_ivc.hpp" +#include "barretenberg/common/op_count_google_bench.hpp" +#include "barretenberg/goblin/goblin.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/proof_system/circuit_builder/ultra_circuit_builder.hpp" +#include "barretenberg/ultra_honk/ultra_composer.hpp" + +using namespace benchmark; +using namespace bb; + +namespace { + +/** + * @brief Benchmark suite for the aztec client PG-Goblin IVC scheme + * + */ +class IvcBench : public benchmark::Fixture { + public: + using Builder = GoblinUltraCircuitBuilder; + + // Number of function circuits to accumulate(based on Zacs target numbers) + static constexpr size_t NUM_ITERATIONS_MEDIUM_COMPLEXITY = 6; + + void SetUp([[maybe_unused]] const ::benchmark::State& state) override + { + bb::srs::init_crs_factory("../srs_db/ignition"); + bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); + } + + /** + * @brief Perform a specified number of function circuit accumulation rounds + * @details Each round "accumulates" a mock function circuit and a mock kernel circuit. Each round thus consists of + * the generation of two circuits, two folding proofs and two Merge proofs. To match the sizes called out in the + * spec + * (https://github.com/AztecProtocol/aztec-packages/blob/master/yellow-paper/docs/cryptography/performance-targets.md) + * 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 + * folding circuits of unequal size. + * + */ + static void perform_ivc_accumulation_rounds(State& state, ClientIVC& ivc) + { + // Initialize IVC with function circuit + Builder function_circuit{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(function_circuit); + ivc.initialize(function_circuit); + + // Accumulate kernel circuit (first kernel mocked as simple circuit since no folding proofs yet) + Builder kernel_circuit{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(kernel_circuit); + auto kernel_fold_proof = ivc.accumulate(kernel_circuit); + + auto NUM_CIRCUITS = static_cast(state.range(0)); + NUM_CIRCUITS -= 1; // Subtract one to account for the "initialization" round above + for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { + + // Accumulate function circuit + Builder function_circuit{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(function_circuit); + auto function_fold_proof = ivc.accumulate(function_circuit); + + // Accumulate kernel circuit + Builder kernel_circuit{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_folding_kernel(kernel_circuit, function_fold_proof, kernel_fold_proof); + auto kernel_fold_proof = ivc.accumulate(kernel_circuit); + } + } +}; + +/** + * @brief Benchmark the prover work for the full PG-Goblin IVC protocol + * + */ +BENCHMARK_DEFINE_F(IvcBench, Full)(benchmark::State& state) +{ + ClientIVC ivc; + + for (auto _ : state) { + BB_REPORT_OP_COUNT_IN_BENCH(state); + // Perform a specified number of iterations of function/kernel accumulation + perform_ivc_accumulation_rounds(state, ivc); + + // Construct IVC scheme proof (fold, decider, merge, eccvm, translator) + ivc.prove(); + } +} + +/** + * @brief Benchmark only the accumulation rounds + * + */ +BENCHMARK_DEFINE_F(IvcBench, Accumulate)(benchmark::State& state) +{ + ClientIVC ivc; + + // Perform a specified number of iterations of function/kernel accumulation + for (auto _ : state) { + perform_ivc_accumulation_rounds(state, ivc); + } +} + +/** + * @brief Benchmark only the Decider component + * + */ +BENCHMARK_DEFINE_F(IvcBench, Decide)(benchmark::State& state) +{ + ClientIVC ivc; + + BB_REPORT_OP_COUNT_IN_BENCH(state); + // Perform a specified number of iterations of function/kernel accumulation + perform_ivc_accumulation_rounds(state, ivc); + + // Construct eccvm proof, measure only translator proof construction + for (auto _ : state) { + ivc.decider_prove(); + } +} + +/** + * @brief Benchmark only the ECCVM component + * + */ +BENCHMARK_DEFINE_F(IvcBench, ECCVM)(benchmark::State& state) +{ + ClientIVC ivc; + + BB_REPORT_OP_COUNT_IN_BENCH(state); + // Perform a specified number of iterations of function/kernel accumulation + perform_ivc_accumulation_rounds(state, ivc); + + // Construct and measure eccvm only + for (auto _ : state) { + ivc.goblin.prove_eccvm(); + } +} + +/** + * @brief Benchmark only the Translator component + * + */ +BENCHMARK_DEFINE_F(IvcBench, Translator)(benchmark::State& state) +{ + ClientIVC ivc; + + BB_REPORT_OP_COUNT_IN_BENCH(state); + // Perform a specified number of iterations of function/kernel accumulation + perform_ivc_accumulation_rounds(state, ivc); + + // Construct eccvm proof, measure only translator proof construction + ivc.goblin.prove_eccvm(); + for (auto _ : state) { + ivc.goblin.prove_translator(); + } +} + +#define ARGS \ + Arg(IvcBench::NUM_ITERATIONS_MEDIUM_COMPLEXITY) \ + ->Arg(1 << 0) \ + ->Arg(1 << 1) \ + ->Arg(1 << 2) \ + ->Arg(1 << 3) \ + ->Arg(1 << 4) \ + ->Arg(1 << 5) \ + ->Arg(1 << 6) + +BENCHMARK_REGISTER_F(IvcBench, Full)->Unit(benchmark::kMillisecond)->ARGS; +BENCHMARK_REGISTER_F(IvcBench, Accumulate)->Unit(benchmark::kMillisecond)->ARGS; +BENCHMARK_REGISTER_F(IvcBench, Decide)->Unit(benchmark::kMillisecond)->ARGS; +BENCHMARK_REGISTER_F(IvcBench, ECCVM)->Unit(benchmark::kMillisecond)->ARGS; +BENCHMARK_REGISTER_F(IvcBench, Translator)->Unit(benchmark::kMillisecond)->ARGS; + +} // namespace + +BENCHMARK_MAIN(); diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp index 3affe72d550..92db4e2aaca 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp @@ -46,14 +46,7 @@ ClientIVC::FoldProof ClientIVC::accumulate(ClientCircuit& circuit) */ ClientIVC::Proof ClientIVC::prove() { - // Construct Goblin proof (merge, eccvm, translator) - auto goblin_proof = goblin.prove(); - - // Construct decider proof for the final accumulator - Composer composer; - auto decider_prover = composer.create_decider_prover(fold_output.accumulator); - auto decider_proof = decider_prover.construct_proof(); - return { goblin_proof, fold_output.folding_data, decider_proof }; + return { fold_output.folding_data, decider_prove(), goblin.prove() }; } /** @@ -76,4 +69,17 @@ bool ClientIVC::verify(Proof& proof) bool decision = decider_verifier.verify_proof(proof.decider_proof); return goblin_verified && folding_verified && decision; } + +/** + * @brief Internal method for constructing a decider proof + * + * @return HonkProof + */ +HonkProof ClientIVC::decider_prove() const +{ + Composer composer; + auto decider_prover = composer.create_decider_prover(fold_output.accumulator); + return decider_prover.construct_proof(); +} + } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp index 6f065bd452f..cd3b82b4d6d 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp @@ -23,9 +23,9 @@ class ClientIVC { // A full proof for the IVC scheme struct Proof { - Goblin::Proof goblin_proof; FoldProof fold_proof; // final fold proof HonkProof decider_proof; + Goblin::Proof goblin_proof; }; private: @@ -46,5 +46,7 @@ class ClientIVC { Proof prove(); bool verify(Proof& proof); + + HonkProof decider_prove() const; }; } // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/mock_kernel_pinning.test.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/mock_kernel_pinning.test.cpp new file mode 100644 index 00000000000..6ff29a1293e --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/client_ivc/mock_kernel_pinning.test.cpp @@ -0,0 +1,42 @@ +#include "barretenberg/client_ivc/client_ivc.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/proof_system/circuit_builder/goblin_ultra_circuit_builder.hpp" +#include "barretenberg/ultra_honk/ultra_composer.hpp" +#include + +using namespace bb; + +/** + * @brief For benchmarking, we want to be sure that our mocking functions create circuits of a known size. We control + * this, to the degree that matters for proof construction time, using these "pinning tests" that fix values. + * + */ +class MockKernelTest : public ::testing::Test { + protected: + static void SetUpTestSuite() { srs::init_crs_factory("../srs_db/ignition"); } +}; + +TEST_F(MockKernelTest, PinFoldingKernelSizes) +{ + ClientIVC ivc; + + // Accumulate three circuits to generate two folding proofs for input to folding kernel + GoblinUltraCircuitBuilder circuit_1{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(circuit_1); + ivc.initialize(circuit_1); + + GoblinUltraCircuitBuilder circuit_2{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(circuit_2); + auto fold_proof_1 = ivc.accumulate(circuit_2); + + GoblinUltraCircuitBuilder circuit_3{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(circuit_3); + auto fold_proof_2 = ivc.accumulate(circuit_3); + + // Construct kernel circuit + GoblinUltraCircuitBuilder kernel_circuit{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_folding_kernel(kernel_circuit, fold_proof_1, fold_proof_2); + GoblinUltraComposer composer; + auto instance = composer.create_instance(kernel_circuit); + EXPECT_EQ(instance->proving_key->log_circuit_size, 17); +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp index d2a75f243f7..12fde5fb31b 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits.hpp @@ -13,6 +13,7 @@ #include "barretenberg/stdlib/merkle_tree/merkle_tree.hpp" #include "barretenberg/stdlib/primitives/curves/secp256k1.hpp" #include "barretenberg/stdlib/primitives/packed_byte_array/packed_byte_array.hpp" +#include "barretenberg/stdlib/recursion/honk/verifier/protogalaxy_recursive_verifier.hpp" #include "barretenberg/stdlib/recursion/honk/verifier/ultra_recursive_verifier.hpp" namespace bb { @@ -164,7 +165,7 @@ class GoblinMockCircuits { } /** - * @brief Construct a size 2^17 mock kernel circuit for benchmarking + * @brief Construct a size 2^17 mock kernel circuit based on vanilla recursion for benchmarking * @details This circuit contains (1) some arbitrary operations representing general kernel logic, (2) recursive * verification of a function circuit proof, and optionally (3) recursive verification of a previous kernel circuit * 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 { * @param function_accum {proof, vkey} for function circuit to be recursively verified * @param prev_kernel_accum {proof, vkey} for previous kernel circuit to be recursively verified */ - static void construct_mock_kernel_circuit(GoblinUltraBuilder& builder, - const KernelInput& function_accum, - const KernelInput& prev_kernel_accum) + static void construct_mock_recursion_kernel_circuit(GoblinUltraBuilder& builder, + const KernelInput& function_accum, + const KernelInput& prev_kernel_accum) { // Add operations representing general kernel logic e.g. state updates. Note: these are structured to make the // kernel "full" within the dyadic size 2^17 (130914 gates) @@ -198,6 +199,42 @@ class GoblinMockCircuits { } } + /** + * @brief Construct a mock kernel circuit based on folding + * @details This circuit contains (1) some arbitrary operations representing general kernel logic, (2) recursive + * folding verification of a function circuit folding proof, and (3) recursive folding verification of a previous + * kernel circuit folding proof. The arbitrary kernel logic is structured to bring the final dyadic circuit size of + * the kernel to 2^17. + * + * @param builder + * @param function_fold_proof + * @param kernel_fold_proof + */ + static void construct_mock_folding_kernel(GoblinUltraBuilder& builder, + const std::vector& function_fold_proof, + const std::vector& kernel_fold_proof) + { + using GURecursiveFlavor = GoblinUltraRecursiveFlavor_; + using RecursiveVerifierInstances = ::bb::VerifierInstances_; + using FoldingRecursiveVerifier = + bb::stdlib::recursion::honk::ProtoGalaxyRecursiveVerifier_; + + // Add operations representing general kernel logic e.g. state updates. Note: these are structured to make the + // kernel "full" within the dyadic size 2^17 (130914 gates) + const size_t NUM_MERKLE_CHECKS = 20; + const size_t NUM_ECDSA_VERIFICATIONS = 1; + const size_t NUM_SHA_HASHES = 1; + stdlib::generate_merkle_membership_test_circuit(builder, NUM_MERKLE_CHECKS); + stdlib::generate_ecdsa_verification_test_circuit(builder, NUM_ECDSA_VERIFICATIONS); + stdlib::generate_sha256_test_circuit(builder, NUM_SHA_HASHES); + + FoldingRecursiveVerifier verifier_1{ &builder }; + verifier_1.verify_folding_proof(function_fold_proof); + + FoldingRecursiveVerifier verifier_2{ &builder }; + verifier_2.verify_folding_proof(kernel_fold_proof); + } + /** * @brief A minimal version of the mock kernel (recursive verifiers only) for faster testing * diff --git a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp index 6d642bf5bc8..2f52c964eab 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp +++ b/barretenberg/cpp/src/barretenberg/goblin/mock_circuits_pinning.test.cpp @@ -34,7 +34,7 @@ TEST_F(MockCircuits, PinFunctionSizes) run_test(false); } -TEST_F(MockCircuits, PinKernelSizes) +TEST_F(MockCircuits, PinRecursionKernelSizes) { const auto run_test = [](bool large) { { @@ -45,7 +45,7 @@ TEST_F(MockCircuits, PinKernelSizes) GoblinMockCircuits::construct_mock_function_circuit(app_circuit, large); auto function_accum = goblin.accumulate(app_circuit); GoblinUltraCircuitBuilder kernel_circuit{ goblin.op_queue }; - GoblinMockCircuits::construct_mock_kernel_circuit(kernel_circuit, function_accum, kernel_accum); + GoblinMockCircuits::construct_mock_recursion_kernel_circuit(kernel_circuit, function_accum, kernel_accum); GoblinUltraComposer composer; auto instance = composer.create_instance(kernel_circuit); if (large) {