Skip to content

Commit

Permalink
feat: Faster CIV benching with mocked VKs (#8843)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
codygunton authored Sep 30, 2024
1 parent a02370c commit fad3d6e
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 69 deletions.
Original file line number Diff line number Diff line change
@@ -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 <benchmark/benchmark.h>

#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;
Expand All @@ -16,89 +15,34 @@ namespace {

/**
* @brief Benchmark suite for the aztec client PG-Goblin IVC scheme
*
*/
class ClientIVCBench : public benchmark::Fixture {
public:
using Builder = MegaCircuitBuilder;
using DeciderVerificationKey = DeciderVerificationKey_<MegaFlavor>;
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
{
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<DeciderVerificationKey>(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<size_t>(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)
Expand Down
5 changes: 4 additions & 1 deletion barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<VerificationKey>& precomputed_vk)
void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr<VerificationKey>& precomputed_vk, bool mock_vk)
{
is_kernel = !is_kernel; // toggle on each call (every even circuit is a kernel)

Expand All @@ -173,6 +173,9 @@ void ClientIVC::accumulate(ClientCircuit& circuit, const std::shared_ptr<Verific

// Set the verification key from precomputed if available, else compute it
honk_vk = precomputed_vk ? precomputed_vk : std::make_shared<VerificationKey>(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) {
Expand Down
13 changes: 11 additions & 2 deletions barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<VerificationKey>& 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<VerificationKey>& precomputed_vk = nullptr,
bool mock_vk = false);

Proof prove();

Expand Down
45 changes: 45 additions & 0 deletions barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.test.cpp
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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());
}
71 changes: 71 additions & 0 deletions barretenberg/cpp/src/barretenberg/client_ivc/test_bench_shared.hpp
Original file line number Diff line number Diff line change
@@ -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<DeciderVerificationKey_<MegaFlavor>>(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<std::shared_ptr<typename MegaFlavor::VerificationKey>> mock_verification_keys(const size_t num_circuits)
{

std::vector<std::shared_ptr<typename MegaFlavor::VerificationKey>> vkeys;

for (size_t idx = 0; idx < num_circuits; ++idx) {
auto key = std::make_shared<typename MegaFlavor::VerificationKey>();
for (auto& commitment : key->get_all()) {
commitment = MegaFlavor::Commitment::random_element();
}
vkeys.push_back(key);
}

return vkeys;
}

} // namespace bb
Original file line number Diff line number Diff line change
Expand Up @@ -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<VerifierCommitmentKey>();
this->circuit_size = proving_key.circuit_size;
Expand All @@ -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);
}
Expand Down

1 comment on commit fad3d6e

@AztecBot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'C++ Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.05.

Benchmark suite Current: fad3d6e Previous: 4354ae0 Ratio
wasmClientIVCBench/Full/6 96144.082579 ms/iter 91525.805055 ms/iter 1.05

This comment was automatically generated by workflow using github-action-benchmark.

CC: @ludamad @codygunton

Please sign in to comment.