From fad3d6e41765c774696ecc98d45a27851c7c4442 Mon Sep 17 00:00:00 2001 From: Cody Gunton Date: Sun, 29 Sep 2024 23:44:01 -0400 Subject: [PATCH] feat: Faster CIV benching with mocked VKs (#8843) Rather than going through a whole separate (and more expensive) CIVC prover flow to get vks, we just use random group elements. In order to get assurance that the benchmark is still a good reflection of performance, we refactor the functions used in the benchmark to create an equivalent test. --- .../client_ivc_bench/client_ivc.bench.cpp | 74 +++---------------- .../barretenberg/client_ivc/client_ivc.cpp | 5 +- .../barretenberg/client_ivc/client_ivc.hpp | 13 +++- .../client_ivc/client_ivc.test.cpp | 45 +++++++++++ .../client_ivc/test_bench_shared.hpp | 71 ++++++++++++++++++ .../stdlib_circuit_builders/mega_flavor.hpp | 6 +- 6 files changed, 145 insertions(+), 69 deletions(-) create mode 100644 barretenberg/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp diff --git a/barretenberg/cpp/src/barretenberg/benchmark/client_ivc_bench/client_ivc.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/client_ivc_bench/client_ivc.bench.cpp index e891e9e23ce..3410f03faf6 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/client_ivc_bench/client_ivc.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/client_ivc_bench/client_ivc.bench.cpp @@ -1,13 +1,12 @@ +/** + * @warning These benchmarks use functions that are tested elsewhere to guard against regressions in the benchmark. + * Please do not anything that is untested. + */ #include -#include "barretenberg/client_ivc/client_ivc.hpp" -#include "barretenberg/client_ivc/mock_circuit_producer.hpp" -#include "barretenberg/common/op_count.hpp" +#include "barretenberg/client_ivc/test_bench_shared.hpp" #include "barretenberg/common/op_count_google_bench.hpp" -#include "barretenberg/goblin/mock_circuits.hpp" -#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" -#include "barretenberg/ultra_honk/ultra_verifier.hpp" using namespace benchmark; using namespace bb; @@ -16,16 +15,10 @@ namespace { /** * @brief Benchmark suite for the aztec client PG-Goblin IVC scheme - * */ class ClientIVCBench : public benchmark::Fixture { public: - using Builder = MegaCircuitBuilder; - using DeciderVerificationKey = DeciderVerificationKey_; - using Proof = ClientIVC::Proof; - using MockCircuitProducer = PrivateFunctionExecutionMockCircuitProducer; - - // Number of function circuits to accumulate(based on Zacs target numbers) + // Number of function circuits to accumulate (based on Zac's target numbers) static constexpr size_t NUM_ITERATIONS_MEDIUM_COMPLEXITY = 6; void SetUp([[maybe_unused]] const ::benchmark::State& state) override @@ -33,72 +26,23 @@ class ClientIVCBench : public benchmark::Fixture { bb::srs::init_crs_factory("../srs_db/ignition"); bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); } - - /** - * @brief Verify an IVC proof - * - */ - static bool verify_ivc(Proof& proof, ClientIVC& ivc) - { - auto verifier_inst = std::make_shared(ivc.verification_queue[0].honk_verification_key); - bool verified = ivc.verify(proof, { ivc.verifier_accumulator, verifier_inst }); - - // This is a benchmark, not a test, so just print success or failure to the log - if (verified) { - info("IVC successfully verified!"); - } else { - info("IVC failed to verify."); - } - return verified; - } - - /** - * @brief Perform a specified number of circuit accumulation rounds - * - * @param NUM_CIRCUITS Number of circuits to accumulate (apps + kernels) - */ - static void perform_ivc_accumulation_rounds(size_t NUM_CIRCUITS, ClientIVC& ivc, auto& precomputed_vks) - { - ASSERT(precomputed_vks.size() == NUM_CIRCUITS); // ensure presence of a precomputed VK for each circuit - - MockCircuitProducer circuit_producer; - - for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { - Builder circuit; - { - BB_OP_COUNT_TIME_NAME("construct_circuits"); - circuit = circuit_producer.create_next_circuit(ivc); - } - - ivc.accumulate(circuit, precomputed_vks[circuit_idx]); - } - } }; /** * @brief Benchmark the prover work for the full PG-Goblin IVC protocol - * */ BENCHMARK_DEFINE_F(ClientIVCBench, Full)(benchmark::State& state) { ClientIVC ivc; ivc.trace_structure = TraceStructure::CLIENT_IVC_BENCH; - auto total_num_circuits = 2 * static_cast(state.range(0)); // 2x accounts for kernel circuits + auto mocked_vkeys = mock_verification_keys(total_num_circuits); - // Precompute the verification keys for the benchmark circuits - MockCircuitProducer circuit_producer; - auto precomputed_vkeys = circuit_producer.precompute_verification_keys(total_num_circuits, ivc.trace_structure); - - Proof proof; for (auto _ : state) { BB_REPORT_OP_COUNT_IN_BENCH(state); - perform_ivc_accumulation_rounds(total_num_circuits, ivc, precomputed_vkeys); - proof = ivc.prove(); + perform_ivc_accumulation_rounds(total_num_circuits, ivc, mocked_vkeys, /* mock_vk */ true); + ivc.prove(); } - - // For good measure, ensure the IVC verifies - verify_ivc(proof, ivc); } #define ARGS Arg(ClientIVCBench::NUM_ITERATIONS_MEDIUM_COMPLEXITY) diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp index 857a70ddb5b..2309dfe689f 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp @@ -146,7 +146,7 @@ void ClientIVC::complete_kernel_circuit_logic(ClientCircuit& circuit) * @param circuit * @param precomputed_vk */ -void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr& precomputed_vk) +void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr& precomputed_vk, bool mock_vk) { is_kernel = !is_kernel; // toggle on each call (every even circuit is a kernel) @@ -173,6 +173,9 @@ void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr(proving_key->proving_key); + if (mock_vk) { + honk_vk->set_metadata(proving_key->proving_key); + } // If this is the first circuit in the IVC, use oink to complete the decider proving key and generate an oink proof if (!initialized) { diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp index 03f2aa4ab5c..8ccc371efad 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp @@ -130,8 +130,17 @@ class ClientIVC { // Complete the logic of a kernel circuit (e.g. PG/merge recursive verification, databus consistency checks) void complete_kernel_circuit_logic(ClientCircuit& circuit); - // Perform prover work for accumulation (e.g. PG folding, merge proving) - void accumulate(ClientCircuit& circuit, const std::shared_ptr& precomputed_vk = nullptr); + /** + * @brief Perform prover work for accumulation (e.g. PG folding, merge proving) + * + * @param circuit The incoming statement + * @param precomputed_vk The verification key of the incoming statement OR a mocked key whose metadata needs to be + * set using the proving key produced from `circuit` in order to pass some assertions in the Oink prover. + * @param mock_vk A boolean to say whether the precomputed vk shoudl have its metadata set. + */ + void accumulate(ClientCircuit& circuit, + const std::shared_ptr& precomputed_vk = nullptr, + bool mock_vk = false); Proof prove(); diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp index f687323c541..c7e85ed675f 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp @@ -1,4 +1,5 @@ #include "barretenberg/client_ivc/client_ivc.hpp" +#include "barretenberg/client_ivc/test_bench_shared.hpp" #include "barretenberg/goblin/goblin.hpp" #include "barretenberg/goblin/mock_circuits.hpp" #include "barretenberg/stdlib_circuit_builders/mega_circuit_builder.hpp" @@ -342,3 +343,47 @@ TEST_F(ClientIVCTests, StructuredPrecomputedVKs) EXPECT_TRUE(ivc.prove_and_verify()); }; + +/** + * @brief Run a test using functions shared with the ClientIVC benchmark. + * @details We do have this in addition to the above tests anyway so we can believe that the benchmark is running on + * real data EXCEPT the verification keys, whose correctness is not needed to assess the performance of the folding + * prover. Before this test was added, we spend more than 50% of the benchmarking time running an entire IVC prover + * protocol just to precompute valid verification keys. + */ +TEST(ClientIVCBenchValidation, Full6) +{ + bb::srs::init_crs_factory("../srs_db/ignition"); + bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); + + ClientIVC ivc; + ivc.trace_structure = TraceStructure::CLIENT_IVC_BENCH; + size_t total_num_circuits{ 12 }; + PrivateFunctionExecutionMockCircuitProducer circuit_producer; + auto precomputed_vkeys = circuit_producer.precompute_verification_keys(total_num_circuits, ivc.trace_structure); + perform_ivc_accumulation_rounds(total_num_circuits, ivc, precomputed_vkeys); + auto proof = ivc.prove(); + bool verified = verify_ivc(proof, ivc); + EXPECT_TRUE(verified); +} + +/** + * @brief Test that running the benchmark suite with movked verification keys will not error out. + */ +TEST(ClientIVCBenchValidation, Full6MockedVKs) +{ + const auto run_test = []() { + bb::srs::init_crs_factory("../srs_db/ignition"); + bb::srs::init_grumpkin_crs_factory("../srs_db/grumpkin"); + + ClientIVC ivc; + ivc.trace_structure = TraceStructure::CLIENT_IVC_BENCH; + size_t total_num_circuits{ 12 }; + PrivateFunctionExecutionMockCircuitProducer circuit_producer; + auto mocked_vkeys = mock_verification_keys(total_num_circuits); + perform_ivc_accumulation_rounds(total_num_circuits, ivc, mocked_vkeys, /* mock_vk */ true); + auto proof = ivc.prove(); + verify_ivc(proof, ivc); + }; + ASSERT_NO_FATAL_FAILURE(run_test()); +} \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp b/barretenberg/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp new file mode 100644 index 00000000000..d1529a2a9b2 --- /dev/null +++ b/barretenberg/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp @@ -0,0 +1,71 @@ + +#include "barretenberg/client_ivc/client_ivc.hpp" +#include "barretenberg/client_ivc/mock_circuit_producer.hpp" +#include "barretenberg/common/op_count.hpp" +#include "barretenberg/goblin/mock_circuits.hpp" +#include "barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp" +#include "barretenberg/ultra_honk/ultra_verifier.hpp" + +namespace bb { + +/** + * @brief Verify an IVC proof + * + */ +bool verify_ivc(ClientIVC::Proof& proof, ClientIVC& ivc) +{ + auto verifier_inst = + std::make_shared>(ivc.verification_queue[0].honk_verification_key); + bool verified = ivc.verify(proof, { ivc.verifier_accumulator, verifier_inst }); + + // This is a benchmark, not a test, so just print success or failure to the log + if (verified) { + info("IVC successfully verified!"); + } else { + info("IVC failed to verify."); + } + return verified; +} + +/** + * @brief Perform a specified number of circuit accumulation rounds + * + * @param NUM_CIRCUITS Number of circuits to accumulate (apps + kernels) + */ +void perform_ivc_accumulation_rounds(size_t NUM_CIRCUITS, + ClientIVC& ivc, + auto& precomputed_vks, + const bool& mock_vk = false) +{ + ASSERT(precomputed_vks.size() == NUM_CIRCUITS); // ensure presence of a precomputed VK for each circuit + + PrivateFunctionExecutionMockCircuitProducer circuit_producer; + + for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { + MegaCircuitBuilder circuit; + { + BB_OP_COUNT_TIME_NAME("construct_circuits"); + circuit = circuit_producer.create_next_circuit(ivc); + } + + ivc.accumulate(circuit, precomputed_vks[circuit_idx], mock_vk); + } +} + +std::vector> mock_verification_keys(const size_t num_circuits) +{ + + std::vector> vkeys; + + for (size_t idx = 0; idx < num_circuits; ++idx) { + auto key = std::make_shared(); + for (auto& commitment : key->get_all()) { + commitment = MegaFlavor::Commitment::random_element(); + } + vkeys.push_back(key); + } + + return vkeys; +} + +} // namespace bb \ No newline at end of file diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp index 3aac2844030..0f23839f8b4 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/mega_flavor.hpp @@ -533,7 +533,7 @@ class MegaFlavor { VerificationKey(const VerificationKey& vk) = default; - VerificationKey(ProvingKey& proving_key) + void set_metadata(ProvingKey& proving_key) { this->pcs_verification_key = std::make_shared(); this->circuit_size = proving_key.circuit_size; @@ -545,7 +545,11 @@ class MegaFlavor { // Databus commitment propagation data this->databus_propagation_data = proving_key.databus_propagation_data; + } + VerificationKey(ProvingKey& proving_key) + { + set_metadata(proving_key); for (auto [polynomial, commitment] : zip_view(proving_key.polynomials.get_precomputed(), this->get_all())) { commitment = proving_key.commitment_key->commit(polynomial); }