Skip to content

Commit 2d197d5

Browse files
committed
Progress
1 parent 83ed0a7 commit 2d197d5

5 files changed

Lines changed: 181 additions & 113 deletions

File tree

candle-core/examples/metal_pool.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ fn run() -> Result<()> {
1515

1616
// Allocate a 4 MB pool for intermediate activations.
1717
let pooled = input.start_pool(4 * 1024 * 1024)?;
18+
println!("{input}");
19+
println!("{pooled}");
20+
println!("{}", pooled.sin()?);
1821

1922
let logits = pooled.sin()?.mul(&pooled.cos()?)?;
2023
let final_tensor = logits.tanh()?.leave_pool()?;

candle-core/src/metal_backend/device.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ impl MetalDevice {
244244
) -> Result<Arc<Buffer>> {
245245
let size_bytes = element_count * dtype.size_in_bytes();
246246
if let Some(pool) = current_pool() {
247-
let allocation = pool.allocate(size_bytes)?;
247+
let allocation = pool.allocate(size_bytes, dtype.size_in_bytes())?;
248248
let buffer = Arc::clone(allocation.buffer());
249249
register_pool_allocation(&buffer, allocation);
250250
Ok(buffer)

candle-core/src/metal_backend/mod.rs

Lines changed: 72 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -39,31 +39,33 @@ fn current_pool() -> Option<Arc<MetalTensorPool>> {
3939
POOL_STACK.with(|stack| stack.borrow().last().cloned().flatten())
4040
}
4141

42-
fn pool_registry() -> &'static Mutex<HashMap<usize, Arc<MetalPoolAllocation>>> {
43-
static REGISTRY: OnceLock<Mutex<HashMap<usize, Arc<MetalPoolAllocation>>>> = OnceLock::new();
42+
fn pool_registry() -> &'static Mutex<HashMap<usize, Vec<Arc<MetalPoolAllocation>>>> {
43+
static REGISTRY: OnceLock<Mutex<HashMap<usize, Vec<Arc<MetalPoolAllocation>>>>> =
44+
OnceLock::new();
4445
REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
4546
}
4647

4748
fn register_pool_allocation(buffer: &Arc<Buffer>, allocation: Arc<MetalPoolAllocation>) {
4849
let ptr = Arc::as_ptr(buffer) as usize;
4950
if let Ok(mut registry) = pool_registry().lock() {
50-
registry.insert(ptr, allocation);
51+
registry.entry(ptr).or_default().push(allocation);
5152
}
5253
}
5354

5455
fn take_pool_allocation(buffer: &Arc<Buffer>) -> Option<Arc<MetalPoolAllocation>> {
5556
let ptr = Arc::as_ptr(buffer) as usize;
56-
pool_registry()
57-
.lock()
58-
.ok()
59-
.and_then(|mut registry| registry.remove(&ptr))
57+
pool_registry().lock().ok().and_then(|mut registry| {
58+
let entry = registry.get_mut(&ptr)?;
59+
let allocation = entry.pop();
60+
if entry.is_empty() {
61+
registry.remove(&ptr);
62+
}
63+
allocation
64+
})
6065
}
6166

62-
pub fn buffer_o<'a>(buffer: &'a Buffer, l: &Layout, dtype: DType) -> BufferOffset<'a> {
63-
BufferOffset {
64-
buffer,
65-
offset_in_bytes: l.start_offset() * dtype.size_in_bytes(),
66-
}
67+
pub fn buffer_o<'a>(storage: &'a MetalStorage, l: &Layout) -> BufferOffset<'a> {
68+
storage.buffer_offset(l)
6769
}
6870
/// Simple way to catch lock error without
6971
/// depending on T
@@ -123,6 +125,7 @@ pub struct MetalStorage {
123125
count: usize,
124126
/// The dtype is kept since buffers are untyped.
125127
dtype: DType,
128+
offset_bytes: usize,
126129
pool_allocation: Option<Arc<MetalPoolAllocation>>,
127130
}
128131

@@ -169,7 +172,7 @@ impl BackendStorage for MetalStorage {
169172

170173
let buffer = device.new_buffer(el, self.dtype, "affine")?;
171174
let command_buffer = self.device.command_buffer()?;
172-
let src = buffer_o(&self.buffer, layout, dtype);
175+
let src = buffer_o(self, layout);
173176
if layout.is_contiguous() {
174177
let name = match self.dtype {
175178
DType::F32 => "affine_f32",
@@ -225,7 +228,7 @@ impl BackendStorage for MetalStorage {
225228

226229
let buffer = device.new_buffer(el, self.dtype, "powf")?;
227230
let command_buffer = self.device.command_buffer()?;
228-
let src = buffer_o(&self.buffer, layout, dtype);
231+
let src = buffer_o(self, layout);
229232
if layout.is_contiguous() {
230233
let name = match self.dtype {
231234
DType::F32 => "powf_f32",
@@ -277,7 +280,7 @@ impl BackendStorage for MetalStorage {
277280

278281
let buffer = device.new_buffer(el, self.dtype, "elu")?;
279282
let command_buffer = self.device.command_buffer()?;
280-
let src = buffer_o(&self.buffer, layout, self.dtype);
283+
let src = buffer_o(self, layout);
281284
if layout.is_contiguous() {
282285
let name = match self.dtype {
283286
DType::F32 => "elu_f32",
@@ -386,7 +389,7 @@ impl BackendStorage for MetalStorage {
386389
let dtype = if return_index { DType::U32 } else { self.dtype };
387390
let buffer = device.new_buffer(dst_el, dtype, "reduce")?;
388391
let command_buffer = self.device.command_buffer()?;
389-
let src = buffer_o(&self.buffer, layout, self.dtype);
392+
let src = buffer_o(self, layout);
390393
candle_metal_kernels::call_reduce_contiguous(
391394
&device.device,
392395
&command_buffer,
@@ -441,7 +444,7 @@ impl BackendStorage for MetalStorage {
441444
let dtype = if return_index { DType::U32 } else { self.dtype };
442445
let buffer = device.new_buffer(dst_el, dtype, "reduce")?;
443446
let command_buffer = self.device.command_buffer()?;
444-
let src = buffer_o(&self.buffer, layout, self.dtype);
447+
let src = buffer_o(self, layout);
445448
candle_metal_kernels::call_reduce_strided(
446449
&device.device,
447450
&command_buffer,
@@ -484,7 +487,7 @@ impl BackendStorage for MetalStorage {
484487
let el_count = shape.elem_count();
485488
let command_buffer = device.command_buffer()?;
486489
command_buffer.set_label("const-set");
487-
let dst = buffer_o(&self_.buffer, l, self_.dtype);
490+
let dst = buffer_o(self_, l);
488491

489492
match (el_count % 2, dtype, l.is_contiguous()) {
490493
(0, DType::BF16 | DType::F16, true) => {
@@ -585,7 +588,7 @@ impl BackendStorage for MetalStorage {
585588
let el_count = shape.elem_count();
586589
let buffer = device.new_buffer(el_count, dtype, "todtype")?;
587590
let command_buffer = device.command_buffer()?;
588-
let src = buffer_o(&self.buffer, layout, self.dtype);
591+
let src = buffer_o(self, layout);
589592
if layout.is_contiguous() {
590593
let kernel_name = match (self.dtype, dtype) {
591594
(DType::U32, DType::BF16) => "cast_u32_bf16",
@@ -705,7 +708,7 @@ impl BackendStorage for MetalStorage {
705708
let buffer = device.new_buffer(el_count, dtype, B::KERNEL)?;
706709
let command_buffer = device.command_buffer()?;
707710
command_buffer.set_label(B::KERNEL);
708-
let src = buffer_o(&self.buffer, layout, self.dtype);
711+
let src = buffer_o(self, layout);
709712

710713
match (el_count % 2, dtype, layout.is_contiguous()) {
711714
(0, DType::BF16 | DType::F16, true) => {
@@ -983,9 +986,9 @@ impl BackendStorage for MetalStorage {
983986
(DType::U8, DType::U8) => "where_u8_u8",
984987
(left, right) => crate::bail!("Metal where_cond {left:?} {right:?} not implemented"),
985988
};
986-
let src = buffer_o(&self.buffer, layout, self.dtype);
987-
let t = buffer_o(&t.buffer, t_l, t.dtype);
988-
let f = buffer_o(&f.buffer, f_l, f.dtype);
989+
let src = buffer_o(self, layout);
990+
let t = buffer_o(t, t_l);
991+
let f = buffer_o(f, f_l);
989992
candle_metal_kernels::call_where_cond_strided(
990993
&device.device,
991994
&command_buffer,
@@ -1031,7 +1034,7 @@ impl BackendStorage for MetalStorage {
10311034
DType::F32 => "im2col1d_f32",
10321035
dtype => crate::bail!("Metal conv1d {dtype:?} not implemented"),
10331036
};
1034-
let src = buffer_o(&self.buffer, layout, self.dtype);
1037+
let src = buffer_o(self, layout);
10351038
candle_metal_kernels::call_im2col1d_strided(
10361039
&self.device.device,
10371040
&command_buffer,
@@ -1044,13 +1047,7 @@ impl BackendStorage for MetalStorage {
10441047
&dst,
10451048
)
10461049
.map_err(MetalError::from)?;
1047-
let col = Self {
1048-
buffer: dst,
1049-
device,
1050-
count: dst_el,
1051-
dtype: self.dtype,
1052-
pool_allocation: None,
1053-
};
1050+
let col = Self::new(dst, device, dst_el, self.dtype);
10541051
let l_out = params.l_out();
10551052
let b = params.b_size;
10561053
let n = params.c_out;
@@ -1224,7 +1221,7 @@ impl BackendStorage for MetalStorage {
12241221
DType::U32 => "im2col_u32",
12251222
dtype => crate::bail!("Metal conv2d {dtype:?} not implemented"),
12261223
};
1227-
let src = buffer_o(&self.buffer, layout, self.dtype);
1224+
let src = buffer_o(self, layout);
12281225
candle_metal_kernels::call_im2col_strided(
12291226
&self.device.device,
12301227
&command_buffer,
@@ -1237,13 +1234,7 @@ impl BackendStorage for MetalStorage {
12371234
&dst,
12381235
)
12391236
.map_err(MetalError::from)?;
1240-
let col = Self {
1241-
buffer: dst,
1242-
device,
1243-
count: dst_el,
1244-
dtype: self.dtype,
1245-
pool_allocation: None,
1246-
};
1237+
let col = Self::new(dst, device, dst_el, self.dtype);
12471238
let h_out = params.out_h();
12481239
let w_out = params.out_w();
12491240
let b = params.b_size;
@@ -1451,7 +1442,7 @@ impl BackendStorage for MetalStorage {
14511442
.device
14521443
.new_buffer(dst_el, self.dtype, "upsample_nearest2d")?;
14531444
let command_buffer = self.device.command_buffer()?;
1454-
let src = buffer_o(&self.buffer, inp_l, self.dtype);
1445+
let src = buffer_o(self, inp_l);
14551446
candle_metal_kernels::call_upsample_nearest_2d(
14561447
&self.device.device,
14571448
&command_buffer,
@@ -1492,8 +1483,8 @@ impl BackendStorage for MetalStorage {
14921483
(left, right) => crate::bail!("Metal gather {left:?} {right:?} not implemented"),
14931484
};
14941485
let command_buffer = self.device.command_buffer()?;
1495-
let src = buffer_o(&self.buffer, src_l, dtype);
1496-
let ids = buffer_o(&ids.buffer, ids_l, ids.dtype);
1486+
let src = buffer_o(self, src_l);
1487+
let ids = buffer_o(ids, ids_l);
14971488
candle_metal_kernels::call_gather(
14981489
&device.device,
14991490
&command_buffer,
@@ -1540,9 +1531,9 @@ impl BackendStorage for MetalStorage {
15401531
})?,
15411532
};
15421533
let command_buffer = self.device.command_buffer()?;
1543-
let dst = buffer_o(&self.buffer, l, self.dtype);
1544-
let src = buffer_o(&src.buffer, src_l, src.dtype);
1545-
let ids = buffer_o(&ids.buffer, ids_l, ids.dtype);
1534+
let dst = buffer_o(self, l);
1535+
let src = buffer_o(src, src_l);
1536+
let ids = buffer_o(ids, ids_l);
15461537
candle_metal_kernels::call_scatter(
15471538
&self.device.device,
15481539
&command_buffer,
@@ -1589,9 +1580,9 @@ impl BackendStorage for MetalStorage {
15891580
})?,
15901581
};
15911582
let command_buffer = self.device.command_buffer()?;
1592-
let dst = buffer_o(&self.buffer, l, self.dtype);
1593-
let src = buffer_o(&src.buffer, src_l, src.dtype);
1594-
let ids = buffer_o(&ids.buffer, ids_l, ids.dtype);
1583+
let dst = buffer_o(self, l);
1584+
let src = buffer_o(src, src_l);
1585+
let ids = buffer_o(ids, ids_l);
15951586
candle_metal_kernels::call_scatter(
15961587
&self.device.device,
15971588
&command_buffer,
@@ -1647,8 +1638,8 @@ impl BackendStorage for MetalStorage {
16471638
}
16481639
};
16491640
let command_buffer = self.device.command_buffer()?;
1650-
let src = buffer_o(&self.buffer, src_l, dtype);
1651-
let ids = buffer_o(&ids.buffer, ids_l, ids.dtype);
1641+
let src = buffer_o(self, src_l);
1642+
let ids = buffer_o(ids, ids_l);
16521643
candle_metal_kernels::call_index_select(
16531644
&device.device,
16541645
&command_buffer,
@@ -1711,8 +1702,8 @@ impl BackendStorage for MetalStorage {
17111702
})?,
17121703
};
17131704
let command_buffer = self.device.command_buffer()?;
1714-
let src = buffer_o(&src.buffer, src_l, src.dtype);
1715-
let ids = buffer_o(&ids.buffer, ids_l, ids.dtype);
1705+
let src = buffer_o(src, src_l);
1706+
let ids = buffer_o(ids, ids_l);
17161707
candle_metal_kernels::call_index_add(
17171708
&self.device.device,
17181709
&command_buffer,
@@ -1827,8 +1818,8 @@ impl BackendStorage for MetalStorage {
18271818
d2,
18281819
src_s,
18291820
dst_s,
1830-
src_o * self.dtype.size_in_bytes(),
1831-
dst_o * self.dtype.size_in_bytes(),
1821+
self.offset_bytes + src_o * self.dtype.size_in_bytes(),
1822+
dst.offset_bytes + dst_o * self.dtype.size_in_bytes(),
18321823
)
18331824
.map_err(MetalError::from)?;
18341825
command_buffer.set_label("copy2d");
@@ -1842,9 +1833,11 @@ impl BackendStorage for MetalStorage {
18421833
command_buffer.set_label("copy_contiguous");
18431834
let blit = command_buffer.new_blit_command_encoder();
18441835
blit.set_label("copy_contiguous");
1845-
let src_offset = (src_l.start_offset() * self.dtype.size_in_bytes()) as NSUInteger;
1836+
let src_offset = (self.offset_bytes + src_l.start_offset() * self.dtype.size_in_bytes())
1837+
as NSUInteger;
18461838
let length = (src_l.shape().elem_count() * self.dtype.size_in_bytes()) as NSUInteger;
1847-
let dst_offset = (dst_offset * dst.dtype().size_in_bytes()) as NSUInteger;
1839+
let dst_offset =
1840+
(dst.offset_bytes + dst_offset * dst.dtype().size_in_bytes()) as NSUInteger;
18481841
blit.copy_from_buffer(&self.buffer, src_offset, dst.buffer(), dst_offset, length);
18491842
blit.end_encoding();
18501843
} else {
@@ -1862,10 +1855,10 @@ impl BackendStorage for MetalStorage {
18621855
DType::U8 => candle_metal_kernels::unary::strided::copy::U8,
18631856
dtype => crate::bail!("Metal copy_strided {dtype:?} not implemented"),
18641857
};
1865-
let src = buffer_o(&self.buffer, src_l, self.dtype);
1858+
let src = buffer_o(self, src_l);
18661859
let dst = BufferOffset {
18671860
buffer: &dst.buffer,
1868-
offset_in_bytes: dst_offset * dst.dtype.size_in_bytes(),
1861+
offset_in_bytes: dst.offset_bytes + dst_offset * dst.dtype.size_in_bytes(),
18691862
};
18701863
candle_metal_kernels::call_unary_strided(
18711864
&self.device.device,
@@ -1887,11 +1880,16 @@ impl BackendStorage for MetalStorage {
18871880
impl MetalStorage {
18881881
pub fn new(buffer: Arc<Buffer>, device: MetalDevice, count: usize, dtype: DType) -> Self {
18891882
let pool_allocation = take_pool_allocation(&buffer);
1883+
let offset_bytes = pool_allocation
1884+
.as_ref()
1885+
.map(|alloc| alloc.offset())
1886+
.unwrap_or(0);
18901887
Self {
18911888
buffer,
18921889
device,
18931890
count,
18941891
dtype,
1892+
offset_bytes,
18951893
pool_allocation,
18961894
}
18971895
}
@@ -1902,6 +1900,13 @@ impl MetalStorage {
19021900
.map(|allocation| allocation.pool())
19031901
}
19041902

1903+
fn buffer_offset<'a>(&'a self, layout: &Layout) -> BufferOffset<'a> {
1904+
BufferOffset {
1905+
buffer: &self.buffer,
1906+
offset_in_bytes: self.offset_bytes + layout.start_offset() * self.dtype.size_in_bytes(),
1907+
}
1908+
}
1909+
19051910
fn determine_pool(storages: &[&Self]) -> Result<Option<Arc<MetalTensorPool>>> {
19061911
let mut pool: Option<Arc<MetalTensorPool>> = None;
19071912
for storage in storages {
@@ -1939,8 +1944,8 @@ impl MetalStorage {
19391944
let shape = lhs_l.shape();
19401945
let el_count = shape.elem_count();
19411946
let command_buffer = device.command_buffer()?;
1942-
let lhs = buffer_o(&self.buffer, lhs_l, self.dtype);
1943-
let rhs = buffer_o(&rhs.buffer, rhs_l, rhs.dtype);
1947+
let lhs = buffer_o(self, lhs_l);
1948+
let rhs = buffer_o(rhs, rhs_l);
19441949
let (buffer, dtype) = if lhs_l.is_contiguous() && rhs_l.is_contiguous() && &op[..1] != "b" {
19451950
use candle_metal_kernels::binary::contiguous;
19461951

@@ -2143,7 +2148,13 @@ impl MetalStorage {
21432148
command_buffer.set_label("to_cpu");
21442149
let blit = command_buffer.new_blit_command_encoder();
21452150
blit.set_label("blit_to_cpu");
2146-
blit.copy_from_buffer(&self.buffer, 0, &buffer, 0, size);
2151+
blit.copy_from_buffer(
2152+
&self.buffer,
2153+
self.offset_bytes as NSUInteger,
2154+
&buffer,
2155+
0,
2156+
size,
2157+
);
21472158
blit.end_encoding();
21482159
}
21492160
self.device.wait_until_completed()?;

0 commit comments

Comments
 (0)