77from cutlass .cute .runtime import from_dlpack
88
99from transformer_nuggets .utils .benchmark import benchmark_cuda_function_in_microseconds
10+ from transformer_nuggets .cute .cache import cute_compile_and_cache , get_cache_stats
11+
12+
13+ # init_logging()
1014
1115
1216@cute .kernel
@@ -31,6 +35,17 @@ def naive_elementwise_add_kernel(
3135 gC [mi , ni ] = a_val + b_val
3236
3337
38+ @cute .jit
39+ def naive_elementwise_add (mA : cute .Tensor , mB : cute .Tensor , mC : cute .Tensor ):
40+ num_threads_per_block = 256
41+ m , n = mA .shape
42+
43+ kernel = naive_elementwise_add_kernel (mA , mB , mC )
44+ kernel .launch (
45+ grid = ((m * n ) // num_threads_per_block , 1 , 1 ), block = (num_threads_per_block , 1 , 1 )
46+ )
47+
48+
3449@cute .kernel
3550def elementwise_apply_kernel (
3651 op : cutlass .Constexpr ,
@@ -62,23 +77,7 @@ def elementwise_apply_kernel(
6277
6378
6479@cute .jit
65- def naive_elementwise_add (mA : cute .Tensor , mB : cute .Tensor , mC : cute .Tensor ):
66- num_threads_per_block = 256
67- m , n = mA .shape
68-
69- kernel = naive_elementwise_add_kernel (mA , mB , mC )
70- kernel .launch (
71- grid = ((m * n ) // num_threads_per_block , 1 , 1 ), block = (num_threads_per_block , 1 , 1 )
72- )
73-
74-
75- @cute .jit
76- def elementwise_op (
77- op : cutlass .Constexpr ,
78- mA : cute .Tensor ,
79- mB : cute .Tensor ,
80- mC : cute .Tensor ,
81- ):
80+ def elem_kernel (op : cutlass .Constexpr , mA : cute .Tensor , mB : cute .Tensor , mC : cute .Tensor ):
8281 thr_layout = cute .make_layout ((4 , 32 ), stride = (32 , 1 ))
8382 val_layout = cute .make_layout ((4 , 8 ), stride = (8 , 1 ))
8483 tiler_mn , tv_layout = cute .make_layout_tv (thr_layout , val_layout )
@@ -93,13 +92,41 @@ def elementwise_op(
9392 )
9493
9594
96- def benchmark (callable , * , num_warmups = 5 , num_iterations = 200 ):
95+ def elementwise_op (
96+ op : cutlass .Constexpr ,
97+ a : torch .Tensor ,
98+ b : torch .Tensor ,
99+ c : torch .Tensor ,
100+ * ,
101+ assumed_align : int = 16 ,
102+ ):
103+ """
104+ Apply an elementwise operation using CUTE kernels.
105+
106+ Args:
107+ op: Cutlass constexpr operation (e.g., add, mul)
108+ a: Input tensor A (PyTorch tensor)
109+ b: Input tensor B (PyTorch tensor)
110+ c: Output tensor C (PyTorch tensor, will be modified in-place)
111+ assumed_align: Memory alignment assumption for dlpack conversion
112+ """
113+ # Convert PyTorch tensors to CUTE tensors
114+ mA = from_dlpack (a , assumed_align = assumed_align )
115+ mB = from_dlpack (b , assumed_align = assumed_align )
116+ mC = from_dlpack (c , assumed_align = assumed_align )
117+
118+ # Compile and execute the kernel
119+ compiled_kernel = cute_compile_and_cache (elem_kernel , op , mA , mB , mC )
120+ return compiled_kernel (mA , mB , mC )
121+
122+
123+ def benchmark (callable , tensor_a , * , num_warmups = 5 , num_iterations = 200 ):
97124 time = benchmark_cuda_function_in_microseconds (callable )
98125 avg_time = time / 1e3
99126
100127 print (f"Average execution time: { avg_time :.4f} ms" )
101128
102- total_bytes = 3 * a .numel () * a .element_size ()
129+ total_bytes = 3 * tensor_a .numel () * tensor_a .element_size ()
103130 throughput_gb_s = total_bytes / (avg_time / 1000 ) / 1e9
104131 print (f"Throughput: { throughput_gb_s :.2f} GB/s" )
105132 print ()
@@ -112,28 +139,38 @@ def benchmark(callable, *, num_warmups=5, num_iterations=200):
112139 b = torch .randn (M , N , device = "cuda" , dtype = torch .float16 )
113140 c = torch .zeros (M , N , device = "cuda" , dtype = torch .float16 )
114141
115- a_ = from_dlpack (a , assumed_align = 16 )
116- b_ = from_dlpack (b , assumed_align = 16 )
117- c_ = from_dlpack (c , assumed_align = 16 )
118-
119- elementwise_op (mul , a_ , b_ , c_ )
142+ # Test the new API that takes regular PyTorch tensors
143+ elementwise_op (mul , a , b , c )
120144 torch .testing .assert_close (c , mul (a , b ))
121145 print ("✓ Multiplication test passed" )
122146
123147 c .zero_ ()
124148
149+ # For naive kernel, we still need CUTE tensors
150+ a_ = from_dlpack (a , assumed_align = 16 )
151+ b_ = from_dlpack (b , assumed_align = 16 )
152+ c_ = from_dlpack (c , assumed_align = 16 )
125153 naive_elementwise_add_compiled = cute .compile (naive_elementwise_add , a_ , b_ , c_ )
126- elementwise_add_compiled = cute .compile (elementwise_op , add , a_ , b_ , c_ )
154+
155+ # Note: elementwise_op is now cached automatically!
156+ # First call will compile, subsequent calls use cache
127157
128158 print ("\n " + "=" * 50 )
129159 print ("BENCHMARK RESULTS" )
130160 print ("=" * 50 + "\n " )
131161
132162 print ("1. Naive elementwise add kernel:" )
133- benchmark (partial (naive_elementwise_add_compiled , a_ , b_ , c_ ))
163+ benchmark (partial (naive_elementwise_add_compiled , a_ , b_ , c_ ), a )
134164
135165 print ("2. PyTorch add (baseline):" )
136- benchmark (partial (torch .add , a , b , out = c ))
166+ benchmark (partial (torch .add , a , b , out = c ), a )
167+
168+ print ("3. Optimized elementwise add kernel (with caching):" )
169+ benchmark (partial (elementwise_op , add , a , b , c , assumed_align = 128 ), a )
137170
138- print ("3. Optimized elementwise add kernel:" )
139- benchmark (partial (elementwise_add_compiled , a_ , b_ , c_ ))
171+ print ("\n Cache Statistics:" )
172+ stats = get_cache_stats ()
173+ print (f" Cache hits: { stats ['hits' ]} " )
174+ print (f" Cache misses: { stats ['misses' ]} " )
175+ print (f" Hit rate: { stats ['hit_rate' ]:.2%} " )
176+ print (f" Cache size: { stats ['cache_size' ]} " )
0 commit comments