Skip to content

Commit 13c5abf

Browse files
authored
perf: avoid cloning CPU witness MLEs (#1342)
## Problem left-over from #923. CPU trace commit cloned large witness MLEs before proving, adding avoidable memory traffic on the prover hot path. ## Design Rationale Keep committed witness MLEs behind `Arc` and drain/transport ownership where possible, avoiding deep clones without changing proof semantics. ## Change Highlights - `ceno_zkvm`: return `Arc` witness MLEs from trace commit and consume structural MLEs during transport. - `ceno_zkvm`: keep GPU trait shape aligned while preserving existing GPU behavior. ## Benchmark / Performance Impact ### Operation | Operation | master (s) | this PR (s) | Improve (master -> this PR) | |-----------|------------|-------------|-----------------------------| | CPU proving, keccak e2e shard total | 6.942 | 6.596 | 4.98% faster | | GPU proving, keccak e2e shard total | 1.191 | 1.186 | 0.44% faster | ### Layer | Layer | master (s) | this PR (s) | Improve (master -> this PR) | |-------|------------|-------------|-----------------------------| | N/A: shard-level proving total measured | N/A | N/A | no regression observed | Benchmark command(s): ```sh cargo run --config net.git-fetch-with-cli=true --release --package ceno_zkvm --bin e2e -- --platform=ceno --max-cycle-per-shard=1600 examples/target/riscv32im-ceno-zkvm-elf/release/examples/keccak_syscall cargo run --config net.git-fetch-with-cli=true --features gpu --release --package ceno_zkvm --bin e2e -- --platform=ceno --max-cycle-per-shard=1600 examples/target/riscv32im-ceno-zkvm-elf/release/examples/keccak_syscall ``` Environment: local x86_64 Linux, release build, local `../ceno-gpu/cuda_hal` patch for GPU validation. raw data: - master: CPU shards `3.272s + 3.670s`; GPU shards `0.624s + 0.568s` - this PR: CPU shards `3.336s + 3.260s`; GPU shards `0.593s + 0.593s` ## Testing ```sh cargo check --config net.git-fetch-with-cli=true --package ceno_zkvm --bin e2e cargo run --config net.git-fetch-with-cli=true --release --package ceno_zkvm --bin e2e -- --platform=ceno --max-cycle-per-shard=1600 examples/target/riscv32im-ceno-zkvm-elf/release/examples/keccak_syscall cargo run --config net.git-fetch-with-cli=true --features gpu --release --package ceno_zkvm --bin e2e -- --platform=ceno --max-cycle-per-shard=1600 examples/target/riscv32im-ceno-zkvm-elf/release/examples/keccak_syscall ``` ## Risks and Rollout Low risk: prover-side ownership change only. Rollback is reverting the `Arc` witness-MLE plumbing. ## Follow-ups (optional) None. ## Copilot Reviewer Directive (keep this section) When Copilot reviews this PR, apply `.github/copilot-instructions.md` strictly.
1 parent a19018a commit 13c5abf

4 files changed

Lines changed: 19 additions & 21 deletions

File tree

ceno_zkvm/src/scheme/cpu/mod.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ impl<E: ExtensionField, PCS: PolynomialCommitmentScheme<E>> TraceCommitter<CpuBa
558558
&self,
559559
traces: BTreeMap<usize, witness::RowMajorMatrix<E::BaseField>>,
560560
) -> (
561-
Vec<MultilinearExtension<'a, E>>,
561+
Vec<ArcMultilinearExtension<'a, E>>,
562562
PCS::CommitmentWithWitness,
563563
PCS::Commitment,
564564
) {
@@ -576,22 +576,19 @@ impl<E: ExtensionField, PCS: PolynomialCommitmentScheme<E>> TraceCommitter<CpuBa
576576
let prover_param = &self.backend.pp;
577577
let pcs_data = PCS::batch_commit(prover_param, traces.into_values().collect_vec()).unwrap();
578578
let commit = PCS::get_pure_commitment(&pcs_data);
579-
let mles = PCS::get_arc_mle_witness_from_commitment(&pcs_data)
580-
.into_par_iter()
581-
.map(|mle| mle.as_ref().clone())
582-
.collect::<Vec<_>>();
579+
let mles = PCS::get_arc_mle_witness_from_commitment(&pcs_data);
583580

584581
(mles, pcs_data, commit)
585582
}
586583

587584
fn extract_witness_mles<'a, 'b>(
588585
&self,
589-
witness_mles: &'b mut Vec<<CpuBackend<E, PCS> as ProverBackend>::MultilinearPoly<'a>>,
586+
witness_mles: &'b mut Vec<Arc<<CpuBackend<E, PCS> as ProverBackend>::MultilinearPoly<'a>>>,
590587
_pcs_data: &'b <CpuBackend<E, PCS> as ProverBackend>::PcsData,
591588
) -> Box<
592589
dyn Iterator<Item = Arc<<CpuBackend<E, PCS> as ProverBackend>::MultilinearPoly<'a>>> + 'b,
593590
> {
594-
let iter = witness_mles.drain(..).map(Arc::new);
591+
let iter = witness_mles.drain(..);
595592
Box::new(iter)
596593
}
597594
}
@@ -1524,9 +1521,9 @@ impl<E: ExtensionField, PCS: PolynomialCommitmentScheme<E>> DeviceTransporter<Cp
15241521

15251522
fn transport_mles<'a>(
15261523
&self,
1527-
mles: &[MultilinearExtension<'a, E>],
1524+
mles: Vec<MultilinearExtension<'a, E>>,
15281525
) -> Vec<ArcMultilinearExtension<'a, E>> {
1529-
mles.iter().map(|mle| mle.clone().into()).collect_vec()
1526+
mles.into_iter().map(Arc::new).collect_vec()
15301527
}
15311528
}
15321529

ceno_zkvm/src/scheme/gpu/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,7 +1670,7 @@ impl<E: ExtensionField, PCS: PolynomialCommitmentScheme<E> + 'static>
16701670
&self,
16711671
traces: BTreeMap<usize, witness::RowMajorMatrix<E::BaseField>>,
16721672
) -> (
1673-
Vec<<GpuBackend<E, PCS> as ProverBackend>::MultilinearPoly<'a>>,
1673+
Vec<Arc<<GpuBackend<E, PCS> as ProverBackend>::MultilinearPoly<'a>>>,
16741674
<GpuBackend<E, PCS> as ProverBackend>::PcsData,
16751675
PCS::Commitment,
16761676
) {
@@ -1811,7 +1811,7 @@ impl<E: ExtensionField, PCS: PolynomialCommitmentScheme<E> + 'static>
18111811

18121812
fn extract_witness_mles<'a, 'b>(
18131813
&self,
1814-
_witness_mles: &'b mut Vec<<GpuBackend<E, PCS> as ProverBackend>::MultilinearPoly<'a>>,
1814+
_witness_mles: &'b mut Vec<Arc<<GpuBackend<E, PCS> as ProverBackend>::MultilinearPoly<'a>>>,
18151815
pcs_data: &'b <GpuBackend<E, PCS> as ProverBackend>::PcsData,
18161816
) -> Box<
18171817
dyn Iterator<Item = Arc<<GpuBackend<E, PCS> as ProverBackend>::MultilinearPoly<'a>>> + 'b,
@@ -3625,11 +3625,11 @@ impl<E: ExtensionField, PCS: PolynomialCommitmentScheme<E> + 'static>
36253625

36263626
fn transport_mles<'a>(
36273627
&self,
3628-
mles: &[MultilinearExtension<'a, E>],
3628+
mles: Vec<MultilinearExtension<'a, E>>,
36293629
) -> Vec<Arc<<GpuBackend<E, PCS> as ProverBackend>::MultilinearPoly<'a>>> {
36303630
let cuda_hal = get_cuda_hal().unwrap();
3631-
mles.iter()
3632-
.map(|mle| Arc::new(MultilinearExtensionGpu::from_ceno(&cuda_hal, mle)))
3631+
mles.into_iter()
3632+
.map(|mle| Arc::new(MultilinearExtensionGpu::from_ceno(&cuda_hal, &mle)))
36333633
.collect_vec()
36343634
}
36353635
}

ceno_zkvm/src/scheme/hal.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,15 @@ pub trait TraceCommitter<PB: ProverBackend> {
117117
&self,
118118
traces: BTreeMap<usize, witness::RowMajorMatrix<<PB::E as ExtensionField>::BaseField>>,
119119
) -> (
120-
Vec<PB::MultilinearPoly<'a>>,
120+
Vec<Arc<PB::MultilinearPoly<'a>>>,
121121
PB::PcsData,
122122
<PB::Pcs as PolynomialCommitmentScheme<PB::E>>::Commitment,
123123
);
124124

125125
/// Return an iterator over witness polynomials so backends can decide how to source them
126126
fn extract_witness_mles<'a, 'b>(
127127
&self,
128-
witness_mles: &'b mut Vec<PB::MultilinearPoly<'a>>,
128+
witness_mles: &'b mut Vec<Arc<PB::MultilinearPoly<'a>>>,
129129
pcs_data: &'b PB::PcsData, // used by GPU backend
130130
) -> Box<dyn Iterator<Item = Arc<PB::MultilinearPoly<'a>>> + 'b>;
131131
}
@@ -280,7 +280,7 @@ pub trait DeviceTransporter<PB: ProverBackend> {
280280

281281
fn transport_mles<'a>(
282282
&self,
283-
mles: &[MultilinearExtension<'a, PB::E>],
283+
mles: Vec<MultilinearExtension<'a, PB::E>>,
284284
) -> Vec<Arc<PB::MultilinearPoly<'a>>>;
285285
}
286286

ceno_zkvm/src/scheme/prover.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ impl<
476476
// commit to witness traces in batch
477477
#[cfg_attr(not(feature = "gpu"), allow(unused_mut))]
478478
let (witness_mles, witness_data, witin_commit): (
479-
Vec<PB::MultilinearPoly<'_>>,
479+
Vec<Arc<PB::MultilinearPoly<'_>>>,
480480
PB::PcsData,
481481
PCS::Commitment,
482482
) = {
@@ -490,7 +490,8 @@ impl<
490490
gpu_device,
491491
gpu_witness_traces,
492492
);
493-
let witness_mles = unsafe { std::mem::transmute(gpu_witness_mles) };
493+
drop(gpu_witness_mles);
494+
let witness_mles = Vec::new();
494495
let witness_data = unsafe { std::mem::transmute_copy(&gpu_witness_data) };
495496
std::mem::forget(gpu_witness_data);
496497
(witness_mles, witness_data, witin_commit)
@@ -894,7 +895,7 @@ impl<
894895
name_and_instances: Vec<(String, [usize; 2])>,
895896
structural_rmms: Vec<witness::RowMajorMatrix<E::BaseField>>,
896897
#[cfg(feature = "gpu")] witness_trace_rows: Vec<Option<usize>>,
897-
#[allow(unused_mut)] mut witness_mles: Vec<PB::MultilinearPoly<'data>>,
898+
#[allow(unused_mut)] mut witness_mles: Vec<Arc<PB::MultilinearPoly<'data>>>,
898899
witness_data: &PB::PcsData,
899900
mut fixed_mles: Vec<Arc<PB::MultilinearPoly<'data>>>,
900901
challenges: [E; 2],
@@ -967,7 +968,7 @@ impl<
967968
let structural_witness = info_span!("[ceno] transport_structural_witness")
968969
.in_scope(|| {
969970
let structural_mles = structural_rmm.to_mles();
970-
self.device.transport_mles(&structural_mles)
971+
self.device.transport_mles(structural_mles)
971972
});
972973
(witness_mle, structural_witness, None)
973974
};

0 commit comments

Comments
 (0)