From de32727fe85669a25cd4a8bf071b77bdc947c89c Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Tue, 27 Feb 2024 19:53:45 +0000 Subject: [PATCH 1/3] Parallelise function and kernel construction --- .../client_ivc_bench/client_ivc.bench.cpp | 55 +++++++++++++++++-- .../cpp/src/barretenberg/goblin/goblin.hpp | 4 +- .../proof_system/op_queue/ecc_op_queue.hpp | 7 +++ 3 files changed, 59 insertions(+), 7 deletions(-) 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 1e10564f6f7..2365599965f 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 @@ -46,14 +46,45 @@ class ClientIVCBench : public benchmark::Fixture { 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 + + ClientIVC::FoldProof function_fold_proof; + ClientIVC::FoldProof kernel_fold_proof; for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { + std::array kernel_and_function_builders; + parallel_for(2, [&](size_t workload_index) { + if (workload_index == 0) { + // Initialize with current op_queue + kernel_and_function_builders[workload_index] = Builder(ivc.goblin.op_queue); + // Compute kernel circuit + if (circuit_idx == 0) { + // First kernel is not really a kernel + GoblinMockCircuits::construct_mock_function_circuit( + kernel_and_function_builders[workload_index]); + } else { + // Actually kernel for next + GoblinMockCircuits::construct_mock_folding_kernel( + kernel_and_function_builders[workload_index], function_fold_proof, kernel_fold_proof); + } + } else { + // Construct function circuit in parallel + // Initialize without op_queue + kernel_and_function_builders[workload_index] = Builder(); + GoblinMockCircuits::construct_mock_function_circuit(kernel_and_function_builders[workload_index]); + } + }); + // Accumulate kernel + kernel_fold_proof = ivc.accumulate(kernel_and_function_builders[0]); + + // Prepend op_queue to function circuit + kernel_and_function_builders[1].op_queue->prepend_previous_queue(*ivc.goblin.op_queue); + + // Accumulate circuit + function_fold_proof = ivc.accumulate(kernel_and_function_builders[1]); + + // Retrieve op_queue from function circuit + std::swap(*ivc.goblin.op_queue, *kernel_and_function_builders[1].op_queue); // Accumulate function circuit Builder function_circuit{ ivc.goblin.op_queue }; @@ -65,6 +96,20 @@ class ClientIVCBench : public benchmark::Fixture { GoblinMockCircuits::construct_mock_folding_kernel(kernel_circuit, function_fold_proof, kernel_fold_proof); auto kernel_fold_proof = ivc.accumulate(kernel_circuit); } + + // Perform last kernel accumulation + if (NUM_CIRCUITS == 0) { + // If we only do 1 fold in total, use mock function circuit + Builder last_kernel_circuit{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_function_circuit(last_kernel_circuit); + auto kernel_fold_proof = ivc.accumulate(last_kernel_circuit); + } else { + // Accumulate kernel circuit + Builder last_kernel_circuit{ ivc.goblin.op_queue }; + GoblinMockCircuits::construct_mock_folding_kernel( + last_kernel_circuit, function_fold_proof, kernel_fold_proof); + auto kernel_fold_proof = ivc.accumulate(last_kernel_circuit); + } } }; diff --git a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp index 0efd9b3183a..cba0a50553d 100644 --- a/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp +++ b/barretenberg/cpp/src/barretenberg/goblin/goblin.hpp @@ -109,7 +109,7 @@ class Goblin { auto ultra_proof = prover.construct_proof(); // Construct and store the merge proof to be recursively verified on the next call to accumulate - MergeProver merge_prover{ op_queue }; + MergeProver merge_prover{ circuit_builder.op_queue }; merge_proof = merge_prover.construct_proof(); if (!merge_proof_exists) { @@ -137,7 +137,7 @@ class Goblin { } // Construct and store the merge proof to be recursively verified on the next call to accumulate - MergeProver merge_prover{ op_queue }; + MergeProver merge_prover{ circuit_builder.op_queue }; merge_proof = merge_prover.construct_proof(); if (!merge_proof_exists) { diff --git a/barretenberg/cpp/src/barretenberg/proof_system/op_queue/ecc_op_queue.hpp b/barretenberg/cpp/src/barretenberg/proof_system/op_queue/ecc_op_queue.hpp index c264cf8e1ea..3b1a6866967 100644 --- a/barretenberg/cpp/src/barretenberg/proof_system/op_queue/ecc_op_queue.hpp +++ b/barretenberg/cpp/src/barretenberg/proof_system/op_queue/ecc_op_queue.hpp @@ -77,6 +77,13 @@ class ECCOpQueue { ultra_ops_commitments = previous.ultra_ops_commitments; previous_ultra_ops_commitments = previous.previous_ultra_ops_commitments; } + /** + * @brief Prepend the information from the previous queue (used before accumulation/merge proof to be able to run + * circuit construction separately) + * + * @param previous_ptr + */ + void prepend_previous_queue(const ECCOpQueue* previous_ptr) { prepend_previous_queue(*previous_ptr); } /** * @brief Enable using std::swap on queues From 8073925ad60e2c69c95d20a3d980273ccfadc08f Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Wed, 28 Feb 2024 18:25:03 +0000 Subject: [PATCH 2/3] Removed extra lines --- .../benchmark/client_ivc_bench/client_ivc.bench.cpp | 10 ---------- 1 file changed, 10 deletions(-) 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 2365599965f..5a5e56f4ffe 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 @@ -85,16 +85,6 @@ class ClientIVCBench : public benchmark::Fixture { // Retrieve op_queue from function circuit std::swap(*ivc.goblin.op_queue, *kernel_and_function_builders[1].op_queue); - - // 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); } // Perform last kernel accumulation From 594952264b00f827d49aa6fa12932e8bb9267765 Mon Sep 17 00:00:00 2001 From: Rumata888 Date: Thu, 29 Feb 2024 14:29:45 +0000 Subject: [PATCH 3/3] here we go again --- .../client_ivc_bench/client_ivc.bench.cpp | 97 +++++++++++++++---- 1 file changed, 77 insertions(+), 20 deletions(-) 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 9840e596f89..41ff9f8ae75 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 @@ -43,43 +43,100 @@ class ClientIVCBench : public benchmark::Fixture { static void perform_ivc_accumulation_rounds(State& state, ClientIVC& ivc) { const size_t size_hint = 1 << 17; // Size hint for reserving wires/selector vector memory in builders - // Initialize IVC with function circuit - Builder initial_function_circuit{ size_hint, ivc.goblin.op_queue }; - GoblinMockCircuits::construct_mock_function_circuit(initial_function_circuit); - ivc.initialize(initial_function_circuit); - auto kernel_verifier_accumulator = std::make_shared(ivc.vks.first_func_vk); - + std::vector initial_function_circuits(2); + + // Construct 2 starting function circuits in parallel + parallel_for(2, [&](size_t circuit_index) { + GoblinMockCircuits::construct_mock_function_circuit(initial_function_circuits[circuit_index]); + }); + + // Prepend queue to the first circuit + initial_function_circuits[0].op_queue->prepend_previous_queue(*ivc.goblin.op_queue); + // Initialize ivc + ivc.initialize(initial_function_circuits[0]); + // Retrieve the queue + std::swap(*ivc.goblin.op_queue, *initial_function_circuits[0].op_queue); + + // Prepend queue to the second circuit + initial_function_circuits[1].op_queue->prepend_previous_queue(*ivc.goblin.op_queue); // Accumulate another function circuit - Builder function_circuit{ ivc.goblin.op_queue }; - GoblinMockCircuits::construct_mock_function_circuit(function_circuit); - auto function_fold_proof = ivc.accumulate(function_circuit); + auto function_fold_proof = ivc.accumulate(initial_function_circuits[1]); + // Retrieve the queue + std::swap(*ivc.goblin.op_queue, *initial_function_circuits[1].op_queue); VerifierFoldData function_fold_output = { function_fold_proof, ivc.vks.func_vk }; - // Create and accumulate the first folding kernel which only verifies the accumulation of a function circuit - Builder kernel_circuit{ size_hint, ivc.goblin.op_queue }; - kernel_verifier_accumulator = GoblinMockCircuits::construct_mock_folding_kernel( - kernel_circuit, function_fold_output, {}, kernel_verifier_accumulator); - auto kernel_fold_proof = ivc.accumulate(kernel_circuit); - VerifierFoldData kernel_fold_output = { kernel_fold_proof, ivc.vks.first_kernel_vk }; + // Free memory + initial_function_circuits.clear(); auto NUM_CIRCUITS = static_cast(state.range(0)); // Subtract two to account for the "initialization" round above i.e. we have already folded two function // circuits NUM_CIRCUITS -= 2; + + // The accumulator for kernel uses the function accumulation verification key + auto kernel_verifier_accumulator = std::make_shared(ivc.vks.first_func_vk); + + VerifierFoldData kernel_fold_output; for (size_t circuit_idx = 0; circuit_idx < NUM_CIRCUITS; ++circuit_idx) { + Builder kernel_circuit{ size_hint, ivc.goblin.op_queue }; + Builder function_circuit{ size_hint }; + // Construct function and kernel circuits in parallel + parallel_for(2, [&](size_t workload_idx) { + // workload index is 0 for kernel and 1 for function + if (workload_idx == 0) { + if (circuit_idx == 0) { + + // Create the first folding kernel which only verifies the accumulation of a + // function circuit + kernel_verifier_accumulator = GoblinMockCircuits::construct_mock_folding_kernel( + kernel_circuit, function_fold_output, {}, kernel_verifier_accumulator); + } else { + // Create kernel circuit containing the recursive folding verification of a function circuit and + // a kernel circuit + kernel_verifier_accumulator = GoblinMockCircuits::construct_mock_folding_kernel( + kernel_circuit, function_fold_output, kernel_fold_output, kernel_verifier_accumulator); + } + } else { + GoblinMockCircuits::construct_mock_function_circuit(function_circuit); + } + }); + + // No need to prepend queue, it's the same after last swap + // Accumulate kernel circuit + auto kernel_fold_proof = ivc.accumulate(kernel_circuit); + + // First iteration and the following ones differ + if (circuit_idx == 0) { + kernel_fold_output = { kernel_fold_proof, ivc.vks.first_kernel_vk }; + } else { + kernel_fold_output = { kernel_fold_proof, ivc.vks.kernel_vk }; + } + + // Prepend queue to function circuit + function_circuit.op_queue->prepend_previous_queue(*ivc.goblin.op_queue); + // Accumulate function circuit - Builder function_circuit{ size_hint, ivc.goblin.op_queue }; - GoblinMockCircuits::construct_mock_function_circuit(function_circuit); auto function_fold_proof = ivc.accumulate(function_circuit); function_fold_output = { function_fold_proof, ivc.vks.func_vk }; - // Create kernel circuit containing the recursive folding verification of a function circuit and a kernel - // circuit and accumulate it + // Retrieve queue + std::swap(*ivc.goblin.op_queue, *function_circuit.op_queue); + } + // If we haven't entered the cycle, the kernel proof accumulates just function proofs + if (NUM_CIRCUITS == 0) { + // Create and accumulate the first folding kernel which only verifies the accumulation of a function circuit + Builder kernel_circuit{ size_hint, ivc.goblin.op_queue }; + auto kernel_verifier_accumulator = std::make_shared(ivc.vks.first_func_vk); + kernel_verifier_accumulator = GoblinMockCircuits::construct_mock_folding_kernel( + kernel_circuit, function_fold_output, {}, kernel_verifier_accumulator); + auto kernel_fold_proof = ivc.accumulate(kernel_circuit); + kernel_fold_output = { kernel_fold_proof, ivc.vks.first_kernel_vk }; + } else { Builder kernel_circuit{ size_hint, ivc.goblin.op_queue }; kernel_verifier_accumulator = GoblinMockCircuits::construct_mock_folding_kernel( kernel_circuit, function_fold_output, kernel_fold_output, kernel_verifier_accumulator); - kernel_fold_proof = ivc.accumulate(kernel_circuit); + auto kernel_fold_proof = ivc.accumulate(kernel_circuit); kernel_fold_output = { kernel_fold_proof, ivc.vks.kernel_vk }; } }