ferrotensor_backend/engine/
mod.rs1use crate::{UnifiedTensor, WgpuContext};
4use std::borrow::Cow;
5use wgpu::{util::DeviceExt, Buffer, Device};
6
7mod attention;
8mod elementwise;
9mod matmul;
10mod misc;
11mod norm;
12mod quant;
13mod training;
14
15pub use attention::AttentionOptions;
16pub use training::AdamParams;
17
18fn shader_source() -> String {
20 let kernels_src = include_str!("../shaders/kernels.wgsl");
21 let matmul_src = include_str!("../shaders/matmul.wgsl");
22 let flash_v2_src = include_str!("../shaders/flash_attention_v2.wgsl");
23 let flash_v2_bw_src = include_str!("../shaders/flash_attention_v2_backward.wgsl");
24 format!("{kernels_src}\n{matmul_src}\n{flash_v2_src}\n{flash_v2_bw_src}")
25}
26
27struct Kernel {
32 pipeline: wgpu::ComputePipeline,
33 layout: wgpu::BindGroupLayout,
34}
35
36impl Kernel {
37 fn new(device: &Device, module: &wgpu::ShaderModule, entry_point: &str) -> Self {
38 let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
39 label: Some(entry_point),
40 layout: None,
41 module,
42 entry_point,
43 });
44 let layout = pipeline.get_bind_group_layout(0);
45 Self { pipeline, layout }
46 }
47}
48
49fn grid(groups: u32) -> (u32, u32, u32) {
52 const MAX: u32 = 65535;
53 if groups <= MAX {
54 (groups, 1, 1)
55 } else {
56 (MAX, groups.div_ceil(MAX), 1)
57 }
58}
59
60fn groups64(n: usize) -> (u32, u32, u32) {
62 grid((n as u32).div_ceil(64))
63}
64
65fn uniform(ctx: &WgpuContext, bytes: &[u8]) -> Buffer {
67 ctx.device
68 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
69 label: Some("Uniform"),
70 contents: bytes,
71 usage: wgpu::BufferUsages::UNIFORM,
72 })
73}
74
75pub struct ComputeEngine {
82 matmul: Kernel,
83 rope: Kernel,
84 rms_norm: Kernel,
85 silu: Kernel,
86 softmax: Kernel,
87 add: Kernel,
88 mul: Kernel,
89 sub: Kernel,
90 add_bw: Kernel,
91 sub_bw: Kernel,
92 mul_bw: Kernel,
93 silu_bw: Kernel,
94 sgd: Kernel,
95 adamw: Kernel,
96 mse_loss: Kernel,
97 mse_loss_bw: Kernel,
98 relu: Kernel,
99 relu_bw: Kernel,
100 softmax_bw: Kernel,
101 dequant: Kernel,
102 top_k: Kernel,
103 affine_dequant: Kernel,
104 conv1d: Kernel,
105 ce_loss: Kernel,
106 ce_loss_bw: Kernel,
107 grad_norm: Kernel,
108 grad_clip: Kernel,
109 silu_gate: Kernel,
110 silu_gate_bw: Kernel,
111 rms_residual: Kernel,
112 grad_accum: Kernel,
113 grad_scale: Kernel,
114 flash_attn_v2: Kernel,
115 flash_attn_v2_bw_dq: Kernel,
116 flash_attn_v2_bw_dkdv: Kernel,
117}
118
119impl ComputeEngine {
120 pub fn new(ctx: &WgpuContext) -> Self {
122 let combined_src = shader_source();
123 let module = ctx
124 .device
125 .create_shader_module(wgpu::ShaderModuleDescriptor {
126 label: Some("Kernels"),
127 source: wgpu::ShaderSource::Wgsl(Cow::Owned(combined_src)),
128 });
129 let k = |entry: &str| Kernel::new(&ctx.device, &module, entry);
130
131 Self {
132 matmul: k("matmul_blocked"),
133 rope: k("rope"),
134 rms_norm: k("rms_norm"),
135 silu: k("silu"),
136 softmax: k("softmax"),
137 add: k("add"),
138 mul: k("mul"),
139 sub: k("sub"),
140 add_bw: k("add_backward"),
141 sub_bw: k("sub_backward"),
142 mul_bw: k("mul_backward"),
143 silu_bw: k("silu_backward_real"),
144 sgd: k("sgd_update"),
145 adamw: k("adamw_update"),
146 mse_loss: k("mse_loss"),
147 mse_loss_bw: k("mse_loss_backward"),
148 relu: k("relu"),
149 relu_bw: k("relu_backward"),
150 softmax_bw: k("softmax_backward"),
151 dequant: k("dequantize"),
152 top_k: k("top_k"),
153 affine_dequant: k("affine_dequantize"),
154 conv1d: k("conv1d"),
155 ce_loss: k("cross_entropy_loss"),
156 ce_loss_bw: k("cross_entropy_backward"),
157 grad_norm: k("gradient_norm_squared"),
158 grad_clip: k("clip_grad_by_norm"),
159 silu_gate: k("silu_gate_fused"),
160 silu_gate_bw: k("silu_gate_backward"),
161 rms_residual: k("rms_norm_residual"),
162 grad_accum: k("gradient_accumulate"),
163 grad_scale: k("gradient_scale"),
164 flash_attn_v2: k("flash_attention_v2_metal"),
165 flash_attn_v2_bw_dq: k("flash_bwd_dq"),
166 flash_attn_v2_bw_dkdv: k("flash_bwd_dk_dv"),
167 }
168 }
169
170 fn run(
173 &self,
174 ctx: &WgpuContext,
175 kernel: &Kernel,
176 entries: &[(u32, &Buffer)],
177 groups: (u32, u32, u32),
178 ) {
179 let entries: Vec<wgpu::BindGroupEntry<'_>> = entries
180 .iter()
181 .map(|(binding, buf)| wgpu::BindGroupEntry {
182 binding: *binding,
183 resource: buf.as_entire_binding(),
184 })
185 .collect();
186 let bg = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor {
187 label: None,
188 layout: &kernel.layout,
189 entries: &entries,
190 });
191 let mut encoder = ctx
192 .device
193 .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
194 {
195 let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default());
196 pass.set_pipeline(&kernel.pipeline);
197 pass.set_bind_group(0, &bg, &[]);
198 pass.dispatch_workgroups(groups.0, groups.1, groups.2);
199 }
200 ctx.queue.submit(Some(encoder.finish()));
201 }
202
203 pub fn copy_tensor(
205 &self,
206 ctx: &WgpuContext,
207 src: &UnifiedTensor,
208 dst: &UnifiedTensor,
209 dst_offset: usize,
210 ) {
211 let mut encoder = ctx
212 .device
213 .create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
214 encoder.copy_buffer_to_buffer(
215 src.buffer(),
216 0,
217 dst.buffer(),
218 dst_offset as u64,
219 src.size_in_bytes(),
220 );
221 ctx.queue.submit(Some(encoder.finish()));
222 }
223}
224
225fn dummy_storage(ctx: &WgpuContext) -> Buffer {
227 ctx.device
228 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
229 label: Some("Dummy Storage"),
230 contents: &[0u8; 4],
231 usage: wgpu::BufferUsages::STORAGE,
232 })
233}
234
235#[cfg(test)]
236mod shader_tests {
237 #[test]
240 fn combined_wgsl_validates() {
241 let src = super::shader_source();
242 let module = match naga::front::wgsl::parse_str(&src) {
243 Ok(m) => m,
244 Err(e) => panic!("WGSL parse error:\n{}", e.emit_to_string(&src)),
245 };
246 let mut validator = naga::valid::Validator::new(
247 naga::valid::ValidationFlags::all(),
248 naga::valid::Capabilities::empty(),
249 );
250 if let Err(e) = validator.validate(&module) {
251 panic!("WGSL validation error:\n{}", e.emit_to_string(&src));
252 }
253 }
254}