@@ -6,12 +6,58 @@ use crate::op::{BinaryOpT, CmpOp, ReduceOp, UnaryOpT};
66use crate :: { CpuStorage , CpuStorageRef , DType , Error , Layout , Result , Shape } ;
77use candle_metal_kernels:: { BufferOffset , CallConvTranspose2dCfg , Kernels } ;
88use metal:: { Buffer , NSUInteger } ;
9+ use std:: cell:: RefCell ;
910use std:: collections:: HashMap ;
1011use std:: ffi:: c_void;
11- use std:: sync:: { Arc , Mutex , PoisonError , RwLock , TryLockError } ;
12+ use std:: sync:: { Arc , Mutex , OnceLock , PoisonError , RwLock , TryLockError } ;
1213
1314mod device;
15+ mod pool;
1416pub use device:: { DeviceId , MetalDevice , SHARED_BUFFER_STORAGE_MODE } ;
17+ pub use pool:: { MetalPoolAllocation , MetalTensorPool } ;
18+
19+ thread_local ! {
20+ static POOL_STACK : RefCell <Vec <Option <Arc <MetalTensorPool >>>> = RefCell :: new( Vec :: new( ) ) ;
21+ }
22+
23+ pub ( crate ) struct PoolContextGuard ;
24+
25+ impl Drop for PoolContextGuard {
26+ fn drop ( & mut self ) {
27+ POOL_STACK . with ( |stack| {
28+ stack. borrow_mut ( ) . pop ( ) ;
29+ } ) ;
30+ }
31+ }
32+
33+ pub ( crate ) fn push_pool_context ( pool : Option < Arc < MetalTensorPool > > ) -> PoolContextGuard {
34+ POOL_STACK . with ( |stack| stack. borrow_mut ( ) . push ( pool) ) ;
35+ PoolContextGuard
36+ }
37+
38+ fn current_pool ( ) -> Option < Arc < MetalTensorPool > > {
39+ POOL_STACK . with ( |stack| stack. borrow ( ) . last ( ) . cloned ( ) . flatten ( ) )
40+ }
41+
42+ fn pool_registry ( ) -> & ' static Mutex < HashMap < usize , Arc < MetalPoolAllocation > > > {
43+ static REGISTRY : OnceLock < Mutex < HashMap < usize , Arc < MetalPoolAllocation > > > > = OnceLock :: new ( ) ;
44+ REGISTRY . get_or_init ( || Mutex :: new ( HashMap :: new ( ) ) )
45+ }
46+
47+ fn register_pool_allocation ( buffer : & Arc < Buffer > , allocation : Arc < MetalPoolAllocation > ) {
48+ let ptr = Arc :: as_ptr ( buffer) as usize ;
49+ if let Ok ( mut registry) = pool_registry ( ) . lock ( ) {
50+ registry. insert ( ptr, allocation) ;
51+ }
52+ }
53+
54+ fn take_pool_allocation ( buffer : & Arc < Buffer > ) -> Option < Arc < MetalPoolAllocation > > {
55+ let ptr = Arc :: as_ptr ( buffer) as usize ;
56+ pool_registry ( )
57+ . lock ( )
58+ . ok ( )
59+ . and_then ( |mut registry| registry. remove ( & ptr) )
60+ }
1561
1662pub fn buffer_o < ' a > ( buffer : & ' a Buffer , l : & Layout , dtype : DType ) -> BufferOffset < ' a > {
1763 BufferOffset {
@@ -77,6 +123,7 @@ pub struct MetalStorage {
77123 count : usize ,
78124 /// The dtype is kept since buffers are untyped.
79125 dtype : DType ,
126+ pool_allocation : Option < Arc < MetalPoolAllocation > > ,
80127}
81128
82129impl BackendStorage for MetalStorage {
@@ -113,6 +160,7 @@ impl BackendStorage for MetalStorage {
113160 }
114161
115162 fn affine ( & self , layout : & Layout , mul : f64 , add : f64 ) -> Result < Self > {
163+ let _guard = Self :: pool_guard ( & [ self ] ) ?;
116164 let device = self . device ( ) . clone ( ) ;
117165
118166 let shape = layout. shape ( ) ;
@@ -168,6 +216,7 @@ impl BackendStorage for MetalStorage {
168216 }
169217
170218 fn powf ( & self , layout : & Layout , pow : f64 ) -> Result < Self > {
219+ let _guard = Self :: pool_guard ( & [ self ] ) ?;
171220 let device = self . device ( ) . clone ( ) ;
172221
173222 let shape = layout. shape ( ) ;
@@ -219,6 +268,7 @@ impl BackendStorage for MetalStorage {
219268 }
220269
221270 fn elu ( & self , layout : & Layout , alpha : f64 ) -> Result < Self > {
271+ let _guard = Self :: pool_guard ( & [ self ] ) ?;
222272 let device = self . device ( ) . clone ( ) ;
223273
224274 let shape = layout. shape ( ) ;
@@ -270,6 +320,7 @@ impl BackendStorage for MetalStorage {
270320 }
271321
272322 fn reduce_op ( & self , op : ReduceOp , layout : & Layout , sum_dims : & [ usize ] ) -> Result < Self > {
323+ let _guard = Self :: pool_guard ( & [ self ] ) ?;
273324 let device = self . device . clone ( ) ;
274325
275326 let src_stride = layout. stride ( ) ;
@@ -408,6 +459,7 @@ impl BackendStorage for MetalStorage {
408459 }
409460
410461 fn cmp ( & self , op : CmpOp , rhs : & Self , lhs_l : & Layout , rhs_l : & Layout ) -> Result < Self > {
462+ let _guard = Self :: pool_guard ( & [ self , rhs] ) ?;
411463 let name = match op {
412464 CmpOp :: Eq => "eq" ,
413465 CmpOp :: Ne => "ne" ,
@@ -527,6 +579,7 @@ impl BackendStorage for MetalStorage {
527579 }
528580
529581 fn to_dtype ( & self , layout : & Layout , dtype : DType ) -> Result < Self > {
582+ let _guard = Self :: pool_guard ( & [ self ] ) ?;
530583 let device = self . device ( ) ;
531584 let shape = layout. shape ( ) ;
532585 let el_count = shape. elem_count ( ) ;
@@ -644,6 +697,7 @@ impl BackendStorage for MetalStorage {
644697 }
645698
646699 fn unary_impl < B : UnaryOpT > ( & self , layout : & Layout ) -> Result < Self > {
700+ let _guard = Self :: pool_guard ( & [ self ] ) ?;
647701 let device = self . device ( ) ;
648702 let dtype = self . dtype ;
649703 let shape = layout. shape ( ) ;
@@ -904,6 +958,7 @@ impl BackendStorage for MetalStorage {
904958 f : & Self ,
905959 f_l : & Layout ,
906960 ) -> Result < Self > {
961+ let _guard = Self :: pool_guard ( & [ self , t, f] ) ?;
907962 let device = self . device . clone ( ) ;
908963 let shape = t_l. shape ( ) ;
909964 let dims = shape. dims ( ) ;
@@ -956,6 +1011,7 @@ impl BackendStorage for MetalStorage {
9561011 kernel_l : & Layout ,
9571012 params : & ParamsConv1D ,
9581013 ) -> Result < Self > {
1014+ let _guard = Self :: pool_guard ( & [ self , kernel] ) ?;
9591015 let device = self . device ( ) . clone ( ) ;
9601016 let shape = layout. shape ( ) ;
9611017 let dims = shape. dims ( ) ;
@@ -993,6 +1049,7 @@ impl BackendStorage for MetalStorage {
9931049 device,
9941050 count : dst_el,
9951051 dtype : self . dtype ,
1052+ pool_allocation : None ,
9961053 } ;
9971054 let l_out = params. l_out ( ) ;
9981055 let b = params. b_size ;
@@ -1027,6 +1084,7 @@ impl BackendStorage for MetalStorage {
10271084 k_layout : & Layout ,
10281085 params : & ParamsConvTranspose1D ,
10291086 ) -> Result < Self > {
1087+ let _guard = Self :: pool_guard ( & [ self , k] ) ?;
10301088 const USE_COL2IM_CONV1D_TR : bool = true ;
10311089
10321090 let can_use_col2im = k_layout. is_contiguous ( )
@@ -1138,6 +1196,7 @@ impl BackendStorage for MetalStorage {
11381196 kernel_l : & Layout ,
11391197 params : & ParamsConv2D ,
11401198 ) -> Result < Self > {
1199+ let _guard = Self :: pool_guard ( & [ self , kernel] ) ?;
11411200 let device = self . device ( ) . clone ( ) ;
11421201 let shape = layout. shape ( ) ;
11431202 let dims = shape. dims ( ) ;
@@ -1183,6 +1242,7 @@ impl BackendStorage for MetalStorage {
11831242 device,
11841243 count : dst_el,
11851244 dtype : self . dtype ,
1245+ pool_allocation : None ,
11861246 } ;
11871247 let h_out = params. out_h ( ) ;
11881248 let w_out = params. out_w ( ) ;
@@ -1220,6 +1280,7 @@ impl BackendStorage for MetalStorage {
12201280 kernel_l : & Layout ,
12211281 params : & ParamsConvTranspose2D ,
12221282 ) -> Result < Self > {
1283+ let _guard = Self :: pool_guard ( & [ self , kernel] ) ?;
12231284 // Kernel shape: (c_in_k, c_out, h_k, w_k)
12241285 // Input shape: (b_size, c_in, h_in, w_in)
12251286 let ( out_w, out_h) = ( params. out_w ( ) , params. out_h ( ) ) ;
@@ -1283,6 +1344,7 @@ impl BackendStorage for MetalStorage {
12831344 ( w_k, h_k) : ( usize , usize ) ,
12841345 ( w_stride, h_stride) : ( usize , usize ) ,
12851346 ) -> Result < Self > {
1347+ let _guard = Self :: pool_guard ( & [ self ] ) ?;
12861348 let shape = inp_l. shape ( ) ;
12871349 let ( b_size, channels, width, height) = shape. dims4 ( ) ?;
12881350 let strides = inp_l. stride ( ) ;
@@ -1325,6 +1387,7 @@ impl BackendStorage for MetalStorage {
13251387 ( w_k, h_k) : ( usize , usize ) ,
13261388 ( w_stride, h_stride) : ( usize , usize ) ,
13271389 ) -> Result < Self > {
1390+ let _guard = Self :: pool_guard ( & [ self ] ) ?;
13281391 let shape = inp_l. shape ( ) ;
13291392 let ( b_size, channels, width, height) = shape. dims4 ( ) ?;
13301393 let strides = inp_l. stride ( ) ;
@@ -1366,6 +1429,7 @@ impl BackendStorage for MetalStorage {
13661429 }
13671430
13681431 fn upsample_nearest2d ( & self , inp_l : & Layout , out_w : usize , out_h : usize ) -> Result < Self > {
1432+ let _guard = Self :: pool_guard ( & [ self ] ) ?;
13691433 // let inp = &inp.slice(inp_l.start_offset()..);
13701434 let shape = inp_l. shape ( ) ;
13711435 let dims = shape. dims ( ) ;
@@ -1405,6 +1469,7 @@ impl BackendStorage for MetalStorage {
14051469 }
14061470
14071471 fn gather ( & self , src_l : & Layout , ids : & Self , ids_l : & Layout , dim : usize ) -> Result < Self > {
1472+ let _guard = Self :: pool_guard ( & [ self , ids] ) ?;
14081473 if !ids_l. is_contiguous ( ) {
14091474 return Err ( crate :: Error :: RequiresContiguous { op : "gather" } . bt ( ) ) ;
14101475 } ;
@@ -1544,6 +1609,7 @@ impl BackendStorage for MetalStorage {
15441609 }
15451610
15461611 fn index_select ( & self , ids : & Self , src_l : & Layout , ids_l : & Layout , dim : usize ) -> Result < Self > {
1612+ let _guard = Self :: pool_guard ( & [ self , ids] ) ?;
15471613 if !ids_l. is_contiguous ( ) {
15481614 crate :: bail!( "Metal index_select requires contiguous ids" )
15491615 }
@@ -1671,6 +1737,7 @@ impl BackendStorage for MetalStorage {
16711737 lhs_l : & Layout ,
16721738 rhs_l : & Layout ,
16731739 ) -> Result < Self > {
1740+ let _guard = Self :: pool_guard ( & [ self , rhs] ) ?;
16741741 let buffer = self . device . new_buffer ( b * m * n, self . dtype , "matmul" ) ?;
16751742 let command_buffer = self . device . command_buffer ( ) ?;
16761743 command_buffer. set_label ( "matmul" ) ;
@@ -1819,14 +1886,43 @@ impl BackendStorage for MetalStorage {
18191886
18201887impl MetalStorage {
18211888 pub fn new ( buffer : Arc < Buffer > , device : MetalDevice , count : usize , dtype : DType ) -> Self {
1889+ let pool_allocation = take_pool_allocation ( & buffer) ;
18221890 Self {
18231891 buffer,
18241892 device,
18251893 count,
18261894 dtype,
1895+ pool_allocation,
18271896 }
18281897 }
18291898
1899+ pub fn pool ( & self ) -> Option < Arc < MetalTensorPool > > {
1900+ self . pool_allocation
1901+ . as_ref ( )
1902+ . map ( |allocation| allocation. pool ( ) )
1903+ }
1904+
1905+ fn determine_pool ( storages : & [ & Self ] ) -> Result < Option < Arc < MetalTensorPool > > > {
1906+ let mut pool: Option < Arc < MetalTensorPool > > = None ;
1907+ for storage in storages {
1908+ if let Some ( candidate) = storage. pool ( ) {
1909+ if let Some ( existing) = & pool {
1910+ if !Arc :: ptr_eq ( existing, & candidate) {
1911+ crate :: bail!( "Cannot operate on tensors from different pools" ) ;
1912+ }
1913+ } else {
1914+ pool = Some ( candidate) ;
1915+ }
1916+ }
1917+ }
1918+ Ok ( pool)
1919+ }
1920+
1921+ pub ( crate ) fn pool_guard ( storages : & [ & Self ] ) -> Result < PoolContextGuard > {
1922+ let pool = Self :: determine_pool ( storages) ?;
1923+ Ok ( push_pool_context ( pool) )
1924+ }
1925+
18301926 pub fn buffer ( & self ) -> & Buffer {
18311927 & self . buffer
18321928 }
@@ -1838,6 +1934,7 @@ impl MetalStorage {
18381934 lhs_l : & Layout ,
18391935 rhs_l : & Layout ,
18401936 ) -> Result < Self > {
1937+ let _guard = Self :: pool_guard ( & [ self , rhs] ) ?;
18411938 let device = self . device ( ) ;
18421939 let shape = lhs_l. shape ( ) ;
18431940 let el_count = shape. elem_count ( ) ;
0 commit comments