1mod compat;
16pub mod energy;
17#[cfg(test)]
18mod kernel_tests;
19mod lt;
20mod plan;
21mod tune;
22
23pub use compat::executor;
24pub use plan::{CudaPlan, Weights};
25
26use std::sync::Arc;
27
28use cudarc::driver::{CudaContext, CudaFunction, CudaModule, CudaSlice, CudaStream, DevicePtr};
29use cudarc::nvrtc::{CompileOptions, compile_ptx_with_opts};
30use kime_tensor::{Error, Result};
31
32const SOURCE: &str = include_str!("../kernels/compat.cu");
33
34const WORKSPACE: usize = 32 << 20;
36
37fn dev(e: impl std::fmt::Debug) -> Error {
38 Error::Device(format!("{e:?}"))
39}
40
41pub(crate) struct Kernels {
43 pub(crate) embed: CudaFunction,
44 pub(crate) ln: [CudaFunction; 4],
46 pub(crate) bias_act: [CudaFunction; 2],
48 pub(crate) rope: [CudaFunction; 2],
50 pub(crate) attention: [CudaFunction; 4],
52 pub(crate) geglu: [CudaFunction; 4],
54 pub(crate) add_type: CudaFunction,
55 pub(crate) gather: [CudaFunction; 2],
57 pub(crate) act_features: CudaFunction,
58 pub(crate) to_f16: CudaFunction,
59}
60
61impl Kernels {
62 fn load(m: &Arc<CudaModule>) -> Result<Self> {
63 let f = |name: &str| m.load_function(name).map_err(dev);
64 Ok(Self {
65 embed: f("embed")?,
66 ln: [f("ln_f32_f32")?, f("ln_f32_f16")?, f("ln_f16_f32")?, f("ln_f16_f16")?],
67 bias_act: [f("bias_act_f32")?, f("bias_act_f16")?],
68 rope: [f("rope_f32")?, f("rope_f16")?],
69 attention: [
70 f("attention_f32_f32")?,
71 f("attention_f32_f16")?,
72 f("attention_f16_f32")?,
73 f("attention_f16_f16")?,
74 ],
75 geglu: [
76 f("geglu_f32_f32")?,
77 f("geglu_f32_f16")?,
78 f("geglu_f16_f32")?,
79 f("geglu_f16_f16")?,
80 ],
81 add_type: f("add_type")?,
82 gather: [f("gather_f32")?, f("gather_f16")?],
83 act_features: f("act_features")?,
84 to_f16: f("to_f16")?,
85 })
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
91pub enum Precision {
92 #[default]
94 F32,
95 F16,
98}
99
100pub struct CudaBackend {
102 stream: Arc<CudaStream>,
103 k: Kernels,
104 lt: lt::Handle,
105 workspace: CudaSlice<u8>,
106 name: String,
107 arch: (i32, i32),
108 precision: Precision,
109 picks: tune::Picks,
110}
111
112impl std::fmt::Debug for CudaBackend {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 f.debug_struct("CudaBackend")
115 .field("name", &self.name)
116 .field("arch", &self.arch)
117 .field("precision", &self.precision)
118 .finish_non_exhaustive()
119 }
120}
121
122impl CudaBackend {
123 pub fn new(ordinal: usize, precision: Precision) -> Result<Self> {
129 let present = unsafe {
132 [
133 ("the CUDA driver", cudarc::driver::sys::is_culib_present()),
134 ("NVRTC", cudarc::nvrtc::sys::is_culib_present()),
135 ("cuBLASLt", cudarc::cublaslt::sys::is_culib_present()),
136 ]
137 };
138 if let Some((name, _)) = present.iter().find(|p| !p.1) {
139 return Err(Error::Device(format!("{name} is not installed")));
140 }
141 let ctx = CudaContext::new(ordinal).map_err(dev)?;
142 unsafe { ctx.disable_event_tracking() };
145 let stream = ctx.new_stream().map_err(dev)?;
146 let arch = ctx.compute_capability().map_err(dev)?;
147 let opts = CompileOptions { arch: Some(arch_flag(arch)), ..Default::default() };
148 let ptx = compile_ptx_with_opts(SOURCE, opts).map_err(dev)?;
149 let module = ctx.load_module(ptx).map_err(dev)?;
150 let k = Kernels::load(&module)?;
151 let lt = lt::Handle::new()?;
152 let workspace = stream.alloc_zeros::<u8>(WORKSPACE).map_err(dev)?;
153 let name = ctx.name().map_err(dev)?;
154 let picks = tune::Picks::for_gpu(&name);
155 Ok(Self { stream, k, lt, workspace, name, arch, precision, picks })
156 }
157
158 #[must_use]
160 pub fn name(&self) -> &str {
161 &self.name
162 }
163
164 #[must_use]
166 pub fn precision(&self) -> Precision {
167 self.precision
168 }
169
170 #[must_use]
172 pub fn arch(&self) -> (i32, i32) {
173 self.arch
174 }
175
176 fn workspace_ptr(&self) -> u64 {
177 self.workspace.device_ptr(&self.stream).0
178 }
179}
180
181fn arch_flag((major, minor): (i32, i32)) -> &'static str {
184 match (major, minor) {
185 (..=7, _) => "compute_75",
186 (8, 0) => "compute_80",
187 (8, 6..=8) => "compute_86",
188 (8, _) => "compute_89",
189 _ => "compute_90",
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn arch_flags() {
199 assert_eq!(arch_flag((7, 5)), "compute_75");
200 assert_eq!(arch_flag((8, 6)), "compute_86");
201 assert_eq!(arch_flag((8, 9)), "compute_89");
202 assert_eq!(arch_flag((12, 0)), "compute_90");
203 }
204}