Skip to content

Commit abd54fa

Browse files
committed
Build the prover without parallel again
`make compile-recursion-elfs` compiles `lambda-vm-prover` for the RISC-V guest, where `parallel` is off and there is no rayon. The three new phase modules `use rayon::prelude::*` unconditionally and call `into_par_iter`/`par_iter`, so the recursion guest stopped building: 27 errors, 8 unresolved-`rayon` and 9 missing-method, plus three `E0505`s in `trace_builder`. This is on #994's branch as it stands, not introduced by this PR — the same `cargo check -p lambda-vm-prover --no-default-features` fails identically at `f800e4b0`. It went unnoticed because no CI run has ever touched that branch; this PR is the first, which is how it surfaced. The four `make lint` arms do not catch it either: the workspace-level `--no-default-features` arm still resolves `parallel` through another member's feature unification. Gated with the idiom already used in `trace_builder.rs` — a `#[cfg]` pair around the iterator source, serial arm `into_iter`/`iter`. Where the closure was long enough that duplicating it would be worse than the problem, it is hoisted to a named binding first and both arms map over that, so the body appears once. No behaviour change on any path that runs today: the serial arms exist to compile for the guest, which links the crate for its verifier and never executes these phases. The `E0505`s were the serial arm of the BITWISE collector loop iterating `&collectors` where the parallel arm moves it into `units`, so the closures' borrows of the op lists outlived the point where `CollectedOps` moves those lists. Consumed by value, matching the parallel arm. Verified: `make compile-recursion-elfs` succeeds, all four `make lint` arms and `cargo fmt --check` pass, and the prove-and-retire tests are unchanged at 13/13.
1 parent daeef58 commit abd54fa

4 files changed

Lines changed: 131 additions & 88 deletions

File tree

‎prover/src/challenge_phase.rs‎

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
1818
use std::collections::HashMap;
1919

20+
#[cfg(feature = "parallel")]
2021
use rayon::prelude::*;
2122

2223
use crypto::fiat_shamir::default_transcript::DefaultTranscript;
@@ -159,12 +160,17 @@ fn assemble_roots(
159160
(&airs.register, &remaining.register, "REGISTER"),
160161
];
161162
// Small tables, many of them: one commit at a time leaves most cores idle.
162-
roots.extend(
163-
fixed
164-
.par_iter()
165-
.map(|(air, trace, name)| commit_resident(air, trace, name))
166-
.collect::<Result<Vec<_>, _>>()?,
167-
);
163+
#[cfg(feature = "parallel")]
164+
let fixed_roots = fixed
165+
.par_iter()
166+
.map(|(air, trace, name)| commit_resident(air, trace, name))
167+
.collect::<Result<Vec<_>, _>>()?;
168+
#[cfg(not(feature = "parallel"))]
169+
let fixed_roots = fixed
170+
.iter()
171+
.map(|(air, trace, name)| commit_resident(air, trace, name))
172+
.collect::<Result<Vec<_>, _>>()?;
173+
roots.extend(fixed_roots);
168174
if airs.include_halt {
169175
roots.push(commit_resident(&airs.halt, &remaining.halt, "HALT")?);
170176
}
@@ -184,12 +190,17 @@ fn assemble_roots(
184190
// PAGE is built from the ELF image rather than from an op list, so
185191
// it is never retired and is committed here with the rest.
186192
let pages: Vec<_> = page_airs.by_ref().collect();
187-
roots.extend(
188-
pages
189-
.par_iter()
190-
.map(|(air, trace)| commit_resident(air, trace, "PAGE"))
191-
.collect::<Result<Vec<_>, _>>()?,
192-
);
193+
#[cfg(feature = "parallel")]
194+
let page_roots = pages
195+
.par_iter()
196+
.map(|(air, trace)| commit_resident(air, trace, "PAGE"))
197+
.collect::<Result<Vec<_>, _>>()?;
198+
#[cfg(not(feature = "parallel"))]
199+
let page_roots = pages
200+
.iter()
201+
.map(|(air, trace)| commit_resident(air, trace, "PAGE"))
202+
.collect::<Result<Vec<_>, _>>()?;
203+
roots.extend(page_roots);
193204
continue;
194205
};
195206
for chunk in 0..count_for(table_counts, kind) {

‎prover/src/commit_phase.rs‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,18 +100,20 @@ fn commit_batch(
100100
roots: &std::sync::Mutex<Vec<ChunkCommitment>>,
101101
items: Vec<Item>,
102102
) -> Result<(), Error> {
103+
#[cfg(feature = "parallel")]
103104
use rayon::prelude::*;
104105
type P = stark::prover::Prover<GoldilocksField, GoldilocksExtension, ()>;
105-
let done: Result<Vec<ChunkCommitment>, Error> = items
106-
.into_par_iter()
107-
.map(|(kind, chunk, trace)| {
108-
<P as IsStarkProver<_, _, _>>::commit_table_root(airs.get(kind).as_ref(), &trace)
109-
.map(|root| (kind, chunk, root))
110-
.ok_or_else(|| {
111-
Error::Prover(format!("commit phase: no commitment for a {kind:?} chunk"))
112-
})
113-
})
114-
.collect();
106+
let commit = |(kind, chunk, trace): Item| -> Result<ChunkCommitment, Error> {
107+
<P as IsStarkProver<_, _, _>>::commit_table_root(airs.get(kind).as_ref(), &trace)
108+
.map(|root| (kind, chunk, root))
109+
.ok_or_else(|| {
110+
Error::Prover(format!("commit phase: no commitment for a {kind:?} chunk"))
111+
})
112+
};
113+
#[cfg(feature = "parallel")]
114+
let done: Result<Vec<ChunkCommitment>, Error> = items.into_par_iter().map(commit).collect();
115+
#[cfg(not(feature = "parallel"))]
116+
let done: Result<Vec<ChunkCommitment>, Error> = items.into_iter().map(commit).collect();
115117
roots.lock().expect("roots").extend(done?);
116118
Ok(())
117119
}

‎prover/src/logup_phase.rs‎

Lines changed: 92 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -97,28 +97,30 @@ fn rounds_batch(
9797
done: &Proofs,
9898
items: Vec<Item>,
9999
) -> Result<(), Error> {
100+
#[cfg(feature = "parallel")]
100101
use rayon::prelude::*;
101102
let order = &challenge.order;
102103
let n = order.len();
103-
let built: Result<Vec<_>, Error> = items
104-
.into_par_iter()
105-
.map(|(kind, chunk, mut trace)| {
106-
let idx = order.index_of(kind, chunk).ok_or_else(|| {
107-
Error::Prover(format!(
108-
"logup phase: {kind:?} chunk {chunk} is not in the layout the Commit phase produced"
109-
))
110-
})?;
111-
let mut transcript = fork(&challenge.transcript, idx, n);
112-
let rounds = prove_table(
113-
airs.get(kind).as_ref(),
114-
&mut trace,
115-
&challenge.challenges,
116-
&mut transcript,
117-
)
118-
.map_err(|e| Error::Prover(format!("logup phase: {kind:?} chunk {chunk}: {e}")))?;
119-
Ok((idx, rounds))
120-
})
121-
.collect();
104+
let run_one = |(kind, chunk, mut trace): Item| {
105+
let idx = order.index_of(kind, chunk).ok_or_else(|| {
106+
Error::Prover(format!(
107+
"logup phase: {kind:?} chunk {chunk} is not in the layout the Commit phase produced"
108+
))
109+
})?;
110+
let mut transcript = fork(&challenge.transcript, idx, n);
111+
let rounds = prove_table(
112+
airs.get(kind).as_ref(),
113+
&mut trace,
114+
&challenge.challenges,
115+
&mut transcript,
116+
)
117+
.map_err(|e| Error::Prover(format!("logup phase: {kind:?} chunk {chunk}: {e}")))?;
118+
Ok((idx, rounds))
119+
};
120+
#[cfg(feature = "parallel")]
121+
let built: Result<Vec<_>, Error> = items.into_par_iter().map(run_one).collect();
122+
#[cfg(not(feature = "parallel"))]
123+
let built: Result<Vec<_>, Error> = items.into_iter().map(run_one).collect();
122124
done.lock().expect("logup results").extend(built?);
123125
Ok(())
124126
}
@@ -235,6 +237,7 @@ fn assemble(
235237
resident: &mut Resident,
236238
challenge: &Challenge,
237239
) -> Result<Vec<StarkProof<GoldilocksField, GoldilocksExtension, ()>>, Error> {
240+
#[cfg(feature = "parallel")]
238241
use rayon::prelude::*;
239242
let order = &challenge.order;
240243
let airs = &challenge.airs;
@@ -299,10 +302,16 @@ fn assemble(
299302
.ok_or_else(|| Error::Prover(format!("logup phase: page {i} is not in the layout")))?;
300303
jobs.push((idx, air, trace));
301304
}
305+
#[cfg(feature = "parallel")]
302306
let built: Vec<(usize, StarkProof<GoldilocksField, GoldilocksExtension, ()>)> = jobs
303307
.into_par_iter()
304308
.map(|(idx, air, trace)| build(idx, air, trace).map(|proof| (idx, proof)))
305309
.collect::<Result<_, _>>()?;
310+
#[cfg(not(feature = "parallel"))]
311+
let built: Vec<(usize, StarkProof<GoldilocksField, GoldilocksExtension, ()>)> = jobs
312+
.into_iter()
313+
.map(|(idx, air, trace)| build(idx, air, trace).map(|proof| (idx, proof)))
314+
.collect::<Result<_, _>>()?;
306315
for (idx, proof) in built {
307316
slots[idx] = Some(proof);
308317
}
@@ -409,29 +418,31 @@ fn deep_batch(
409418
done: &Deeps,
410419
items: Vec<Item>,
411420
) -> Result<(), Error> {
421+
#[cfg(feature = "parallel")]
412422
use rayon::prelude::*;
413423
let order = &challenge.order;
414424
let n = order.len();
415-
let built: Result<Vec<_>, Error> = items
416-
.into_par_iter()
417-
.map(|(kind, chunk, mut trace)| {
418-
let idx = order.index_of(kind, chunk).ok_or_else(|| {
419-
Error::Prover(format!(
420-
"batched phase: {kind:?} chunk {chunk} is not in the layout"
421-
))
422-
})?;
423-
let mut transcript = fork(&challenge.transcript, idx, n);
424-
let deep = deep_of(
425-
airs.get(kind).as_ref(),
426-
&mut trace,
427-
&challenge.challenges,
428-
&mut transcript,
429-
challenge.roots.get(idx).cloned(),
430-
)
431-
.map_err(|e| Error::Prover(format!("batched phase: {kind:?} chunk {chunk}: {e}")))?;
432-
Ok((idx, deep))
433-
})
434-
.collect();
425+
let run_one = |(kind, chunk, mut trace): Item| {
426+
let idx = order.index_of(kind, chunk).ok_or_else(|| {
427+
Error::Prover(format!(
428+
"batched phase: {kind:?} chunk {chunk} is not in the layout"
429+
))
430+
})?;
431+
let mut transcript = fork(&challenge.transcript, idx, n);
432+
let deep = deep_of(
433+
airs.get(kind).as_ref(),
434+
&mut trace,
435+
&challenge.challenges,
436+
&mut transcript,
437+
challenge.roots.get(idx).cloned(),
438+
)
439+
.map_err(|e| Error::Prover(format!("batched phase: {kind:?} chunk {chunk}: {e}")))?;
440+
Ok((idx, deep))
441+
};
442+
#[cfg(feature = "parallel")]
443+
let built: Result<Vec<_>, Error> = items.into_par_iter().map(run_one).collect();
444+
#[cfg(not(feature = "parallel"))]
445+
let built: Result<Vec<_>, Error> = items.into_iter().map(run_one).collect();
435446
// Folded in a fixed order within the batch, so the sequence is a function
436447
// of the walk and not of which thread finished first.
437448
let mut built = built?;
@@ -548,6 +559,7 @@ pub fn run_batched(
548559
let airs = &challenge.airs;
549560
let n = order.len();
550561
{
562+
#[cfg(feature = "parallel")]
551563
use rayon::prelude::*;
552564
let mut state = done.lock().expect("fold state");
553565
let build = |idx: usize,
@@ -600,10 +612,16 @@ pub fn run_batched(
600612
})?;
601613
jobs.push((idx, air, trace));
602614
}
615+
#[cfg(feature = "parallel")]
603616
let deeps: Vec<(usize, Deep)> = jobs
604617
.into_par_iter()
605618
.map(|(idx, air, trace)| build(idx, air, trace))
606619
.collect::<Result<_, _>>()?;
620+
#[cfg(not(feature = "parallel"))]
621+
let deeps: Vec<(usize, Deep)> = jobs
622+
.into_iter()
623+
.map(|(idx, air, trace)| build(idx, air, trace))
624+
.collect::<Result<_, _>>()?;
607625
for (idx, deep) in deeps {
608626
fold_one(&mut state, idx, deep);
609627
}
@@ -714,30 +732,32 @@ fn open_batch(
714732
done: &Opens,
715733
items: Vec<Item>,
716734
) -> Result<(), Error> {
735+
#[cfg(feature = "parallel")]
717736
use rayon::prelude::*;
718737
let order = &challenge.order;
719738
let n = order.len();
720-
let built: Result<Vec<_>, Error> = items
721-
.into_par_iter()
722-
.map(|(kind, chunk, mut trace)| {
723-
let idx = order.index_of(kind, chunk).ok_or_else(|| {
724-
Error::Prover(format!(
725-
"open pass: {kind:?} chunk {chunk} is not in the layout"
726-
))
727-
})?;
728-
let mut transcript = fork(&challenge.transcript, idx, n);
729-
let opening = open_of(
730-
airs.get(kind).as_ref(),
731-
&mut trace,
732-
&challenge.challenges,
733-
&mut transcript,
734-
iotas_of(batched, idx)?,
735-
take_kept(batched, idx),
736-
)
737-
.map_err(|e| Error::Prover(format!("open pass: {kind:?} chunk {chunk}: {e}")))?;
738-
Ok((idx, opening))
739-
})
740-
.collect();
739+
let run_one = |(kind, chunk, mut trace): Item| {
740+
let idx = order.index_of(kind, chunk).ok_or_else(|| {
741+
Error::Prover(format!(
742+
"open pass: {kind:?} chunk {chunk} is not in the layout"
743+
))
744+
})?;
745+
let mut transcript = fork(&challenge.transcript, idx, n);
746+
let opening = open_of(
747+
airs.get(kind).as_ref(),
748+
&mut trace,
749+
&challenge.challenges,
750+
&mut transcript,
751+
iotas_of(batched, idx)?,
752+
take_kept(batched, idx),
753+
)
754+
.map_err(|e| Error::Prover(format!("open pass: {kind:?} chunk {chunk}: {e}")))?;
755+
Ok((idx, opening))
756+
};
757+
#[cfg(feature = "parallel")]
758+
let built: Result<Vec<_>, Error> = items.into_par_iter().map(run_one).collect();
759+
#[cfg(not(feature = "parallel"))]
760+
let built: Result<Vec<_>, Error> = items.into_iter().map(run_one).collect();
741761
done.lock().expect("openings").extend(built?);
742762
Ok(())
743763
}
@@ -868,12 +888,19 @@ pub fn run_open(
868888
jobs.push((idx, air, trace));
869889
}
870890
{
891+
#[cfg(feature = "parallel")]
871892
use rayon::prelude::*;
872-
opens.extend(
873-
jobs.into_par_iter()
874-
.map(|(idx, air, trace)| build(idx, air, trace))
875-
.collect::<Result<Vec<_>, _>>()?,
876-
);
893+
#[cfg(feature = "parallel")]
894+
let opened = jobs
895+
.into_par_iter()
896+
.map(|(idx, air, trace)| build(idx, air, trace))
897+
.collect::<Result<Vec<_>, _>>()?;
898+
#[cfg(not(feature = "parallel"))]
899+
let opened = jobs
900+
.into_iter()
901+
.map(|(idx, air, trace)| build(idx, air, trace))
902+
.collect::<Result<Vec<_>, _>>()?;
903+
opens.extend(opened);
877904
}
878905

879906
opens.sort_by_key(|(idx, _)| *idx);

‎prover/src/tables/trace_builder.rs‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4226,7 +4226,10 @@ fn build_traces<I: ImageSource + Sync>(
42264226
{
42274227
base.add_ops(&bitwise_ops);
42284228
memw_register::collect_bitwise_from_memw_register(&memw_register_rows, &mut base);
4229-
for f in &collectors {
4229+
// By value, like the parallel arm's `units.extend(collectors)`: these
4230+
// closures borrow the op lists, and the lists are moved into
4231+
// `CollectedOps` below, so the collectors have to be dropped here.
4232+
for f in collectors {
42304233
f(&mut base);
42314234
}
42324235
}

0 commit comments

Comments
 (0)