25#include <benchmark/benchmark.h>
47 return { ActiveBlock{ .start = 0, .size = round_size } };
52 constexpr size_t NUM_BLOCKS = 18;
53 constexpr size_t ACTIVE_PERCENT = 65;
54 const size_t total_pairs = round_size / 2;
55 const size_t active_pairs =
std::max<size_t>(NUM_BLOCKS, total_pairs * ACTIVE_PERCENT / 100);
56 const size_t inactive_pairs = total_pairs - active_pairs;
57 const size_t base_block_pairs = active_pairs / NUM_BLOCKS;
58 const size_t extra_block_pairs = active_pairs % NUM_BLOCKS;
59 const size_t base_gap_pairs = inactive_pairs / NUM_BLOCKS;
60 const size_t extra_gap_pairs = inactive_pairs % NUM_BLOCKS;
63 blocks.reserve(NUM_BLOCKS);
64 size_t pair_cursor = 0;
65 for (
size_t block_idx = 0; block_idx < NUM_BLOCKS; ++block_idx) {
66 const size_t gap = base_gap_pairs + (block_idx < extra_gap_pairs ? 1 : 0);
68 const size_t block_pairs = base_block_pairs + (block_idx < extra_block_pairs ? 1 : 0);
69 blocks.push_back(ActiveBlock{ .start = pair_cursor * 2, .size = block_pairs * 2 });
70 pair_cursor += block_pairs;
75template <
typename FF> std::vector<FF> make_challenges(
const size_t log_n)
79 for (
size_t idx = 0; idx < log_n; ++idx) {
80 result.emplace_back(
static_cast<uint64_t
>(idx + 7));
85template <
typename Flavor>
class SyntheticPolynomials {
91 explicit SyntheticPolynomials(
const size_t size)
95 storage.emplace_back(size);
96 const FF value =
FF(
static_cast<uint64_t
>(poly_idx + 3));
97 for (
auto& coeff : storage.back().coeffs()) {
101 for (
auto [prover_poly, stored_poly] :
zip_view(polynomials.get_all(), storage)) {
102 prover_poly = stored_poly.share();
104 if constexpr (
requires(
ProverPolynomials& p) { p.row_skip_active_prefix_end =
size_t{}; }) {
105 polynomials.row_skip_active_prefix_end = size;
115template <
typename Flavor,
typename Edges>
void fill_extended_edges(Edges& edges)
118 size_t entity_idx = 0;
119 for (
auto& edge : edges.get_all()) {
120 const FF value =
FF(
static_cast<uint64_t
>(entity_idx + 11));
121 for (
auto& evaluation : edge.evaluations) {
128template <
typename Flavor>
auto make_relation_parameters()
133 params.eta_two =
FF(19);
134 params.eta_three =
FF(23);
135 params.beta =
FF(29);
136 params.gamma =
FF(31);
137 params.public_input_delta =
FF(37);
138 params.eccvm_set_permutation_delta =
FF(43);
142template <
typename Flavor>
auto make_subrelation_separators()
146 for (
size_t idx = 0; idx < alphas.size(); ++idx) {
147 alphas[idx] =
FF(
static_cast<uint64_t
>(idx + 101));
152template <
typename Flavor>
155 const auto& extended_edges,
162template <
typename Flavor>
void bench_accumulate_relations_only(benchmark::State& state)
166 typename Round::SumcheckTupleOfTuplesOfUnivariates accum{};
167 typename Round::ExtendedEdges edges;
168 fill_extended_edges<Flavor>(edges);
169 auto params = make_relation_parameters<Flavor>();
171 const FF scaling_factor =
FF(5);
173 for (
auto _ : state) {
174 accumulate_one_edge<Flavor>(round, accum, edges, params, scaling_factor);
175 benchmark::DoNotOptimize(accum);
182enum class Scheduler {
187template <
typename Flavor>
188void bench_sumcheck_loop_shape(benchmark::State& state,
const Scheduler scheduler,
const bool fragmented)
192 using Tuple =
typename Round::SumcheckTupleOfTuplesOfUnivariates;
194 const size_t log_n =
static_cast<size_t>(state.range(0));
195 const size_t round_size =
size_t{ 1 } << log_n;
196 constexpr size_t ROWS_PER_CHUNK = 64;
198 SyntheticPolynomials<Flavor> synthetic_polynomials(round_size);
199 auto& polynomials = synthetic_polynomials.polynomials;
200 auto params = make_relation_parameters<Flavor>();
201 const auto blocks = make_active_blocks(round_size, fragmented);
203 for (
auto _ : state) {
204 Round round(round_size);
207 if (scheduler == Scheduler::STATIC_BLOCKS) {
209 typename Round::ExtendedEdges extended_edges;
210 for (
const auto& block : blocks) {
211 const size_t iterations = block.size / 2;
212 for (
size_t i : chunk.range(iterations)) {
213 const size_t edge_idx = block.start + i * 2;
214 round.
extend_edges(extended_edges, polynomials, edge_idx);
215 accumulate_one_edge<Flavor>(
216 round, thread_accumulators[chunk.
thread_index], extended_edges, params,
FF(7));
222 for (
const auto& block : blocks) {
223 for (
size_t start = block.start; start < block.start + block.size; start += ROWS_PER_CHUNK) {
224 chunks.push_back(ActiveBlock{
226 .size = std::min(ROWS_PER_CHUNK, block.start + block.size - start),
231 std::atomic<size_t> next_chunk{ 0 };
233 thread_accumulators.resize(num_slots);
235 typename Round::ExtendedEdges extended_edges;
238 if (chunk_idx >= chunks.size()) {
241 const auto& chunk = chunks[chunk_idx];
242 for (
size_t edge_idx = chunk.start; edge_idx < chunk.start + chunk.size; edge_idx += 2) {
243 round.
extend_edges(extended_edges, polynomials, edge_idx);
244 accumulate_one_edge<Flavor>(
245 round, thread_accumulators[slot_idx], extended_edges, params,
FF(7));
252 for (
const auto& accum : thread_accumulators) {
255 benchmark::DoNotOptimize(total);
258 size_t active_edges = 0;
259 for (
const auto& block : blocks) {
260 active_edges += block.size;
262 state.counters[
"active_edge_pairs"] =
static_cast<double>(active_edges / 2);
263 state.counters[
"active_pct"] = 100.0 *
static_cast<double>(active_edges) /
static_cast<double>(round_size);
264 state.counters[
"blocks"] =
static_cast<double>(blocks.size());
267 state.counters[
"threads"] =
static_cast<double>(
get_num_cpus());
271 size_t relations = 0;
272 size_t subrelations = 0;
273 size_t heavy_period = 0;
276template <Scheduler scheduler>
void bench_nano_scheduler(benchmark::State& state)
279 const size_t log_n =
static_cast<size_t>(state.range(0));
280 const size_t rows =
size_t{ 1 } << log_n;
281 const bool imbalanced =
static_cast<bool>(state.range(1));
282 const auto blocks = make_active_blocks(rows,
true);
283 const NanoSpec spec{ .relations = 12, .subrelations = 36, .heavy_period = imbalanced ? 8UL : 1UL };
284 constexpr size_t ROWS_PER_CHUNK = 64;
286 auto do_row = [&](std::array<FF, 64>& accum,
const size_t row) {
287 const bool heavy = ((row / 2) % spec.heavy_period) == 0;
288 const size_t active_relations = heavy ? spec.relations : 2;
289 FF x =
FF(
static_cast<uint64_t
>((row & 255) + 3));
290 for (
size_t relation_idx = 0; relation_idx < active_relations; ++relation_idx) {
291 for (
size_t subrelation_idx = 0; subrelation_idx < spec.subrelations / spec.relations; ++subrelation_idx) {
292 x = x *
FF(
static_cast<uint64_t
>(relation_idx + 5)) +
FF(
static_cast<uint64_t
>(subrelation_idx + 7));
293 accum[relation_idx * 4 + subrelation_idx] += x;
298 for (
auto _ : state) {
300 for (
auto& accum : accumulators) {
304 if constexpr (scheduler == Scheduler::STATIC_BLOCKS) {
306 for (
const auto& block : blocks) {
307 const size_t iterations = block.size / 2;
308 for (
size_t i : chunk.range(iterations)) {
309 do_row(accumulators[chunk.
thread_index], block.start + i * 2);
315 for (
const auto& block : blocks) {
316 for (
size_t start = block.start; start < block.start + block.size; start += ROWS_PER_CHUNK) {
317 chunks.push_back(ActiveBlock{
319 .size = std::min(ROWS_PER_CHUNK, block.start + block.size - start),
324 std::atomic<size_t> next_chunk{ 0 };
326 accumulators.resize(num_slots);
330 if (chunk_idx >= chunks.size()) {
333 const auto& chunk = chunks[chunk_idx];
334 for (
size_t edge_idx = chunk.start; edge_idx < chunk.start + chunk.size; edge_idx += 2) {
335 do_row(accumulators[slot_idx], edge_idx);
340 benchmark::DoNotOptimize(accumulators);
343 state.counters[
"relations"] =
static_cast<double>(spec.relations);
344 state.counters[
"subrelations"] =
static_cast<double>(spec.subrelations);
345 state.counters[
"threads"] =
static_cast<double>(
get_num_cpus());
346 state.counters[
"imbalanced"] = imbalanced ? 1.0 : 0.0;
349template <
typename Flavor>
void bench_compute_univariate_round0(benchmark::State& state)
353 const size_t log_n =
static_cast<size_t>(state.range(0));
354 const size_t round_size =
size_t{ 1 } << log_n;
356 SyntheticPolynomials<Flavor> synthetic_polynomials(round_size);
357 auto params = make_relation_parameters<Flavor>();
358 auto alphas = make_subrelation_separators<Flavor>();
361 for (
auto _ : state) {
362 Round round(round_size);
364 benchmark::DoNotOptimize(
result);
369 state.counters[
"threads"] =
static_cast<double>(
get_num_cpus());
374std::vector<int> log_n_sweep()
376 const char* env =
std::getenv(
"SUMCHECK_BENCH_LOG_N");
377 if (env ==
nullptr) {
378 return { 15, 17, 19 };
380 std::vector<int> sizes;
381 std::string spec(env);
383 while (pos < spec.size()) {
384 size_t comma = spec.find(
',', pos);
385 const std::string token = spec.substr(pos, comma == std::string::npos ? std::string::npos : comma - pos);
386 if (!token.empty()) {
389 if (comma == std::string::npos) {
394 return sizes.empty() ? std::vector<int>{ 17 } : sizes;
397template <
typename Flavor>
398void register_flavor_benches(
const std::string&
name,
const bool fragmented,
const std::vector<int>& sizes)
400 benchmark::RegisterBenchmark((
name +
"/accumulate_relations_only").c_str(),
401 &bench_accumulate_relations_only<Flavor>)
403 auto* loop_static = benchmark::RegisterBenchmark((
name +
"/loop_static_blocks").c_str(),
404 &bench_sumcheck_loop_shape<Flavor>,
405 Scheduler::STATIC_BLOCKS,
407 auto* loop_stealing = benchmark::RegisterBenchmark((
name +
"/loop_chunk_stealing").c_str(),
408 &bench_sumcheck_loop_shape<Flavor>,
409 Scheduler::CHUNK_STEALING,
411 auto* univariate_round0 = benchmark::RegisterBenchmark((
name +
"/compute_univariate_round0").c_str(),
412 &bench_compute_univariate_round0<Flavor>);
413 for (
const int log_n : sizes) {
414 loop_static->Arg(log_n);
415 loop_stealing->Arg(log_n);
416 univariate_round0->Arg(log_n);
418 loop_static->UseRealTime()->Unit(benchmark::kMillisecond);
419 loop_stealing->UseRealTime()->Unit(benchmark::kMillisecond);
420 univariate_round0->UseRealTime()->Unit(benchmark::kMillisecond);
427 const std::vector<int> sizes = log_n_sweep();
428 register_flavor_benches<bb::MegaZKFlavor>(
"MegaZK",
false, sizes);
429 register_flavor_benches<bb::TranslatorShortMonomialFlavor>(
"TranslatorShort",
true, sizes);
430 register_flavor_benches<bb::ECCVMShortMonomialFlavor>(
"ECCVMShort",
false, sizes);
431 benchmark::RegisterBenchmark(
"Nano/static_blocks", &bench_nano_scheduler<Scheduler::STATIC_BLOCKS>)
435 ->Unit(benchmark::kMillisecond);
436 benchmark::RegisterBenchmark(
"Nano/chunk_stealing", &bench_nano_scheduler<Scheduler::CHUNK_STEALING>)
440 ->Unit(benchmark::kMillisecond);
442 benchmark::Initialize(&argc, argv);
443 benchmark::RunSpecifiedBenchmarks();
444 benchmark::Shutdown();
A container for the prover polynomials.
typename Curve::ScalarField FF
static constexpr size_t NUM_SUBRELATIONS
static constexpr size_t NUM_ALL_ENTITIES
static constexpr size_t NUM_RELATIONS
static constexpr void add_nested_tuples(Tuple &tuple_1, const Tuple &tuple_2)
Componentwise addition of nested tuples (tuples of tuples)
Imlementation of the Sumcheck prover round.
decltype(create_sumcheck_tuple_of_tuples_of_univariates< Relations >()) SumcheckTupleOfTuplesOfUnivariates
SumcheckRoundUnivariate compute_univariate(ProverPolynomialsOrPartiallyEvaluatedMultivariates &polynomials, const bb::RelationParameters< FF > &relation_parameters, const bb::GateSeparatorPolynomial< FF > &gate_separators, const SubrelationSeparators &alphas)
Return the evaluations of the round univariate at .
static void extend_edges(ExtendedEdges &extended_edges, const ProverPolynomialsOrPartiallyEvaluatedMultivariates &multivariates, const size_t edge_idx)
To compute the round univariate in Round , the prover first computes the values of Honk polynomials ...
std::array< FF, Flavor::NUM_SUBRELATIONS - 1 > SubrelationSeparators
void accumulate_relation_univariates_public(SumcheckTupleOfTuplesOfUnivariates &univariate_accumulators, const auto &extended_edges, const bb::RelationParameters< FF > &relation_parameters, const FF &scaling_factor)
typename ECCVMFlavor::ProverPolynomials ProverPolynomials
Entry point for Barretenberg command-line interface.
field< Bn254FrParams > fr
void parallel_for(size_t num_iterations, const std::function< void(size_t)> &func)
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
Container for parameters used by the grand product (permutation, lookup) Honk relations.
static constexpr field zero()