Skip to content

Commit 814c620

Browse files
committed
chore: address shuklaayush comments
1 parent 4c9cea5 commit 814c620

3 files changed

Lines changed: 34 additions & 31 deletions

File tree

crates/vm/src/system/cuda/memory.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub struct MemoryInventoryGPU {
3737
pub device_ctx: GpuDeviceCtx,
3838
pub boundary: BoundaryChipGPU,
3939
pub merkle_tree: MemoryMerkleTree,
40-
pub initial_memory: Vec<DeviceBuffer<u8>>,
40+
pub initial_memory: Vec<Arc<DeviceBuffer<u8>>>,
4141
pub merkle_records: Option<DeviceBuffer<u32>>,
4242
#[cfg(feature = "metrics")]
4343
pub(super) unpadded_merkle_height: usize,
@@ -97,15 +97,15 @@ impl MemoryInventoryGPU {
9797
addr_sp,
9898
raw_mem.len()
9999
);
100-
self.initial_memory.push(if raw_mem.is_empty() {
100+
self.initial_memory.push(Arc::new(if raw_mem.is_empty() {
101101
DeviceBuffer::new()
102102
} else {
103103
raw_mem
104104
.to_device_on(&self.device_ctx)
105105
.expect("failed to copy memory to device")
106-
});
106+
}));
107107
self.merkle_tree
108-
.build_async(&self.initial_memory[addr_sp], addr_sp);
108+
.build_async(self.initial_memory[addr_sp].clone(), addr_sp);
109109
}
110110
self.boundary.initial_leaves = self
111111
.initial_memory

crates/vm/src/system/cuda/merkle_tree/cuda.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use openvm_cuda_common::{
77
error::CudaError,
88
stream::{cudaStream_t, GpuDeviceCtx},
99
};
10+
use tracing::instrument;
1011

1112
use super::{SharedBuffer, DIGEST_WIDTH, MERKLE_TOUCHED_BLOCK_WIDTH};
1213

@@ -157,6 +158,7 @@ pub mod merkle_tree {
157158
}
158159

159160
#[allow(clippy::too_many_arguments)]
161+
#[instrument(name = "update_merkle_tree", skip_all)]
160162
pub unsafe fn update_merkle_tree<T>(
161163
trace: &DeviceMatrix<T>,
162164
subtree_ptrs: &DeviceBuffer<usize>,

crates/vm/src/system/cuda/merkle_tree/mod.rs

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,20 @@ pub struct MemoryMerkleSubTree {
5858
pub height: usize,
5959
pub path_len: usize,
6060
layout: MemoryMerkleSubTreeLayout,
61-
/// initial_data_ptr is a raw device pointer to the initial-memory buffer (`d_data`) captured
62-
/// in [`Self::build_async`], stored as a `usize` so the borrow is erased.
61+
/// Shared handle to the initial-memory buffer (`d_data`) captured in [`Self::build_async`].
62+
/// `None` for empty/dummy subtrees that have no backing buffer.
6363
///
64-
/// SAFETY / lifetime: under the `OmitBottomLevels` layout the omitted bottom levels are never
65-
/// materialized into `buf`; they are recomputed on demand from this buffer during
64+
/// Holding an `Arc` makes the subtree a co-owner of the buffer, so the host cannot free it
65+
/// while the subtree is alive. Under the `OmitBottomLevels` layout the omitted bottom levels
66+
/// are never materialized into `buf`; they are recomputed on demand from this buffer during
6667
/// [`MemoryMerkleTree::update_with_touched_blocks`] (see `recompute_omitted_node` in
67-
/// `merkle_tree.cu`). The pointed-to buffer must therefore stay alive and unmodified from the
68-
/// `build_async` call all the way until that update completes — not merely until the build
69-
/// kernel runs. The compiler cannot enforce this through the erased pointer, so the caller is
70-
/// responsible: in production the owning `DeviceBuffer`s are held in
71-
/// `MemoryInventoryGPU::initial_memory` and only dropped after `update_with_touched_blocks` is
72-
/// done.
73-
initial_data_ptr: usize,
68+
/// `merkle_tree.cu`), so the buffer must stay alive until that update completes.
69+
///
70+
/// NOTE: this only fixes host-side ownership. The buffer is also consumed by GPU kernels
71+
/// enqueued on the stream, so it must additionally outlive those kernels — that ordering is
72+
/// guaranteed by the `stream.synchronize()` in [`MemoryMerkleTree::drop_subtrees`], not by the
73+
/// `Arc`. Drop the subtrees (which releases these handles) only after that sync.
74+
initial_data: Option<Arc<DeviceBuffer<u8>>>,
7475
}
7576

7677
impl MemoryMerkleSubTree {
@@ -139,7 +140,7 @@ impl MemoryMerkleSubTree {
139140
buf,
140141
path_len,
141142
layout,
142-
initial_data_ptr: 0,
143+
initial_data: None,
143144
}
144145
}
145146

@@ -150,7 +151,7 @@ impl MemoryMerkleSubTree {
150151
buf: DeviceBuffer::new(),
151152
path_len: 0,
152153
layout: MemoryMerkleSubTreeLayout::Full,
153-
initial_data_ptr: 0,
154+
initial_data: None,
154155
}
155156
}
156157

@@ -171,15 +172,15 @@ impl MemoryMerkleSubTree {
171172
/// Here `addr_space_idx` is the address space _shifted_ by ADDR_SPACE_OFFSET = 1
172173
pub fn build_async(
173174
&mut self,
174-
d_data: &DeviceBuffer<u8>,
175+
d_data: Arc<DeviceBuffer<u8>>,
175176
addr_space_idx: usize,
176177
zero_hash: &DeviceBuffer<H>,
177178
device_ctx: &GpuDeviceCtx,
178179
) {
179180
let event = CudaEvent::new().unwrap();
180-
// Capture the raw device pointer; `d_data` must outlive `update_with_touched_blocks`,
181-
// which re-reads it under the `OmitBottomLevels` layout (see the `initial_data_ptr` field).
182-
self.initial_data_ptr = d_data.as_ptr() as usize;
181+
// Co-own the buffer; it must outlive `update_with_touched_blocks`, which re-reads it under
182+
// the `OmitBottomLevels` layout (see the `initial_data` field).
183+
self.initial_data = Some(d_data.clone());
183184
if self.buf.is_empty() {
184185
self.buf = DeviceBuffer::with_capacity_on(1, device_ctx);
185186
unsafe {
@@ -195,7 +196,7 @@ impl MemoryMerkleSubTree {
195196
} else {
196197
unsafe {
197198
build_merkle_subtree(
198-
d_data,
199+
&d_data,
199200
1 << self.stored_heap_height(),
200201
&self.buf,
201202
self.path_len,
@@ -340,8 +341,8 @@ impl MemoryMerkleTree {
340341
/// `OmitBottomLevels` layout, `d_data` is also re-read during
341342
/// [`Self::update_with_touched_blocks`] to recompute the omitted bottom levels, so it must
342343
/// remain valid until that update completes — not just until the build kernel runs. See
343-
/// [`MemoryMerkleSubTree`]'s `initial_data_ptr` field for details.
344-
pub fn build_async(&mut self, d_data: &DeviceBuffer<u8>, addr_space: usize) {
344+
/// [`MemoryMerkleSubTree`]'s `initial_data` field for details.
345+
pub fn build_async(&mut self, d_data: Arc<DeviceBuffer<u8>>, addr_space: usize) {
345346
if addr_space < ADDR_SPACE_OFFSET as usize {
346347
return;
347348
}
@@ -421,7 +422,7 @@ impl MemoryMerkleTree {
421422
let initial_data_ptrs = self
422423
.subtrees
423424
.iter()
424-
.map(|s| s.initial_data_ptr)
425+
.map(|s| s.initial_data.as_ref().map_or(0, |b| b.as_ptr() as usize))
425426
.collect::<Vec<_>>();
426427
let subtrees_pointers = self
427428
.subtrees
@@ -649,15 +650,15 @@ mod tests {
649650
.iter()
650651
.map(|mem| {
651652
let mem_slice = mem.as_slice();
652-
if !mem_slice.is_empty() {
653+
Arc::new(if !mem_slice.is_empty() {
653654
mem_slice.to_device_on(&gpu_merkle_tree.device_ctx).unwrap()
654655
} else {
655656
DeviceBuffer::new()
656-
}
657+
})
657658
})
658659
.collect::<Vec<_>>();
659660
for (i, mem_slice) in mem_slices.iter().enumerate() {
660-
gpu_merkle_tree.build_async(mem_slice, i);
661+
gpu_merkle_tree.build_async(mem_slice.clone(), i);
661662
}
662663
assert_eq!(
663664
gpu_merkle_tree.subtrees[RV64_REGISTER_AS as usize - 1].layout,
@@ -862,15 +863,15 @@ mod tests {
862863
.iter()
863864
.map(|mem| {
864865
let mem_slice = mem.as_slice();
865-
if !mem_slice.is_empty() {
866+
Arc::new(if !mem_slice.is_empty() {
866867
mem_slice.to_device_on(&gpu_merkle_tree.device_ctx).unwrap()
867868
} else {
868869
DeviceBuffer::new()
869-
}
870+
})
870871
})
871872
.collect::<Vec<_>>();
872873
for (i, mem_slice) in mem_slices.iter().enumerate() {
873-
gpu_merkle_tree.build_async(mem_slice, i);
874+
gpu_merkle_tree.build_async(mem_slice.clone(), i);
874875
}
875876
gpu_merkle_tree.finalize();
876877

0 commit comments

Comments
 (0)