Skip to content

Commit fd31fc9

Browse files
committed
cleanup
1 parent 8ab633f commit fd31fc9

11 files changed

Lines changed: 175 additions & 331 deletions

File tree

crypto/crypto/src/merkle_tree/merkle.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,9 @@ where
168168
})
169169
}
170170

171-
/// Create a root-only Merkle tree placeholder: stores the commitment root
172-
/// but no nodes. Used when the tree's authentication paths are gathered from
173-
/// a device-resident copy (GPU) instead of this host tree, so the host nodes
174-
/// are never materialised (saving the full-tree Device→Host copy).
171+
/// Create a root only Merkle tree placeholder: stores the commitment root
172+
/// but no nodes. Used when paths are gathered from a device resident copy
173+
/// (GPU) instead of this host tree, so the host nodes are never built.
175174
/// [`get_proof_by_pos`](Self::get_proof_by_pos) must NOT be called on it.
176175
pub fn from_root(root: B::Node) -> Self {
177176
MerkleTree {
@@ -255,9 +254,9 @@ where
255254
/// For example, give me an inclusion proof for the 3rd element in the
256255
/// Merkle tree
257256
pub fn get_proof_by_pos(&self, pos: usize) -> Option<Proof<B::Node>> {
258-
// A root-only tree (from `from_root`) has no nodes to walk — callers
259-
// must gather paths from the device-resident copy instead. Catch the
260-
// misuse early in debug builds rather than returning a misleading `None`.
257+
// A root only tree (from `from_root`) has no nodes to walk. Callers must
258+
// gather paths from the device resident copy instead. Catch the misuse
259+
// early in debug builds rather than returning a misleading None.
261260
debug_assert!(
262261
!self.nodes.is_empty(),
263262
"get_proof_by_pos called on a root-only MerkleTree (no nodes)"

crypto/math-cuda/src/device.rs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,13 @@ fn retain_default_mempool(ctx: &CudaContext) {
214214
}
215215
}
216216

217-
/// Device VRAM budget (bytes) for table-session admission control.
217+
/// Device VRAM budget in bytes for table session admission control.
218218
///
219-
/// `LAMBDA_VM_VRAM_BUDGET_MB` overrides it explicitly — used to force-exercise
220-
/// the throttle in tests/benchmarks. Otherwise it is 80% of total device
221-
/// memory, leaving headroom for the context, module code, and the pool's
222-
/// retained blocks. On any query failure it returns `u64::MAX`, which disables
223-
/// budgeting: admission then falls back to the core-bound chunk size alone.
219+
/// LAMBDA_VM_VRAM_BUDGET_MB overrides it (used to force the throttle in tests).
220+
/// Otherwise it is 80% of total device memory, leaving headroom for the
221+
/// context, module code, and retained pool blocks. Returns u64::MAX on any
222+
/// query failure, which disables budgeting (chunks fall back to the core bound
223+
/// size alone).
224224
fn detect_vram_budget_bytes(ctx: &CudaContext) -> u64 {
225225
if let Ok(mb) = std::env::var("LAMBDA_VM_VRAM_BUDGET_MB")
226226
&& let Ok(mb) = mb.parse::<u64>()
@@ -255,17 +255,15 @@ impl Backend {
255255
// before returning), so the tracking is pure overhead. Disable it.
256256
unsafe { ctx.disable_event_tracking() };
257257

258-
// Retain freed device memory in the stream-ordered pool for reuse.
258+
// Retain freed device memory in the stream ordered pool for reuse.
259259
//
260-
// cudarc routes `CudaStream::alloc*` through `cuMemAllocAsync`, which
261-
// draws from the device's default memory pool. That pool's release
262-
// threshold defaults to 0, so every freed buffer is handed back to the
263-
// OS at the next sync — meaning the prover's large, repeatedly-shaped
264-
// LDE / FRI buffers are re-malloc'd from scratch each op. Raising the
265-
// threshold to "unbounded" keeps freed blocks resident in the pool so
266-
// subsequent allocations of the same size are satisfied without a real
267-
// driver allocation. Best-effort: on any error (no pool support, sync
268-
// allocator) we silently keep the current behaviour.
260+
// cudarc routes CudaStream::alloc* through cuMemAllocAsync, drawing from
261+
// the device default memory pool. Its release threshold defaults to 0,
262+
// so every freed buffer goes back to the OS at the next sync and the
263+
// prover's large LDE/FRI buffers are rebuilt from scratch each op.
264+
// Raising the threshold keeps freed blocks in the pool so a same size
265+
// allocation skips a real driver allocation. Best effort: on any error
266+
// we keep the current behaviour.
269267
retain_default_mempool(&ctx);
270268

271269
let arith = ctx.load_module(Ptx::from_src(ARITH_PTX))?;

crypto/math-cuda/src/fri.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ impl FriCommitState {
215215
};
216216

217217
// Keep the layer tree resident on device; copy only the 32-byte root so
218-
// R4 query openings gather paths on device instead of D2H'ing the tree.
218+
// R4 query openings gather paths on device instead of copying the tree.
219219
let mut root = [0u8; 32];
220220
self.stream.memcpy_dtoh(&nodes_dev.slice(0..32), &mut root)?;
221221
self.stream.synchronize()?;

crypto/math-cuda/src/lde.rs

Lines changed: 36 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -220,10 +220,9 @@ fn launch_pointwise_mul_batched(
220220
/// Layout: `m` columns, each `lde_size` u64s, column `c` at byte offset
221221
/// `c * lde_size * 8` within `buf`. Freed when `buf` Arc drops.
222222
///
223-
/// `tree` optionally carries the main-trace Merkle tree kept resident on device
224-
/// (populated by the keep path), so R4 query openings can gather authentication
225-
/// paths on device instead of D2H'ing the whole tree to host. `None` on the CPU
226-
/// path or when the tree wasn't retained.
223+
/// `tree` optionally carries the main trace Merkle tree kept resident on device
224+
/// (the keep path), so R4 query openings gather paths on device instead of
225+
/// copying the whole tree to host. None on the CPU path.
227226
#[derive(Clone)]
228227
pub struct GpuLdeBase {
229228
pub buf: Arc<CudaSlice<u64>>,
@@ -240,24 +239,22 @@ pub struct GpuLdeExt3 {
240239
pub buf: Arc<CudaSlice<u64>>,
241240
pub m: usize,
242241
pub lde_size: usize,
243-
/// Optionally the aux/composition Merkle tree kept resident on device (the
244-
/// keep path), so R4 openings gather paths on device instead of D2H'ing the
245-
/// whole tree. `None` on the CPU path or when not retained.
242+
/// Optionally the aux or composition Merkle tree kept resident on device
243+
/// (the keep path), so R4 openings gather paths on device. None otherwise.
246244
pub tree: Option<GpuMerkleTree>,
247245
}
248246

249-
/// Handle to a Merkle tree kept live on device after a commit, so query
250-
/// openings can gather authentication paths on device instead of D2H'ing the
251-
/// whole tree to host. Node layout matches the CPU tree
252-
/// (`crypto/crypto/src/merkle_tree`): `nodes[0..leaves_len-1]` are inner nodes
253-
/// (root at index 0), `nodes[leaves_len-1..]` are the leaves; each node is 32
247+
/// Merkle tree kept resident on device after a commit, so query openings gather
248+
/// paths on device instead of copying the whole tree to host. Node layout
249+
/// matches the CPU tree (`crypto/crypto/src/merkle_tree`): `nodes[0..leaves_len-1]`
250+
/// are inner nodes (root at 0), `nodes[leaves_len-1..]` are the leaves, each 32
254251
/// bytes. Freed when the `nodes` Arc drops.
255252
#[derive(Clone)]
256253
pub struct GpuMerkleTree {
257254
pub nodes: Arc<CudaSlice<u8>>,
258255
pub leaves_len: usize,
259-
/// The Merkle root (node 0), copied to host at build time (32 bytes) so the
260-
/// commitment is available without D2H'ing the whole tree.
256+
/// The Merkle root (node 0), copied to host at build time so the commitment
257+
/// is available without copying the whole tree.
261258
pub root: [u8; 32],
262259
}
263260

@@ -643,41 +640,11 @@ pub fn coset_lde_batch_base_into_with_leaf_hash(
643640
.map(|_| ())
644641
}
645642

646-
/// Like `coset_lde_batch_base_into_with_leaf_hash`, but also builds the full
647-
/// Merkle tree on device and returns the `2*lde_size - 1` node buffer back
648-
/// to the caller in `merkle_nodes_out` (byte length `(2*lde_size - 1) * 32`).
649-
///
650-
/// The leaf hashes are never exposed to the caller — they stay on device and
651-
/// feed straight into the pair-hash tree kernel, avoiding the
652-
/// pinned→pageable→pinned round-trip that the separate-step GPU tree build
653-
/// would pay.
654-
pub fn coset_lde_batch_base_into_with_merkle_tree(
655-
columns: &[&[u64]],
656-
blowup_factor: usize,
657-
weights: &[u64],
658-
outputs: &mut [&mut [u64]],
659-
merkle_nodes_out: &mut [u8],
660-
) -> Result<()> {
661-
coset_lde_batch_base_into_with_merkle_tree_inner(
662-
columns,
663-
blowup_factor,
664-
weights,
665-
outputs,
666-
Some(merkle_nodes_out),
667-
KeccakCommit::FullTree,
668-
false,
669-
false,
670-
)
671-
.map(|_| ())
672-
}
673-
674-
/// Fused LDE + leaf-hash + Merkle tree build, keeping **both** the LDE buffer
675-
/// and the Merkle tree resident on device (returned in `GpuLdeBase` + its
676-
/// `tree`, including the root). The host tree is **not** materialised — there is
677-
/// no `merkle_nodes_out` — so the whole-tree Device→Host copy is eliminated.
678-
/// Callers gather query authentication paths from the device tree instead
679-
/// (`crate::merkle::gather_merkle_paths_dev`) and use a root-only host tree for
680-
/// the commitment.
643+
/// Fused LDE, leaf hash, and Merkle tree build that keeps both the LDE buffer
644+
/// and the Merkle tree (with root) resident on device, returned in `GpuLdeBase`.
645+
/// The host tree is not built (no `merkle_nodes_out`), so the whole tree copy to
646+
/// host is eliminated. Callers gather query paths from the device tree
647+
/// (`crate::merkle::gather_merkle_paths_dev`) and use a root only host tree.
681648
pub fn coset_lde_batch_base_into_with_merkle_tree_keep(
682649
columns: &[&[u64]],
683650
blowup_factor: usize,
@@ -840,32 +807,32 @@ fn coset_lde_batch_base_into_with_merkle_tree_inner(
840807
crate::merkle::build_inner_tree_levels(stream.as_ref(), be, &mut nodes_dev, lde_size)?;
841808
}
842809

843-
// D2H the LDE columns via pinned staging (still read on host by constraint
844-
// eval / openings). The full tree nodes are D2H'd only when the caller asks
845-
// for a host tree (`nodes_out`); the device-resident path passes `None` and
846-
// keeps the tree on device, eliminating the whole-tree D2H.
810+
// Copy the LDE columns to host via pinned staging (constraint eval and
811+
// openings still read them). The full tree is copied only when the caller
812+
// wants a host tree (`nodes_out`); the resident path passes None and keeps
813+
// the tree on device.
847814
stream.memcpy_dtoh(&buf, &mut pinned[..m * lde_size])?;
848815
if let Some(no) = nodes_out {
849816
d2h_bytes_via_pinned_hashes(&stream, be, &nodes_dev, no)?;
850817
}
851-
// Ensure the (async) LDE D2H above has landed before reading `pinned`.
852-
// Previously the tree D2H's `synchronize` covered this; the device-resident
853-
// path skips that D2H, so synchronize explicitly.
818+
// Make sure the async LDE copy above landed before reading `pinned`. The
819+
// tree copy used to provide this sync; the resident path skips it, so sync
820+
// here.
854821
stream.synchronize()?;
855822

856-
// Copy pinned into caller outputs. Runs under the pinned-staging lock,
823+
// Copy pinned into caller outputs. Runs under the pinned staging lock,
857824
// where rayon can deadlock. See `Backend::pinned_staging`.
858825
for (c, dst) in outputs.iter_mut().enumerate() {
859826
dst.copy_from_slice(&pinned[c * lde_size..c * lde_size + lde_size]);
860827
}
861828
drop(staging);
862829

863830
if keep_device_buf {
864-
// Retain the device tree nodes for on-device opening (FullTree only; in
865-
// LeavesOnly mode there are no inner nodes to gather paths from).
831+
// Retain the device tree for on device opening (FullTree only; LeavesOnly
832+
// has no inner nodes to gather paths from).
866833
let tree = if keep_tree && commit == KeccakCommit::FullTree {
867-
// Copy just the 32-byte root (node 0) so the commitment is available
868-
// without D2H'ing the whole tree.
834+
// Copy just the 32 byte root (node 0) so the commitment is available
835+
// without copying the whole tree.
869836
let mut root = [0u8; 32];
870837
stream.memcpy_dtoh(&nodes_dev.slice(0..32), &mut root)?;
871838
stream.synchronize()?;
@@ -891,60 +858,10 @@ fn coset_lde_batch_base_into_with_merkle_tree_inner(
891858
}
892859
}
893860

894-
/// Ext3 variant of `coset_lde_batch_base_into_with_leaf_hash`: fused
895-
/// LDE + Keccak-256 leaf hashing over ext3 columns. Thin wrapper over
896-
/// `coset_lde_batch_ext3_into_with_merkle_tree_inner` with `LeavesOnly`.
897-
pub fn coset_lde_batch_ext3_into_with_leaf_hash(
898-
columns: &[&[u64]],
899-
n: usize,
900-
blowup_factor: usize,
901-
weights: &[u64],
902-
outputs: &mut [&mut [u64]],
903-
hashed_leaves_out: &mut [u8],
904-
) -> Result<()> {
905-
coset_lde_batch_ext3_into_with_merkle_tree_inner(
906-
columns,
907-
n,
908-
blowup_factor,
909-
weights,
910-
outputs,
911-
Some(hashed_leaves_out),
912-
KeccakCommit::LeavesOnly,
913-
false,
914-
false,
915-
)
916-
.map(|_| ())
917-
}
918-
919-
/// Ext3 variant of the fused `coset_lde_batch_base_into_with_merkle_tree`.
920-
/// LDE + leaf hashing + inner-tree build, all on device; D2Hs only the LDE
921-
/// evaluations and the full `2*lde_size - 1` node buffer.
922-
pub fn coset_lde_batch_ext3_into_with_merkle_tree(
923-
columns: &[&[u64]],
924-
n: usize,
925-
blowup_factor: usize,
926-
weights: &[u64],
927-
outputs: &mut [&mut [u64]],
928-
merkle_nodes_out: &mut [u8],
929-
) -> Result<()> {
930-
coset_lde_batch_ext3_into_with_merkle_tree_inner(
931-
columns,
932-
n,
933-
blowup_factor,
934-
weights,
935-
outputs,
936-
Some(merkle_nodes_out),
937-
KeccakCommit::FullTree,
938-
false,
939-
false,
940-
)
941-
.map(|_| ())
942-
}
943-
944861
/// Ext3 variant of [`coset_lde_batch_base_into_with_merkle_tree_keep`]: keeps
945-
/// both the de-interleaved LDE buffer and the Merkle tree (with root) resident
946-
/// on device. The host tree is **not** materialised (no `merkle_nodes_out`), so
947-
/// the whole-tree D2H is eliminated; openings gather paths from the device tree.
862+
/// both the deinterleaved LDE buffer and the Merkle tree (with root) resident on
863+
/// device. No host tree is built, so the whole tree copy to host is eliminated;
864+
/// openings gather paths from the device tree.
948865
pub fn coset_lde_batch_ext3_into_with_merkle_tree_keep(
949866
columns: &[&[u64]],
950867
n: usize,
@@ -1103,15 +1020,15 @@ fn coset_lde_batch_ext3_into_with_merkle_tree_inner(
11031020
crate::merkle::build_inner_tree_levels(stream.as_ref(), be, &mut nodes_dev, lde_size)?;
11041021
}
11051022

1106-
// D2H the LDE columns. The full tree nodes are D2H'd only when the caller
1107-
// asks for a host tree (`nodes_out`); the device-resident path passes `None`
1108-
// and keeps the tree on device, eliminating the whole-tree D2H.
1023+
// Copy the LDE columns to host. The full tree is copied only when the caller
1024+
// wants a host tree (`nodes_out`); the resident path passes None and keeps
1025+
// the tree on device.
11091026
stream.memcpy_dtoh(&buf, &mut pinned[..mb * lde_size])?;
11101027
if let Some(no) = nodes_out {
11111028
d2h_bytes_via_pinned_hashes(&stream, be, &nodes_dev, no)?;
11121029
}
1113-
// Ensure the (async) LDE D2H has landed before reading `pinned` (the tree
1114-
// D2H's synchronize used to cover this; the device-resident path skips it).
1030+
// Make sure the async LDE copy landed before reading `pinned` (the tree copy
1031+
// used to provide this sync; the resident path skips it).
11151032
stream.synchronize()?;
11161033

11171034
unpack_pinned_slabs_to_ext3(pinned, outputs, lde_size);

crypto/math-cuda/src/merkle.rs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -221,11 +221,10 @@ pub fn build_merkle_tree_on_device(hashed_leaves: &[u8]) -> Result<Vec<u8>> {
221221
/// Gather Merkle authentication paths on device for `positions` (leaf indices)
222222
/// against the resident tree `nodes_dev` (standard layout, `2*leaves_len-1`
223223
/// nodes of 32 bytes). Returns `positions.len() * depth * 32` bytes, where
224-
/// `depth = log2(leaves_len)`: query `q`'s path occupies bytes
225-
/// `[q*depth*32 .. (q+1)*depth*32]`, each 32-byte node a sibling from leaf to
226-
/// root — byte-for-byte the same nodes the CPU `MerkleTree::get_proof_by_pos`
227-
/// (`build_merkle_path`) collects. Runs on the caller's `stream` (pass the
228-
/// table's session stream so it shares the queue with the rest of R4).
224+
/// `depth = log2(leaves_len)`. Query `q`'s path is `[q*depth*32 ..
225+
/// (q+1)*depth*32]`, each 32 byte node a sibling from leaf to root. These are
226+
/// the same nodes the CPU `MerkleTree::get_proof_by_pos` collects. Runs on the
227+
/// caller's `stream` (pass the table's session stream).
229228
pub fn gather_merkle_paths_dev(
230229
nodes_dev: &CudaSlice<u8>,
231230
leaves_len: usize,
@@ -241,9 +240,9 @@ pub fn gather_merkle_paths_dev(
241240
"leaves_len must be a power of two >= 2"
242241
);
243242
let depth = leaves_len.trailing_zeros() as usize;
244-
// Guard the kernel's device reads: an out-of-range position would walk off
245-
// the node buffer (OOB device read). Positions are valid by construction;
246-
// this catches any caller bug before it becomes UB.
243+
// Guard the kernel's device reads: a position past leaves_len would walk
244+
// off the node buffer. Positions are valid by construction; this catches a
245+
// caller bug before it becomes an out of bounds device read.
247246
assert!(
248247
positions.iter().all(|&p| (p as usize) < leaves_len),
249248
"gather_merkle_paths_dev: leaf position >= leaves_len"
@@ -286,9 +285,9 @@ pub fn gather_merkle_paths_dev(
286285
///
287286
/// Returns `(2*(lde_size/2) - 1) * 32` bytes of tree nodes in the standard
288287
/// layout (root at byte offset 0, leaves in the tail).
289-
/// Build the composition-poly Merkle tree on device (leaves hash row-pairs, so
290-
/// `num_leaves = lde_size / 2`). Returns the device node buffer, the leaf count,
291-
/// and the stream it was built on. Shared by the host-D2H and device-keep
288+
/// Build the composition Merkle tree on device. Leaves hash row pairs, so
289+
/// `num_leaves = lde_size / 2`. Returns the device node buffer, the leaf count,
290+
/// and the stream it was built on. Shared by the host copy and device keep
292291
/// wrappers below.
293292
fn build_comp_poly_tree_nodes_dev(
294293
parts_interleaved: &[&[u64]],
@@ -366,8 +365,8 @@ pub fn build_comp_poly_tree_from_evals_ext3(parts_interleaved: &[&[u64]]) -> Res
366365

367366
/// Like [`build_comp_poly_tree_from_evals_ext3`] but keeps the tree nodes on
368367
/// device (returned as a [`crate::lde::GpuMerkleTree`] with its root), so R4
369-
/// composition openings gather authentication paths on device instead of
370-
/// D2H'ing the whole tree. `leaves_len = lde_size / 2` (row-pair leaves).
368+
/// composition openings gather paths on device instead of copying the whole
369+
/// tree to host. `leaves_len = lde_size / 2` (row pair leaves).
371370
pub fn build_comp_poly_tree_from_evals_ext3_keep(
372371
parts_interleaved: &[&[u64]],
373372
) -> Result<crate::lde::GpuMerkleTree> {

crypto/math-cuda/tests/merkle_gather.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Parity: GPU `gather_merkle_paths_dev` must produce, for each leaf position,
2-
//! the exact `merkle_path` the CPU `MerkleTree::get_proof_by_pos` returns
3-
//! same sibling-from-leaf-to-root order, byte-for-byte. This is the gate for
4-
//! gathering R4 query openings on device instead of D2H'ing the whole tree.
2+
//! the exact `merkle_path` the CPU `MerkleTree::get_proof_by_pos` returns: the
3+
//! same sibling order from leaf to root, byte for byte. This is the gate for
4+
//! gathering R4 query openings on device instead of copying the whole tree.
55
66
use crypto::merkle_tree::backends::field_element_vector::FieldElementVectorBackend;
77
use crypto::merkle_tree::merkle::MerkleTree;

crypto/stark/src/fri/fri_commitment.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ where
1515
pub merkle_tree: MerkleTree<B>,
1616
/// The layer's Merkle tree kept resident on device (GPU FRI commit path),
1717
/// so R4 query openings gather authentication paths on device. When set,
18-
/// `merkle_tree` is a root-only placeholder. `None` on the CPU path.
18+
/// `merkle_tree` is a root only placeholder. `None` on the CPU path.
1919
#[cfg(feature = "cuda")]
2020
pub gpu_tree: Option<math_cuda::lde::GpuMerkleTree>,
2121
}

0 commit comments

Comments
 (0)