Skip to content

Commit e882aec

Browse files
authored
feat: merge-train/barretenberg (#22228)
BEGIN_COMMIT_OVERRIDE fix: verify accumulated pairing points in native ChonkVerifier (#22224) chore: enable _GLIBCXX_DEBUG in debug build presets (#22218) feat: add --memory_profile_out flag for Chonk memory profiling (#22145) fix: disable max capacity test in debug + tiny gate separator improvements (#22215) fix: WASM build for memory_profile.cpp (#22231) fix: translator audit fixes (#22242) fix: remove constexpr from functions using std::vector for _GLIBCXX_DEBUG compat (#22239) fix: pippenger edge case (#22256) fix: avoid dereferencing past-the-end vector iterators in serialize.hpp (#22261) chore: crypto primitives external audit response 0 (#22263) feat: switch memory profiling from peak RSS to live heap usage (#22266) fix: replace UB end-iterator dereference in serialize.hpp (#22262) fix: catch exceptions in ChonkBatchVerifier::batch_check (#22270) END_COMMIT_OVERRIDE
2 parents 5d69881 + 0990598 commit e882aec

File tree

57 files changed

+636
-136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+636
-136
lines changed

barretenberg/cpp/CLAUDE.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,42 @@ Typical workflow
155155
2. Build native code: `cd barretenberg/cpp && ./bootstrap.sh build_native`
156156
3. Check VKs: `cd scripts && ./test_chonk_standalone_vks_havent_changed.sh`
157157
4. If VKs changed intentionally: `./test_chonk_standalone_vks_havent_changed.sh --update_inputs`
158+
159+
## Example IVC inputs
160+
161+
Example IVC inputs (msgpack files) for `bb prove --scheme chonk` are generated by e2e benchmark tests. Run the full bootstrap from the repo root to populate them:
162+
163+
```bash
164+
cd $(git rev-parse --show-toplevel) && ./bootstrap.sh
165+
```
166+
167+
This creates `yarn-project/end-to-end/example-app-ivc-inputs-out/<flow>/ivc-inputs.msgpack`. The inputs are generated by the `build_bench` function in `yarn-project/end-to-end/bootstrap.sh`, which runs client flow tests with `CAPTURE_IVC_FOLDER` set. In CI, these are cached as `bb-chonk-captures-<hash>.tar.gz`.
168+
169+
## Memory profiling
170+
171+
The `--memory_profile_out <file>` flag on `bb prove` outputs a JSON array of RSS checkpoints at key proving stages (after alloc, trace, oink, sumcheck, accumulate) for each circuit, with circuit names and indices.
172+
173+
```bash
174+
cd barretenberg/cpp
175+
./build/bin/bb prove \
176+
--scheme chonk \
177+
--ivc_inputs_path <path-to>/ivc-inputs.msgpack \
178+
-o /tmp/proof-out \
179+
-v \
180+
--memory_profile_out /tmp/proof-out/memory_profile.json
181+
```
182+
183+
For a visual timeline of a single run, pipe verbose output to `plot_memory.py`:
184+
185+
```bash
186+
bb prove --scheme chonk ... -v 2>&1 | python3 scripts/plot_memory.py > memory.html
187+
```
188+
189+
The extraction script converts the JSON into dashboard benchmark entries (one overlaid line per circuit stage, tracked across commits):
190+
191+
```bash
192+
echo '[]' > /tmp/proof-out/benchmarks.bench.json
193+
python3 scripts/extract_memory_benchmarks.py /tmp/proof-out "app-proving/flow/native"
194+
```
195+
196+
In CI, this is integrated into `ci_benchmark_ivc_flows.sh` (native only) and uploaded to the benchmark dashboard.

barretenberg/cpp/CMakePresets.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@
9090
"CC": "clang-20",
9191
"CXX": "clang++-20",
9292
"CFLAGS": "-gdwarf-4",
93-
"CXXFLAGS": "-gdwarf-4",
93+
"CXXFLAGS": "-gdwarf-4 -D_GLIBCXX_DEBUG",
9494
"LDFLAGS": "-gdwarf-4"
9595
},
9696
"cacheVariables": {
@@ -162,7 +162,7 @@
162162
"binaryDir": "build-debug-fast",
163163
"environment": {
164164
"CFLAGS": "-O2 -gdwarf",
165-
"CXXFLAGS": "-O2 -gdwarf-4",
165+
"CXXFLAGS": "-O2 -gdwarf-4 -D_GLIBCXX_DEBUG",
166166
"LDFLAGS": "-O2 -gdwarf-4"
167167
},
168168
"cacheVariables": {

barretenberg/cpp/scripts/ci_benchmark_ivc_flows.sh

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,14 @@ function run_bb_cli_bench {
5959

6060
if [[ "$runtime" == "native" ]]; then
6161
# Add --bench_out_hierarchical flag for native builds to capture hierarchical op counts and timings
62-
memusage "./$native_build_dir/bin/bb" "$@" "--bench_out_hierarchical" "$output/benchmark_breakdown.json" || {
63-
echo "bb native failed with args: $@ --bench_out_hierarchical $output/benchmark_breakdown.json"
62+
memusage "./$native_build_dir/bin/bb" "$@" "--bench_out_hierarchical" "$output/benchmark_breakdown.json" "--memory_profile_out" "$output/memory_profile.json" || {
63+
echo "bb native failed with args: $@ --bench_out_hierarchical $output/benchmark_breakdown.json --memory_profile_out $output/memory_profile.json"
6464
exit 1
6565
}
6666
else # wasm
6767
export WASMTIME_ALLOWED_DIRS="--dir=$flow_folder --dir=$output"
6868
# Add --bench_out_hierarchical flag for wasm builds to capture hierarchical op counts and timings
69+
# Note: --memory_profile_out is native-only (getrusage not available in wasm)
6970
memusage scripts/wasmtime.sh $WASMTIME_ALLOWED_DIRS ./build-wasm-threads/bin/bb "$@" "--bench_out_hierarchical" "$output/benchmark_breakdown.json" || {
7071
echo "bb wasm failed with args: $@ --bench_out_hierarchical $output/benchmark_breakdown.json"
7172
exit 1
@@ -139,6 +140,12 @@ EOF
139140
echo "Extracting component timings from hierarchical breakdown..."
140141
python3 scripts/extract_component_benchmarks.py "$output" "$name_path"
141142
fi
143+
144+
# Extract memory profile metrics if available
145+
if [[ -f "$output/memory_profile.json" ]]; then
146+
echo "Extracting memory profile metrics..."
147+
python3 scripts/extract_memory_benchmarks.py "$output" "$name_path"
148+
fi
142149
}
143150

144151
export -f verify_ivc_flow run_bb_cli_bench
@@ -178,4 +185,17 @@ if [[ "${CI:-}" == "1" ]] && [[ "${CI_USE_BUILD_INSTANCE_KEY:-0}" == "1" ]]; the
178185
else
179186
echo "Warning: benchmark breakdown file not found at $benchmark_breakdown_file"
180187
fi
188+
189+
# Upload memory profile to S3
190+
memory_profile_file="bench-out/app-proving/$flow_name/$runtime/memory_profile.json"
191+
if [[ -f "$memory_profile_file" ]]; then
192+
tmp_memory_file="/tmp/memory_profile_${runtime}_${flow_name}_$$.json"
193+
cp "$memory_profile_file" "$tmp_memory_file"
194+
memory_disk_key="memory-${runtime}-${flow_name}-${current_sha}"
195+
{
196+
cat "$tmp_memory_file" | gzip | cache_s3_transfer_to "bench/bb-breakdown" "$memory_disk_key"
197+
rm -f "$tmp_memory_file"
198+
} &
199+
echo "Uploaded memory profile to S3: bench/bb-breakdown/$memory_disk_key"
200+
fi
181201
fi
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env python3
2+
"""Extracts memory profile metrics from a memory profile JSON and appends
3+
them to the benchmark JSON file as dashboard entries.
4+
5+
Usage: extract_memory_benchmarks.py <output_dir> <name_path>
6+
7+
The output_dir must contain:
8+
- memory_profile.json (memory profile data from bb --memory_profile_out)
9+
- benchmarks.bench.json (existing benchmark results to append to)
10+
11+
The memory profile JSON format is documented in memory_profile.cpp.
12+
"""
13+
import json
14+
import sys
15+
16+
if len(sys.argv) != 3:
17+
print(f"Usage: {sys.argv[0]} <output_dir> <name_path>", file=sys.stderr)
18+
sys.exit(1)
19+
20+
output_dir = sys.argv[1]
21+
name_path = sys.argv[2]
22+
23+
try:
24+
with open(f"{output_dir}/memory_profile.json", "r") as f:
25+
data = json.load(f)
26+
27+
entries = []
28+
29+
# RSS timeline: each checkpoint becomes a line on the per-commit dashboard chart
30+
# Stage ordering: prefix with sequence number so alphabetical sort = execution order
31+
STAGE_ORDER = {
32+
"after_alloc": "0_alloc",
33+
"after_trace": "1_trace",
34+
"after_oink": "2_oink",
35+
"after_sumcheck": "3_sumcheck",
36+
"after_accumulate": "4_accumulate",
37+
}
38+
# JSON is a flat array of checkpoints (msgpack-serialized from C++)
39+
for cp in data:
40+
circuit_name = cp.get("circuit_name", "")
41+
idx = cp["circuit_index"]
42+
stage = STAGE_ORDER.get(cp["stage"], cp["stage"])
43+
label = f"{idx:02d}_{circuit_name}_{stage}" if circuit_name else f"{idx:02d}_{stage}"
44+
entries.append({
45+
"name": f"{name_path}/{label}",
46+
"unit": "MB",
47+
"value": cp["heap_mb"],
48+
"extra": f"stacked:{name_path}/heap_over_stages"
49+
})
50+
51+
# Append to existing benchmarks file
52+
with open(f"{output_dir}/benchmarks.bench.json", "r") as f:
53+
existing = json.load(f)
54+
55+
existing.extend(entries)
56+
57+
with open(f"{output_dir}/benchmarks.bench.json", "w") as f:
58+
json.dump(existing, f, indent=2)
59+
60+
print(f"Extracted {len(entries)} memory profile metrics")
61+
except Exception as e:
62+
print(f"Warning: Could not extract memory profile: {e}", file=sys.stderr)

barretenberg/cpp/src/barretenberg/bb/cli.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "barretenberg/common/assert.hpp"
3030
#include "barretenberg/common/bb_bench.hpp"
3131
#include "barretenberg/common/get_bytecode.hpp"
32+
#include "barretenberg/common/memory_profile.hpp"
3233
#include "barretenberg/common/thread.hpp"
3334
#include "barretenberg/common/version.hpp"
3435
#include "barretenberg/dsl/acir_format/serde/index.hpp"
@@ -389,6 +390,15 @@ int parse_and_run_cli_command(int argc, char* argv[])
389390
"parent-child relationships) as json.")
390391
->group(advanced_group);
391392
};
393+
std::string memory_profile_out;
394+
const auto add_memory_profile_out_option = [&](CLI::App* subcommand) {
395+
return subcommand
396+
->add_option("--memory_profile_out",
397+
memory_profile_out,
398+
"Path to write memory profile data (polynomial breakdown by category, RSS "
399+
"checkpoints, CRS size) as json.")
400+
->group(advanced_group);
401+
};
392402

393403
/***************************************************************************************************************
394404
* Top-level flags
@@ -482,6 +492,7 @@ int parse_and_run_cli_command(int argc, char* argv[])
482492
add_print_bench_flag(prove);
483493
add_bench_out_option(prove);
484494
add_bench_out_hierarchical_option(prove);
495+
add_memory_profile_out_option(prove);
485496
add_storage_budget_option(prove);
486497
add_output_format_option(prove);
487498

@@ -811,6 +822,10 @@ int parse_and_run_cli_command(int argc, char* argv[])
811822
if (!flags.storage_budget.empty()) {
812823
storage_budget = parse_size_string(flags.storage_budget);
813824
}
825+
if (!memory_profile_out.empty()) {
826+
bb::detail::use_memory_profile = true;
827+
vinfo("Memory profiling enabled via --memory_profile_out");
828+
}
814829
if (print_bench || !bench_out.empty() || !bench_out_hierarchical.empty()) {
815830
bb::detail::use_bb_bench = true;
816831
vinfo("BB_BENCH enabled via --print_bench or --bench_out");
@@ -987,6 +1002,11 @@ int parse_and_run_cli_command(int argc, char* argv[])
9871002
bb::detail::GLOBAL_BENCH_STATS.serialize_aggregate_data_json(file);
9881003
}
9891004
#endif
1005+
if (!memory_profile_out.empty()) {
1006+
std::ofstream file(memory_profile_out);
1007+
bb::detail::GLOBAL_MEMORY_PROFILE.serialize_json(file);
1008+
vinfo("Memory profile written to ", memory_profile_out);
1009+
}
9901010
return 0;
9911011
}
9921012
if (check->parsed()) {

barretenberg/cpp/src/barretenberg/bbapi/bbapi_chonk.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "barretenberg/commitment_schemes/ipa/ipa.hpp"
66
#include "barretenberg/commitment_schemes/verification_key.hpp"
77
#include "barretenberg/common/log.hpp"
8+
#include "barretenberg/common/memory_profile.hpp"
89
#include "barretenberg/common/serialize.hpp"
910
#include "barretenberg/common/throw_or_abort.hpp"
1011
#include "barretenberg/dsl/acir_format/acir_format.hpp"
@@ -90,6 +91,9 @@ ChonkAccumulate::Response ChonkAccumulate::execute(BBApiRequest& request) &&
9091
}
9192

9293
info("ChonkAccumulate - accumulating circuit '", request.loaded_circuit_name, "'");
94+
if (detail::use_memory_profile) {
95+
detail::GLOBAL_MEMORY_PROFILE.set_circuit_name(request.loaded_circuit_name);
96+
}
9397
request.ivc_in_progress->accumulate(circuit, precomputed_vk);
9498
request.ivc_stack_depth++;
9599

barretenberg/cpp/src/barretenberg/benchmark/goblin_bench/eccvm.bench.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
#include <benchmark/benchmark.h>
22

3+
#include "barretenberg/commitment_schemes/ipa/ipa.hpp"
4+
#include "barretenberg/ecc/curves/bn254/fq.hpp"
35
#include "barretenberg/eccvm/eccvm_circuit_builder.hpp"
46
#include "barretenberg/eccvm/eccvm_prover.hpp"
57
#include "barretenberg/eccvm/eccvm_verifier.hpp"
8+
#include "barretenberg/srs/global_crs.hpp"
69

710
using namespace benchmark;
811
using namespace bb;
@@ -40,6 +43,9 @@ Builder generate_trace(size_t target_num_gates)
4043
op_queue->merge();
4144
}
4245

46+
using Fq = curve::BN254::BaseField;
47+
op_queue->append_hiding_op(Fq::random_element(), Fq::random_element());
48+
4349
Builder builder{ op_queue };
4450
return builder;
4551
}
@@ -63,12 +69,35 @@ void eccvm_prove(State& state) noexcept
6369
std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
6470
ECCVMProver prover(builder, prover_transcript);
6571
for (auto _ : state) {
66-
auto [proof, ipa_claim] = prover.construct_proof();
72+
auto [proof, opening_claim] = prover.construct_proof();
73+
auto ipa_transcript = std::make_shared<Transcript>();
74+
IPA<Flavor::Curve>::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
75+
};
76+
}
77+
78+
void eccvm_ipa(State& state) noexcept
79+
{
80+
size_t target_num_gates = 1 << static_cast<size_t>(state.range(0));
81+
Builder builder = generate_trace(target_num_gates);
82+
std::shared_ptr<Transcript> prover_transcript = std::make_shared<Transcript>();
83+
ECCVMProver prover(builder, prover_transcript);
84+
auto [proof, opening_claim] = prover.construct_proof();
85+
for (auto _ : state) {
86+
auto ipa_transcript = std::make_shared<Transcript>();
87+
IPA<Flavor::Curve>::compute_opening_proof(prover.key->commitment_key, opening_claim, ipa_transcript);
6788
};
6889
}
6990

7091
BENCHMARK(eccvm_generate_prover)->Unit(kMillisecond)->DenseRange(12, CONST_ECCVM_LOG_N);
7192
BENCHMARK(eccvm_prove)->Unit(kMillisecond)->DenseRange(12, CONST_ECCVM_LOG_N);
93+
BENCHMARK(eccvm_ipa)->Unit(kMillisecond)->DenseRange(12, CONST_ECCVM_LOG_N);
7294
} // namespace
7395

74-
BENCHMARK_MAIN();
96+
int main(int argc, char** argv)
97+
{
98+
bb::srs::init_file_crs_factory(bb::srs::bb_crs_path());
99+
benchmark::Initialize(&argc, argv);
100+
benchmark::RunSpecifiedBenchmarks();
101+
benchmark::Shutdown();
102+
return 0;
103+
}

barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator.test.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,7 @@ class BatchedHonkTranslatorTests : public ::testing::Test {
187187
// ── Round 3: translator Z_PERM, then joint alpha + gate challenges ────────
188188
m.add_entry(round, "Z_PERM", G);
189189
m.add_challenge(round, "Sumcheck:alpha");
190-
for (size_t i = 0; i < JOINT_LOG_N; ++i) {
191-
m.add_challenge(round, "Sumcheck:gate_challenge_" + std::to_string(i));
192-
}
190+
m.add_challenge(round, "Sumcheck:gate_challenge");
193191
round++;
194192

195193
// ── Round 4: Libra masking commitment + Sum ───────────────────────────────

barretenberg/cpp/src/barretenberg/chonk/batched_honk_translator/batched_honk_translator_prover.cpp

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,8 @@ void BatchedHonkTranslatorProver::execute_joint_sumcheck_rounds()
6969
const FF alpha = transcript->template get_challenge<FF>("Sumcheck:alpha");
7070

7171
// Draw joint gate challenges (17 total).
72-
std::vector<FF> gate_challenges(JOINT_LOG_N);
73-
for (size_t i = 0; i < JOINT_LOG_N; i++) {
74-
gate_challenges[i] = transcript->template get_challenge<FF>("Sumcheck:gate_challenge_" + std::to_string(i));
75-
}
72+
std::vector<FF> gate_challenges =
73+
transcript->template get_dyadic_powers_of_challenge<FF>("Sumcheck:gate_challenge", JOINT_LOG_N);
7674

7775
// Compute α^{K_H}: offset for translator subrelation separators.
7876
FF alpha_power_KH = FF(1);
@@ -95,12 +93,9 @@ void BatchedHonkTranslatorProver::execute_joint_sumcheck_rounds()
9593
MegaZKCommitmentKey small_ck(1 << (log_subgroup_size + 1));
9694
zk_sumcheck_data = ZKData(JOINT_LOG_N, transcript, small_ck);
9795

98-
// Gate separator polynomials:
99-
// MegaZK circuit uses gate_challenges[0..mega_zk_log_n-1] for beta_products (real rounds only).
100-
// During virtual rounds, only betas[] and partial_evaluation_result are accessed.
101-
// Translator uses all JOINT_LOG_N challenges.
102-
GateSeparatorPolynomial<FF> mega_zk_gate_sep(gate_challenges, mega_zk_log_n);
103-
GateSeparatorPolynomial<FF> translator_gate_sep(gate_challenges, JOINT_LOG_N);
96+
// Single gate separator for both circuits: beta_products has size 2^JOINT_LOG_N which covers
97+
// both the MegaZK real rounds (2^mega_zk_log_n) and translator rounds (2^JOINT_LOG_N).
98+
GateSeparatorPolynomial<FF> gate_sep(gate_challenges, JOINT_LOG_N);
10499

105100
// Round helper objects.
106101
MegaZKProverRound mega_zk_round(static_cast<size_t>(1) << mega_zk_log_n);
@@ -146,8 +141,7 @@ void BatchedHonkTranslatorProver::execute_joint_sumcheck_rounds()
146141
TranslatorFlavor::get_minicircuit_evaluations(translator_partial));
147142
}
148143
zk_sumcheck_data.update_zk_sumcheck_data(u, round_idx);
149-
mega_zk_gate_sep.partially_evaluate(u);
150-
translator_gate_sep.partially_evaluate(u);
144+
gate_sep.partially_evaluate(u);
151145
translator_round.round_size >>= 1;
152146
};
153147

@@ -159,13 +153,13 @@ void BatchedHonkTranslatorProver::execute_joint_sumcheck_rounds()
159153
auto do_round = [&](auto& hpolys, auto& tpolys, size_t round_idx) -> FF {
160154
U_joint = SumcheckRoundUnivariate::zero();
161155

162-
auto U_H = mega_zk_round.compute_univariate(hpolys, mega_zk_params, mega_zk_gate_sep, mega_zk_alphas);
156+
auto U_H = mega_zk_round.compute_univariate(hpolys, mega_zk_params, gate_sep, mega_zk_alphas);
163157
U_H += mega_zk_round.compute_disabled_contribution(
164-
hpolys, mega_zk_params, mega_zk_gate_sep, mega_zk_alphas, rdp, masking_tail);
158+
hpolys, mega_zk_params, gate_sep, mega_zk_alphas, rdp, masking_tail);
165159
U_joint += U_H;
166160

167-
auto U_T = translator_round.compute_univariate(
168-
tpolys, translator_relation_parameters, translator_gate_sep, translator_alphas);
161+
auto U_T =
162+
translator_round.compute_univariate(tpolys, translator_relation_parameters, gate_sep, translator_alphas);
169163
for (auto& eval : U_T.evaluations) {
170164
eval *= alpha_power_KH;
171165
}
@@ -235,13 +229,13 @@ void BatchedHonkTranslatorProver::execute_joint_sumcheck_rounds()
235229
for (size_t round_idx = mega_zk_log_n; round_idx < JOINT_LOG_N; round_idx++) {
236230
U_joint = SumcheckRoundUnivariate::zero();
237231

238-
auto U_H = mega_zk_round.compute_virtual_contribution(
239-
mega_zk_partial, mega_zk_params, mega_zk_gate_sep, mega_zk_alphas);
232+
auto U_H =
233+
mega_zk_round.compute_virtual_contribution(mega_zk_partial, mega_zk_params, gate_sep, mega_zk_alphas);
240234
U_H *= rdp_scalar;
241235
U_joint += U_H;
242236

243237
auto U_T = translator_round.compute_univariate(
244-
translator_partial, translator_relation_parameters, translator_gate_sep, translator_alphas);
238+
translator_partial, translator_relation_parameters, gate_sep, translator_alphas);
245239
for (auto& eval : U_T.evaluations) {
246240
eval *= alpha_power_KH;
247241
}

0 commit comments

Comments
 (0)