diff --git a/barretenberg/cpp/scripts/analyze_client_ivc_bench.py b/barretenberg/cpp/scripts/analyze_client_ivc_bench.py index 46a37826efae..07809f1f1cc7 100644 --- a/barretenberg/cpp/scripts/analyze_client_ivc_bench.py +++ b/barretenberg/cpp/scripts/analyze_client_ivc_bench.py @@ -71,3 +71,39 @@ print(f"{key:<{max_label_length}}{time_ms:>8.0f} {time_ms/total_time_ms:>8.2%}") +# Relations breakdown +# Note: The timings here are off likely because the tracking is occuring in a hot loop but +# they should be meaningful relative to one another +print('\nRelation contributions (times to be interpreted relatively):') +relations = [ + "Arithmetic::accumulate(t)", + "Permutation::accumulate(t)", + "Lookup::accumulate(t)", + "DeltaRange::accumulate(t)", + "Elliptic::accumulate(t)", + "Auxiliary::accumulate(t)", + "EccOp::accumulate(t)", + "DatabusRead::accumulate(t)", + "PoseidonExt::accumulate(t)", + "PoseidonInt::accumulate(t)", +] +with open(PREFIX/IVC_BENCH_JSON, "r") as read_file: + read_result = json.load(read_file) + for _bench in read_result["benchmarks"]: + if _bench["name"] == BENCHMARK: + bench = _bench +bench_components = dict(filter(lambda x: x[0] in relations, bench.items())) + +# For each kept time, get the proportion over all kept times. +sum_of_kept_times_ms = sum(float(time) + for _, time in bench_components.items())/1e6 +max_label_length = max(len(label) for label in relations) +column = {"function": "function", "ms": "ms", "%": "% sum"} +print( + f"{column['function']:<{max_label_length}}{column['ms']:>8} {column['%']:>8}") +for key in relations: + if key not in bench: + time_ms = 0 + else: + time_ms = bench[key]/1e6 + print(f"{key:<{max_label_length}}{time_ms:>8.0f} {time_ms/sum_of_kept_times_ms:>8.2%}") \ No newline at end of file diff --git a/barretenberg/cpp/scripts/benchmark_client_ivc.sh b/barretenberg/cpp/scripts/benchmark_client_ivc.sh index 17a193c6d829..7991ef879407 100755 --- a/barretenberg/cpp/scripts/benchmark_client_ivc.sh +++ b/barretenberg/cpp/scripts/benchmark_client_ivc.sh @@ -2,6 +2,7 @@ set -eu TARGET="client_ivc_bench" +# Note: to run structured trace version, change "Full" to "FullStructured" here and in analyze script FILTER="ClientIVCBench/Full/6$" BUILD_DIR=build-op-count-time diff --git a/barretenberg/cpp/scripts/compare_branch_vs_baseline_remote.sh b/barretenberg/cpp/scripts/compare_branch_vs_baseline_remote.sh index 27d1af8966ae..edd23d05119a 100755 --- a/barretenberg/cpp/scripts/compare_branch_vs_baseline_remote.sh +++ b/barretenberg/cpp/scripts/compare_branch_vs_baseline_remote.sh @@ -11,7 +11,7 @@ # Specify the benchmark suite and the "baseline" branch against which to compare BENCHMARK=${1:-goblin_bench} -FILTER=${2:-""} +FILTER=${2:-"*."} PRESET=${3:-clang16} BUILD_DIR=${4:-build} HARDWARE_CONCURRENCY=${HARDWARE_CONCURRENCY:-16} @@ -24,12 +24,7 @@ echo -e "\nComparing $BENCHMARK between $BASELINE_BRANCH and current branch:" # Move above script dir. cd $(dirname $0)/.. -# Configure and build benchmark in feature branch -echo -e "\nConfiguring and building $BENCHMARK in current feature branch...\n" -cmake --preset $PRESET -cmake --build --preset $PRESET --target $BENCHMARK - -# Run bench in current branch +# Run benchmark in current branch echo -e "\nRunning benchmark in feature branch.." ./scripts/benchmark_remote.sh $BENCHMARK\ "./$BENCHMARK --benchmark_filter=$FILTER\ @@ -40,13 +35,8 @@ echo -e "\nRunning benchmark in feature branch.." scp $BB_SSH_KEY $BB_SSH_INSTANCE:$BB_SSH_CPP_PATH/build/results_after.json $BUILD_DIR/ -# Configure and build benchmark in $BASELINE branch -echo -e "\nConfiguring and building $BENCHMARK in $BASELINE_BRANCH...\n" +# Run benchmark in baseline branch git checkout $BASELINE_BRANCH -cmake --preset $PRESET -cmake --build --preset $PRESET --target $BENCHMARK - -# Run bench in current branch echo -e "\nRunning benchmark in feature branch.." ./scripts/benchmark_remote.sh $BENCHMARK\ "./$BENCHMARK --benchmark_filter=$FILTER\ diff --git a/barretenberg/cpp/scripts/compare_branch_vs_baseline_remote_wasm.sh b/barretenberg/cpp/scripts/compare_branch_vs_baseline_remote_wasm.sh new file mode 100755 index 000000000000..d7732ffc41a8 --- /dev/null +++ b/barretenberg/cpp/scripts/compare_branch_vs_baseline_remote_wasm.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +# Install requirements (numpy + scipy) for comparison script if necessary. +# Note: By default, installation will occur in $HOME/.local/bin. +# pip3 install --user -r $BUILD_DIR/_deps/benchmark-src/requirements.txt + + +# This script is used to compare a suite of benchmarks between baseline (default: master) and +# the branch from which the script is run. Simply check out the branch of interest, ensure +# it is up to date with local master, and run the script. + +# Specify the benchmark suite and the "baseline" branch against which to compare +BENCHMARK=${1:-goblin_bench} +FILTER=${2:-"*."} +PRESET=${3:-wasm-threads} +BUILD_DIR=${4:-build-wasm-threads} +HARDWARE_CONCURRENCY=${HARDWARE_CONCURRENCY:-16} + +BASELINE_BRANCH="master" +BENCH_TOOLS_DIR="$BUILD_DIR/_deps/benchmark-src/tools" + +echo -e "\nComparing $BENCHMARK between $BASELINE_BRANCH and current branch:" + +# Move above script dir. +cd $(dirname $0)/.. + +# Run benchmark in feature branch +echo -e "\nRunning benchmark in feature branch.." +./scripts/benchmark_wasm_remote.sh $BENCHMARK\ + "./$BENCHMARK --benchmark_filter=$FILTER\ + --benchmark_out=../results_after.json\ + --benchmark_out_format=json" + +scp $BB_SSH_KEY $BB_SSH_INSTANCE:$BB_SSH_CPP_PATH/results_after.json $BUILD_DIR/ + +# Run benchmark in $BASELINE branch + +echo -e "\nRunning benchmark in baseline branch.." +git checkout $BASELINE_BRANCH +./scripts/benchmark_wasm_remote.sh $BENCHMARK\ + "./$BENCHMARK --benchmark_filter=$FILTER\ + --benchmark_out=../results_before.json\ + --benchmark_out_format=json" + +scp $BB_SSH_KEY $BB_SSH_INSTANCE:$BB_SSH_CPP_PATH/results_before.json $BUILD_DIR/ + +# Call compare.py on the results (json) to get high level statistics. +# See docs at https://github.com/google/benchmark/blob/main/docs/tools.md for more details. +$BENCH_TOOLS_DIR/compare.py benchmarks $BUILD_DIR/results_before.json $BUILD_DIR/results_after.json + +# Return to branch from which the script was called +git checkout - \ No newline at end of file diff --git a/barretenberg/cpp/scripts/compare_client_ivc_bench.sh b/barretenberg/cpp/scripts/compare_client_ivc_bench.sh new file mode 100755 index 000000000000..aa4179d6df07 --- /dev/null +++ b/barretenberg/cpp/scripts/compare_client_ivc_bench.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -eu + +./scripts/compare_branch_vs_baseline_remote_wasm.sh client_ivc_bench 'Full/6$' \ No newline at end of file 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 dcbcd0c39a5f..ded7acc08f14 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 @@ -173,6 +173,25 @@ BENCHMARK_DEFINE_F(ClientIVCBench, Full)(benchmark::State& state) } } +/** + * @brief Benchmark the prover work for the full PG-Goblin IVC protocol + * + */ +BENCHMARK_DEFINE_F(ClientIVCBench, FullStructured)(benchmark::State& state) +{ + ClientIVC ivc; + ivc.structured_flag = true; + ivc.precompute_folding_verification_keys(); + 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 * @@ -252,6 +271,7 @@ BENCHMARK_DEFINE_F(ClientIVCBench, Translator)(benchmark::State& state) ->Arg(1 << 6) BENCHMARK_REGISTER_F(ClientIVCBench, Full)->Unit(benchmark::kMillisecond)->ARGS; +BENCHMARK_REGISTER_F(ClientIVCBench, FullStructured)->Unit(benchmark::kMillisecond)->ARGS; BENCHMARK_REGISTER_F(ClientIVCBench, Accumulate)->Unit(benchmark::kMillisecond)->ARGS; BENCHMARK_REGISTER_F(ClientIVCBench, Decide)->Unit(benchmark::kMillisecond)->ARGS; BENCHMARK_REGISTER_F(ClientIVCBench, ECCVM)->Unit(benchmark::kMillisecond)->ARGS; diff --git a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp index 48959e431e9b..af040b8da4b8 100644 --- a/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp +++ b/barretenberg/cpp/src/barretenberg/benchmark/relations_bench/relations.bench.cpp @@ -1,4 +1,5 @@ #include "barretenberg/eccvm/eccvm_flavor.hpp" +#include "barretenberg/protogalaxy/protogalaxy_prover.hpp" #include "barretenberg/stdlib_circuit_builders/goblin_ultra_flavor.hpp" #include "barretenberg/stdlib_circuit_builders/ultra_flavor.hpp" #include "barretenberg/translator_vm/goblin_translator_flavor.hpp" @@ -13,46 +14,109 @@ namespace bb::benchmark::relations { using Fr = bb::fr; using Fq = grumpkin::fr; -template void execute_relation(::benchmark::State& state) +// Generic helper for executing Relation::accumulate for the template specified input type +template +void execute_relation(::benchmark::State& state) { using FF = typename Flavor::FF; - using AllValues = typename Flavor::AllValues; - using SumcheckArrayOfValuesOverSubrelations = typename Relation::SumcheckArrayOfValuesOverSubrelations; auto params = bb::RelationParameters::get_random(); - // Extract an array containing all the polynomial evaluations at a given row i - AllValues new_value{}; - // Define the appropriate SumcheckArrayOfValuesOverSubrelations type for this relation and initialize to zero - SumcheckArrayOfValuesOverSubrelations accumulator; - // Evaluate each constraint in the relation and check that each is satisfied + // Instantiate zero-initialized inputs and accumulator + Input input{}; + Accumulator accumulator; for (auto _ : state) { - Relation::accumulate(accumulator, new_value, params, 1); + Relation::accumulate(accumulator, input, params, 1); } } -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); - -BENCHMARK(execute_relation>); - -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); - -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); -BENCHMARK(execute_relation>); + +// Single execution of relation on values (FF), e.g. Sumcheck verifier / PG perturbator work +template void execute_relation_for_values(::benchmark::State& state) +{ + using Input = typename Flavor::AllValues; + using Accumulator = typename Relation::SumcheckArrayOfValuesOverSubrelations; + + execute_relation(state); +} + +// Single execution of relation on Sumcheck univariates, i.e. Sumcheck/Decider prover work +template void execute_relation_for_univariates(::benchmark::State& state) +{ + using Input = typename Flavor::ExtendedEdges; + using Accumulator = typename Relation::SumcheckTupleOfUnivariatesOverSubrelations; + + execute_relation(state); +} + +// Single execution of relation on PG univariates, i.e. PG combiner work +template void execute_relation_for_pg_univariates(::benchmark::State& state) +{ + using ProverInstances = ProverInstances_; + using ProtoGalaxyProver = ProtoGalaxyProver_; + using Input = ProtoGalaxyProver::ExtendedUnivariates; + using Accumulator = typename Relation::template ProtogalaxyTupleOfUnivariatesOverSubrelations; + + execute_relation(state); +} + +// Ultra relations (PG prover combiner work) +BENCHMARK(execute_relation_for_pg_univariates>); +BENCHMARK(execute_relation_for_pg_univariates>); +BENCHMARK(execute_relation_for_pg_univariates>); +BENCHMARK(execute_relation_for_pg_univariates>); +BENCHMARK(execute_relation_for_pg_univariates>); +BENCHMARK(execute_relation_for_pg_univariates>); + +// Goblin-Ultra only relations (PG prover combiner work) +BENCHMARK(execute_relation_for_pg_univariates>); +BENCHMARK(execute_relation_for_pg_univariates>); +BENCHMARK(execute_relation_for_pg_univariates>); +BENCHMARK(execute_relation_for_pg_univariates>); + +// Ultra relations (Sumcheck prover work) +BENCHMARK(execute_relation_for_univariates>); +BENCHMARK(execute_relation_for_univariates>); +BENCHMARK(execute_relation_for_univariates>); +BENCHMARK(execute_relation_for_univariates>); +BENCHMARK(execute_relation_for_univariates>); +BENCHMARK(execute_relation_for_univariates>); + +// Goblin-Ultra only relations (Sumcheck prover work) +BENCHMARK(execute_relation_for_univariates>); +BENCHMARK(execute_relation_for_univariates>); +BENCHMARK(execute_relation_for_univariates>); +BENCHMARK(execute_relation_for_univariates>); + +// Ultra relations (verifier work) +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); + +// Goblin-Ultra only relations (verifier work) +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); + +// Translator VM +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); + +// ECCVM +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); +BENCHMARK(execute_relation_for_values>); } // namespace bb::benchmark::relations diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp index da85699eb471..758a313658be 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.cpp @@ -11,7 +11,7 @@ namespace bb { void ClientIVC::initialize(ClientCircuit& circuit) { goblin.merge(circuit); // Construct new merge proof - prover_fold_output.accumulator = std::make_shared(circuit); + prover_fold_output.accumulator = std::make_shared(circuit, structured_flag); } /** @@ -24,7 +24,7 @@ void ClientIVC::initialize(ClientCircuit& circuit) ClientIVC::FoldProof ClientIVC::accumulate(ClientCircuit& circuit) { goblin.merge(circuit); // Add recursive merge verifier and construct new merge proof - prover_instance = std::make_shared(circuit); + prover_instance = std::make_shared(circuit, structured_flag); FoldingProver folding_prover({ prover_fold_output.accumulator, prover_instance }); prover_fold_output = folding_prover.fold_instances(); return prover_fold_output.folding_data; diff --git a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp index 6ef9a8676b6a..54c6a7991ff5 100644 --- a/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp +++ b/barretenberg/cpp/src/barretenberg/client_ivc/client_ivc.hpp @@ -78,6 +78,9 @@ class ClientIVC { // be needed in the real IVC as they are provided as inputs std::shared_ptr prover_instance; + // A flag indicating whether or not to construct a structured trace in the ProverInstance + bool structured_flag = false; + void initialize(ClientCircuit& circuit); FoldProof accumulate(ClientCircuit& circuit); diff --git a/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.cpp b/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.cpp index d582089921ec..286728ca1468 100644 --- a/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.cpp +++ b/barretenberg/cpp/src/barretenberg/execution_trace/execution_trace.cpp @@ -118,7 +118,7 @@ typename ExecutionTrace_::TraceData ExecutionTrace_::construct_t // If the trace is structured, we populate the data from the next block at a fixed block size offset if (is_structured) { - offset += builder.FIXED_BLOCK_SIZE; + offset += block.get_fixed_size(); } else { // otherwise, the next block starts immediately following the previous one offset += block_size; } diff --git a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/arithmetization.hpp b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/arithmetization.hpp index c864af55e7c9..f71b77021eed 100644 --- a/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/arithmetization.hpp +++ b/barretenberg/cpp/src/barretenberg/plonk_honk_shared/arithmetization/arithmetization.hpp @@ -51,6 +51,8 @@ template class ExecutionTr bool has_ram_rom = false; // does the block contain RAM/ROM gates bool is_pub_inputs = false; // is this the public inputs block + uint32_t fixed_size; // Fixed size for use in structured trace + bool operator==(const ExecutionTraceBlock& other) const = default; size_t size() const { return std::get<0>(this->wires).size(); } @@ -64,6 +66,9 @@ template class ExecutionTr p.reserve(size_hint); } } + + uint32_t get_fixed_size() const { return fixed_size; } + void set_fixed_size(uint32_t size_in) { fixed_size = size_in; } }; // These are not magic numbers and they should not be written with global constants. These parameters are not @@ -119,7 +124,6 @@ template class UltraArith { public: static constexpr size_t NUM_WIRES = 4; static constexpr size_t NUM_SELECTORS = 11; - static constexpr size_t FIXED_BLOCK_SIZE = 1 << 10; // Size of each block in a structured trace (arbitrary for now) using FF = FF_; class UltraTraceBlock : public ExecutionTraceBlock { @@ -158,10 +162,24 @@ template class UltraArith { UltraTraceBlock aux; UltraTraceBlock lookup; + static constexpr uint32_t FIXED_BLOCK_SIZE = 1 << 10; // (Arbitrary for now) + std::array fixed_block_sizes{ + 1 << 3, // pub_inputs; + FIXED_BLOCK_SIZE, // arithmetic; + FIXED_BLOCK_SIZE, // delta_range; + FIXED_BLOCK_SIZE, // elliptic; + FIXED_BLOCK_SIZE, // aux; + FIXED_BLOCK_SIZE // lookup; + }; + TraceBlocks() { aux.has_ram_rom = true; pub_inputs.is_pub_inputs = true; + // Set fixed block sizes for use in structured trace + for (auto [block, size] : zip_view(this->get(), fixed_block_sizes)) { + block.set_fixed_size(size); + } } auto get() { return RefArray{ pub_inputs, arithmetic, delta_range, elliptic, aux, lookup }; } @@ -178,6 +196,31 @@ template class UltraArith { info(""); } + size_t get_total_structured_size() + { + size_t total_size = 0; + for (auto block : this->get()) { + total_size += block.get_fixed_size(); + } + return total_size; + } + + /** + * @brief Check that the number of rows populated in each block does not exceed the specified fixed size + * @note This check is only applicable when utilizing a structured trace + * + */ + void check_within_fixed_sizes() + { + for (auto block : this->get()) { + if (block.size() > block.get_fixed_size()) { + info("WARNING: Num gates in circuit block exceeds the specified fixed size - execution trace will " + "not be constructed correctly!"); + ASSERT(false); + } + } + } + bool operator==(const TraceBlocks& other) const = default; }; @@ -197,7 +240,6 @@ template class UltraHonkArith { public: static constexpr size_t NUM_WIRES = 4; static constexpr size_t NUM_SELECTORS = 14; - static constexpr size_t FIXED_BLOCK_SIZE = 1 << 10; // Size of each block in a structured trace (arbitrary for now) using FF = FF_; @@ -270,10 +312,32 @@ template class UltraHonkArith { UltraHonkTraceBlock poseidon_external; UltraHonkTraceBlock poseidon_internal; + // This is a set of fixed block sizes that accomodates the circuits currently processed in the ClientIvc bench. + // Note 1: The individual block sizes do NOT need to be powers of 2, this is just for conciseness. + // Note 2: Current sizes result in a full trace size of 2^18. It's not possible to define a fixed structure + // that accomdates both the kernel and the function circuit while remaining under 2^17. This is because the + // circuits differ in structure but are also both designed to be "full" within the 2^17 size. + std::array fixed_block_sizes{ + 1 << 10, // ecc_op; + 1 << 7, // pub_inputs; + 1 << 16, // arithmetic; + 1 << 15, // delta_range; + 1 << 14, // elliptic; + 1 << 16, // aux; + 1 << 15, // lookup; + 1 << 7, // busread; + 1 << 11, // poseidon_external; + 1 << 14 // poseidon_internal; + }; + TraceBlocks() { aux.has_ram_rom = true; pub_inputs.is_pub_inputs = true; + // Set fixed block sizes for use in structured trace + for (auto [block, size] : zip_view(this->get(), fixed_block_sizes)) { + block.set_fixed_size(size); + } } auto get() @@ -284,20 +348,40 @@ template class UltraHonkArith { void summarize() const { - info("Gate blocks summary:"); - info("goblin ecc op:\t", ecc_op.size()); - info("pub inputs:\t", pub_inputs.size()); - info("arithmetic:\t", arithmetic.size()); - info("delta range:\t", delta_range.size()); - info("elliptic:\t", elliptic.size()); - info("auxiliary:\t", aux.size()); - info("lookups:\t", lookup.size()); - info("busread:\t", busread.size()); - info("poseidon ext:\t", poseidon_external.size()); - info("poseidon int:\t", poseidon_internal.size()); + info("Gate blocks summary: (actual gates / fixed capacity)"); + info("goblin ecc op:\t", ecc_op.size(), "/", ecc_op.get_fixed_size()); + info("pub inputs:\t", pub_inputs.size(), "/", pub_inputs.get_fixed_size()); + info("arithmetic:\t", arithmetic.size(), "/", arithmetic.get_fixed_size()); + info("delta range:\t", delta_range.size(), "/", delta_range.get_fixed_size()); + info("elliptic:\t", elliptic.size(), "/", elliptic.get_fixed_size()); + info("auxiliary:\t", aux.size(), "/", aux.get_fixed_size()); + info("lookups:\t", lookup.size(), "/", lookup.get_fixed_size()); + info("busread:\t", busread.size(), "/", busread.get_fixed_size()); + info("poseidon ext:\t", poseidon_external.size(), "/", poseidon_external.get_fixed_size()); + info("poseidon int:\t", poseidon_internal.size(), "/", poseidon_internal.get_fixed_size()); info(""); } + size_t get_total_structured_size() + { + size_t total_size = 0; + for (auto block : this->get()) { + total_size += block.get_fixed_size(); + } + return total_size; + } + + void check_within_fixed_sizes() + { + for (auto block : this->get()) { + if (block.size() > block.get_fixed_size()) { + info("WARNING: Num gates in circuit block exceeds the specified fixed size - execution trace will " + "not be constructed correctly!"); + ASSERT(false); + } + } + } + bool operator==(const TraceBlocks& other) const = default; }; diff --git a/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp b/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp index 6471ba85b56e..e4821a242954 100644 --- a/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp +++ b/barretenberg/cpp/src/barretenberg/polynomials/univariate.hpp @@ -86,6 +86,20 @@ template to_buffer() const { return ::to_buffer(evaluations); } diff --git a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp index c03af2e5333e..6629142a5188 100644 --- a/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp +++ b/barretenberg/cpp/src/barretenberg/protogalaxy/protogalaxy_prover.hpp @@ -323,8 +323,23 @@ template class ProtoGalaxyProver_ { const FF& scaling_factor) { using Relation = std::tuple_element_t; - Relation::accumulate( - std::get(univariate_accumulators), extended_univariates, relation_parameters, scaling_factor); + + // Check if the relation is skippable to speed up accumulation + if constexpr (!isSkippable) { + // If not, accumulate normally + Relation::accumulate(std::get(univariate_accumulators), + extended_univariates, + relation_parameters, + scaling_factor); + } else { + // If so, only compute the contribution if the relation is active + if (!Relation::skip(extended_univariates)) { + Relation::accumulate(std::get(univariate_accumulators), + extended_univariates, + relation_parameters, + scaling_factor); + } + } // Repeat for the next relation. if constexpr (relation_idx + 1 < Flavor::NUM_RELATIONS) { @@ -349,9 +364,23 @@ template class ProtoGalaxyProver_ { const FF& scaling_factor) { using Relation = std::tuple_element_t; - Relation::accumulate( - std::get(univariate_accumulators), extended_univariates, relation_parameters, scaling_factor); - + // WORKTODO: disable skipping for the combiner for now.. + // Check if the relation is skippable to speed up accumulation + if constexpr (!isSkippable) { + // If not, accumulate normally + Relation::accumulate(std::get(univariate_accumulators), + extended_univariates, + relation_parameters, + scaling_factor); + } else { + // If so, only compute the contribution if the relation is active + if (!Relation::skip(extended_univariates)) { + Relation::accumulate(std::get(univariate_accumulators), + extended_univariates, + relation_parameters, + scaling_factor); + } + } // Repeat for the next relation. if constexpr (relation_idx + 1 < Flavor::NUM_RELATIONS) { accumulate_relation_univariates< diff --git a/barretenberg/cpp/src/barretenberg/relations/auxiliary_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/auxiliary_relation.hpp index ea8e4b40e73b..5bb956d8ac48 100644 --- a/barretenberg/cpp/src/barretenberg/relations/auxiliary_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/auxiliary_relation.hpp @@ -53,10 +53,7 @@ template class AuxiliaryRelationImpl { * @brief Returns true if the contribution from all subrelations for the provided inputs is identically zero * */ - template inline static bool skip(const AllEntities& in) - { - return (in.q_aux.value_at(0).is_zero() && in.q_aux.value_at(1).is_zero()); - } + template inline static bool skip(const AllEntities& in) { return in.q_aux.is_zero(); } /** * @brief Expression for the generalized permutation sort gate. @@ -98,7 +95,7 @@ template class AuxiliaryRelationImpl { const Parameters& params, const FF& scaling_factor) { - + BB_OP_COUNT_TIME_NAME("Auxiliary::accumulate"); // All subrelations have the same length so we use the same length view for all calculations using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; using View = typename Accumulator::View; diff --git a/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp index 7a68d156b38d..3c897ce39090 100644 --- a/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/databus_lookup_relation.hpp @@ -63,6 +63,12 @@ template class DatabusLookupRelationImpl { true, false, true, false }; + template inline static bool skip([[maybe_unused]] const AllEntities& in) + { + // Ensure the input does not contain a read gate or data that is being read + return in.q_busread.is_zero() && in.calldata_read_counts.is_zero() && in.return_data_read_counts.is_zero(); + } + // Interface for easy access of databus components by column (bus_idx) template struct BusData; @@ -231,6 +237,7 @@ template class DatabusLookupRelationImpl { const Parameters& params, const FF& scaling_factor) { + BB_OP_COUNT_TIME_NAME("DatabusRead::accumulate"); using Accumulator = typename std::tuple_element_t<0, ContainerOverSubrelations>; using View = typename Accumulator::View; diff --git a/barretenberg/cpp/src/barretenberg/relations/delta_range_constraint_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/delta_range_constraint_relation.hpp index 25429fbc0028..b2bef8ea7910 100644 --- a/barretenberg/cpp/src/barretenberg/relations/delta_range_constraint_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/delta_range_constraint_relation.hpp @@ -20,7 +20,7 @@ template class DeltaRangeConstraintRelationImpl { */ template inline static bool skip(const AllEntities& in) { - return (in.q_delta_range.value_at(0).is_zero() && in.q_delta_range.value_at(1).is_zero()); + return in.q_delta_range.is_zero(); } /** @@ -44,6 +44,7 @@ template class DeltaRangeConstraintRelationImpl { const Parameters&, const FF& scaling_factor) { + BB_OP_COUNT_TIME_NAME("DeltaRange::accumulate"); using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; using View = typename Accumulator::View; auto w_1 = View(in.w_l); diff --git a/barretenberg/cpp/src/barretenberg/relations/ecc_op_queue_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/ecc_op_queue_relation.hpp index faf2f0da162d..29d23be4e651 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ecc_op_queue_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/ecc_op_queue_relation.hpp @@ -18,6 +18,13 @@ template class EccOpQueueRelationImpl { 3 // op-queue-wire vanishes sub-relation 4 }; + template inline static bool skip([[maybe_unused]] const AllEntities& in) + { + // The prover can skip execution of this relation altogether since an honest input will lead to a zero + // contribution at every row, even when the selector lagrange_ecc_op is on + return true; + } + /** * @brief Expression for the generalized permutation sort gate. * @details The relation is defined as C(in(X)...) = @@ -43,6 +50,7 @@ template class EccOpQueueRelationImpl { const Parameters&, const FF& scaling_factor) { + BB_OP_COUNT_TIME_NAME("EccOp::accumulate"); using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; using View = typename Accumulator::View; diff --git a/barretenberg/cpp/src/barretenberg/relations/elliptic_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/elliptic_relation.hpp index 2c0b2a850627..7fcd8df4b56f 100644 --- a/barretenberg/cpp/src/barretenberg/relations/elliptic_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/elliptic_relation.hpp @@ -18,10 +18,7 @@ template class EllipticRelationImpl { * @brief Returns true if the contribution from all subrelations for the provided inputs is identically zero * */ - template inline static bool skip(const AllEntities& in) - { - return (in.q_elliptic.value_at(0).is_zero() && in.q_elliptic.value_at(1).is_zero()); - } + template inline static bool skip(const AllEntities& in) { return in.q_elliptic.is_zero(); } // TODO(@zac-williamson #2609 find more generic way of doing this) static constexpr FF get_curve_b() @@ -51,6 +48,7 @@ template class EllipticRelationImpl { const Parameters&, const FF& scaling_factor) { + BB_OP_COUNT_TIME_NAME("Elliptic::accumulate"); // TODO(@zac - williamson #2608 when Pedersen refactor is completed, // replace old addition relations with these ones and // remove endomorphism coefficient in ecc add gate(not used)) diff --git a/barretenberg/cpp/src/barretenberg/relations/lookup_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/lookup_relation.hpp index 337469bd5c80..46b70df7cabf 100644 --- a/barretenberg/cpp/src/barretenberg/relations/lookup_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/lookup_relation.hpp @@ -184,7 +184,7 @@ template class LookupRelationImpl { const Parameters& params, const FF& scaling_factor) { - + BB_OP_COUNT_TIME_NAME("Lookup::accumulate"); { using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; using View = typename Accumulator::View; diff --git a/barretenberg/cpp/src/barretenberg/relations/permutation_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/permutation_relation.hpp index 8ff08be35d2c..b904fabeb956 100644 --- a/barretenberg/cpp/src/barretenberg/relations/permutation_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/permutation_relation.hpp @@ -25,8 +25,7 @@ template class UltraPermutationRelationImpl { { // If z_perm == z_perm_shift, this implies that none of the wire values for the present input are involved in // non-trivial copy constraints. - return (in.z_perm.value_at(0) == in.z_perm_shift.value_at(0) && - in.z_perm.value_at(1) == in.z_perm_shift.value_at(1)); + return (in.z_perm - in.z_perm_shift).is_zero(); } inline static auto& get_grand_product_polynomial(auto& in) { return in.z_perm; } @@ -96,6 +95,7 @@ template class UltraPermutationRelationImpl { const Parameters& params, const FF& scaling_factor) { + BB_OP_COUNT_TIME_NAME("Permutation::accumulate"); // Contribution (1) [&]() { using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; diff --git a/barretenberg/cpp/src/barretenberg/relations/poseidon2_external_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/poseidon2_external_relation.hpp index 29d082b4a4e0..11de33a20796 100644 --- a/barretenberg/cpp/src/barretenberg/relations/poseidon2_external_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/poseidon2_external_relation.hpp @@ -19,7 +19,7 @@ template class Poseidon2ExternalRelationImpl { */ template inline static bool skip(const AllEntities& in) { - return (in.q_poseidon2_external.value_at(0).is_zero() && in.q_poseidon2_external.value_at(1).is_zero()); + return in.q_poseidon2_external.is_zero(); } /** @@ -52,6 +52,7 @@ template class Poseidon2ExternalRelationImpl { const Parameters&, const FF& scaling_factor) { + BB_OP_COUNT_TIME_NAME("PoseidonExt::accumulate"); using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; using View = typename Accumulator::View; auto w_l = View(in.w_l); diff --git a/barretenberg/cpp/src/barretenberg/relations/poseidon2_internal_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/poseidon2_internal_relation.hpp index e21999358682..0014db76971e 100644 --- a/barretenberg/cpp/src/barretenberg/relations/poseidon2_internal_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/poseidon2_internal_relation.hpp @@ -21,7 +21,7 @@ template class Poseidon2InternalRelationImpl { */ template inline static bool skip(const AllEntities& in) { - return (in.q_poseidon2_internal.value_at(0).is_zero() && in.q_poseidon2_internal.value_at(1).is_zero()); + return in.q_poseidon2_internal.is_zero(); } /** @@ -49,6 +49,7 @@ template class Poseidon2InternalRelationImpl { const Parameters&, const FF& scaling_factor) { + BB_OP_COUNT_TIME_NAME("PoseidonInt::accumulate"); using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; using View = typename Accumulator::View; auto w_l = View(in.w_l); diff --git a/barretenberg/cpp/src/barretenberg/relations/ultra_arithmetic_relation.hpp b/barretenberg/cpp/src/barretenberg/relations/ultra_arithmetic_relation.hpp index 7a5a1e0d9178..d99b57fb7165 100644 --- a/barretenberg/cpp/src/barretenberg/relations/ultra_arithmetic_relation.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/ultra_arithmetic_relation.hpp @@ -16,10 +16,7 @@ template class UltraArithmeticRelationImpl { * @brief Returns true if the contribution from all subrelations for the provided inputs is identically zero * */ - template inline static bool skip(const AllEntities& in) - { - return (in.q_arith.value_at(0).is_zero() && in.q_arith.value_at(1).is_zero()); - } + template inline static bool skip(const AllEntities& in) { return in.q_arith.is_zero(); } /** * @brief Expression for the Ultra Arithmetic gate. @@ -78,6 +75,7 @@ template class UltraArithmeticRelationImpl { const Parameters&, const FF& scaling_factor) { + BB_OP_COUNT_TIME_NAME("Arithmetic::accumulate"); { using Accumulator = std::tuple_element_t<0, ContainerOverSubrelations>; using View = typename Accumulator::View; diff --git a/barretenberg/cpp/src/barretenberg/relations/utils.hpp b/barretenberg/cpp/src/barretenberg/relations/utils.hpp index 1a777af8fdcd..680f9190427b 100644 --- a/barretenberg/cpp/src/barretenberg/relations/utils.hpp +++ b/barretenberg/cpp/src/barretenberg/relations/utils.hpp @@ -140,6 +140,34 @@ template class RelationUtils { } } + /** + * @brief Calculate the contribution of each relation to the expected value of the full Honk relation. + * + * @details For each relation, use the purported values (supplied by the prover) of the multivariates to + * calculate a contribution to the purported value of the full Honk relation. These are stored in `evaluations`. + * Adding these together, with appropriate scaling factors, produces the expected value of the full Honk + * relation. This value is checked against the final value of the target total sum (called sigma_0 in the + * thesis). + */ + template + // TODO(#224)(Cody): Input should be an array? + inline static void accumulate_relation_evaluations_without_skipping(PolynomialEvaluations evaluations, + RelationEvaluations& relation_evaluations, + const Parameters& relation_parameters, + const FF& partial_evaluation_result) + { + using Relation = std::tuple_element_t; + + Relation::accumulate( + std::get(relation_evaluations), evaluations, relation_parameters, partial_evaluation_result); + + // Repeat for the next relation. + if constexpr (relation_idx + 1 < NUM_RELATIONS) { + accumulate_relation_evaluations( + evaluations, relation_evaluations, relation_parameters, partial_evaluation_result); + } + } + /** * @brief Calculate the contribution of each relation to the expected value of the full Honk relation. * @@ -157,8 +185,23 @@ template class RelationUtils { const FF& partial_evaluation_result) { using Relation = std::tuple_element_t; - Relation::accumulate( - std::get(relation_evaluations), evaluations, relation_parameters, partial_evaluation_result); + + // Check if the relation is skippable to speed up accumulation + if constexpr (!isSkippable || !std::is_same_v) { + // If not, accumulate normally + Relation::accumulate(std::get(relation_evaluations), + evaluations, + relation_parameters, + partial_evaluation_result); + } else { + // If so, only compute the contribution if the relation is active + if (!Relation::skip(evaluations)) { + Relation::accumulate(std::get(relation_evaluations), + evaluations, + relation_parameters, + partial_evaluation_result); + } + } // Repeat for the next relation. if constexpr (relation_idx + 1 < NUM_RELATIONS) { diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/standard_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/standard_circuit_builder.hpp index a421a10adaa1..e6ede171ffa8 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/standard_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/standard_circuit_builder.hpp @@ -15,7 +15,6 @@ template class StandardCircuitBuilder_ : public CircuitBuilderBase using Arithmetization = StandardArith; using GateBlocks = typename Arithmetization::TraceBlocks; static constexpr size_t NUM_WIRES = Arithmetization::NUM_WIRES; - static constexpr size_t FIXED_BLOCK_SIZE = 0; // not used, for compatibility only // Keeping NUM_WIRES, at least temporarily, for backward compatibility static constexpr size_t program_width = Arithmetization::NUM_WIRES; static constexpr size_t num_selectors = Arithmetization::NUM_SELECTORS; diff --git a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp index 90dde82d76c0..feecbf0938d4 100644 --- a/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp +++ b/barretenberg/cpp/src/barretenberg/stdlib_circuit_builders/ultra_circuit_builder.hpp @@ -33,7 +33,6 @@ class UltraCircuitBuilder_ : public CircuitBuilderBase class ProverInstance_ { // If using a structured trace, ensure that no block exceeds the fixed size if (is_structured) { - for (auto& block : circuit.blocks.get()) { - ASSERT(block.size() <= circuit.FIXED_BLOCK_SIZE); - } + circuit.blocks.check_within_fixed_sizes(); } // TODO(https://github.com/AztecProtocol/barretenberg/issues/905): This is adding ops to the op queue but NOT to @@ -109,8 +107,7 @@ template class ProverInstance_ { */ size_t compute_structured_dyadic_size(Circuit& builder) { - size_t num_blocks = builder.blocks.get().size(); - size_t minimum_size = num_blocks * builder.FIXED_BLOCK_SIZE; + size_t minimum_size = builder.blocks.get_total_structured_size(); return builder.get_circuit_subgroup_size(minimum_size); } diff --git a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp index c1ac763379ab..51bdf4466058 100644 --- a/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp +++ b/barretenberg/cpp/src/barretenberg/sumcheck/sumcheck_round.hpp @@ -336,7 +336,8 @@ template class SumcheckVerifierRound { const bb::PowPolynomial& pow_polynomial, const RelationSeparator alpha) { - Utils::template accumulate_relation_evaluations<>( + // The verifier should never skip computation of contributions from any relation + Utils::template accumulate_relation_evaluations_without_skipping<>( purported_evaluations, relation_evaluations, relation_parameters, pow_polynomial.partial_evaluation_result); auto running_challenge = FF(1); diff --git a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp index dc5f8e76dc08..d33607a6bdbc 100644 --- a/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp +++ b/barretenberg/cpp/src/barretenberg/ultra_honk/goblin_ultra_composer.test.cpp @@ -88,7 +88,6 @@ TEST_F(GoblinUltraHonkComposerTests, BasicStructured) // Construct and verify Honk proof using a structured trace bool structured = true; auto instance = std::make_shared>(builder, structured); - builder.blocks.summarize(); GoblinUltraProver prover(instance); auto verification_key = std::make_shared(instance->proving_key); GoblinUltraVerifier verifier(verification_key);