1use burn_backend::{DeviceId, DeviceOps, DeviceSettings};
2
3use crate::backends::*;
4
5#[cfg(feature = "autodiff")]
6use alloc::boxed::Box;
7
8#[cfg(feature = "cubecl")]
11use alloc::vec::Vec;
12#[cfg(feature = "cubecl")]
13use burn_backend::cubecl::{ThroughputKey, ThroughputValue};
14
15#[derive(Clone, Eq)]
31pub enum DispatchDevice {
32 #[cfg(feature = "cpu")]
34 Cpu(CpuDevice),
35
36 #[cfg(feature = "cuda")]
38 Cuda(CudaDevice),
39
40 #[cfg(feature = "metal")]
42 Metal(WgpuDevice),
43
44 #[cfg(feature = "rocm")]
46 Rocm(RocmDevice),
47
48 #[cfg(feature = "vulkan")]
50 Vulkan(WgpuDevice),
51
52 #[cfg(feature = "wgpu")]
54 Wgpu(WgpuDevice),
55
56 #[cfg(feature = "webgpu")]
58 WebGpu(WgpuDevice),
59
60 #[cfg(any(feature = "flex", default_backend))]
62 Flex(FlexDevice),
63
64 #[cfg(feature = "ndarray")]
66 NdArray(NdArrayDevice),
67
68 #[cfg(feature = "tch")]
70 LibTorch(LibTorchDevice),
71
72 #[cfg(feature = "remote")]
74 Remote(RemoteDevice),
75
76 #[cfg(feature = "autodiff")]
78 Autodiff(AutodiffDevice),
79}
80
81#[cfg(feature = "cubecl")]
82impl DispatchDevice {
83 pub fn performance_stats(&self, keys: &[ThroughputKey]) -> Vec<ThroughputValue> {
90 match self {
91 #[cfg(feature = "cuda")]
92 DispatchDevice::Cuda(device) => burn_cuda::device_throughput(device, keys),
93 #[cfg(feature = "wgpu")]
94 DispatchDevice::Wgpu(device) => burn_wgpu::device_throughput(device, keys),
95 #[cfg(feature = "vulkan")]
96 DispatchDevice::Vulkan(device) => burn_wgpu::device_throughput(device, keys),
97 #[cfg(feature = "metal")]
98 DispatchDevice::Metal(device) => burn_wgpu::device_throughput(device, keys),
99 #[cfg(feature = "webgpu")]
100 DispatchDevice::WebGpu(device) => burn_wgpu::device_throughput(device, keys),
101 #[allow(unreachable_patterns)]
102 _ => Vec::new(),
103 }
104 }
105}
106
107#[cfg(feature = "autodiff")]
108#[derive(Debug, Clone, PartialEq, Eq)]
113pub struct AutodiffDevice {
114 pub(crate) inner: Box<DispatchDevice>,
115 pub(crate) checkpointing: CheckpointingStrategy,
116}
117
118#[cfg(feature = "autodiff")]
119impl AutodiffDevice {
120 pub(crate) fn new(device: DispatchDevice, checkpointing: CheckpointingStrategy) -> Self {
121 Self {
122 inner: Box::new(device),
123 checkpointing,
124 }
125 }
126
127 pub fn inner(self) -> DispatchDevice {
129 *self.inner
130 }
131}
132
133#[cfg(feature = "autodiff")]
134impl core::ops::Deref for AutodiffDevice {
136 type Target = DispatchDevice;
137
138 fn deref(&self) -> &Self::Target {
139 &self.inner
140 }
141}
142
143#[allow(missing_docs)]
144#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
145#[repr(u8)]
147pub enum CheckpointingStrategy {
148 Balanced,
149 #[default]
150 None,
151}
152
153#[cfg(feature = "autodiff")]
154pub(crate) fn validate_checkpointing(
155 lhs: Option<crate::CheckpointingStrategy>,
156 rhs: Option<crate::CheckpointingStrategy>,
157) -> Option<crate::CheckpointingStrategy> {
158 match (lhs, rhs) {
159 (Some(lhs), Some(rhs)) => {
160 assert_eq!(
161 lhs, rhs,
162 "Autodiff strategy mismatch: {lhs:?} vs {rhs:?}. Tensors in the same operation must share a strategy."
163 );
164 Some(lhs)
165 }
166 (None, None) => None,
167 (None, rhs) => rhs,
171 (lhs, None) => lhs,
172 }
173}
174
175impl core::fmt::Debug for DispatchDevice {
176 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
177 match self {
178 #[cfg(feature = "cpu")]
179 Self::Cpu(device) => f.debug_tuple("Cpu").field(device).finish(),
180 #[cfg(feature = "cuda")]
181 Self::Cuda(device) => f.debug_tuple("Cuda").field(device).finish(),
182 #[cfg(feature = "metal")]
183 Self::Metal(device) => f.debug_tuple("Metal").field(device).finish(),
184 #[cfg(feature = "rocm")]
185 Self::Rocm(device) => f.debug_tuple("Rocm").field(device).finish(),
186 #[cfg(feature = "vulkan")]
187 Self::Vulkan(device) => f.debug_tuple("Vulkan").field(device).finish(),
188 #[cfg(feature = "wgpu")]
189 Self::Wgpu(device) => f.debug_tuple("Wgpu").field(device).finish(),
190 #[cfg(feature = "webgpu")]
191 Self::WebGpu(device) => f.debug_tuple("WebGpu").field(device).finish(),
192 #[cfg(any(feature = "flex", default_backend))]
193 Self::Flex(device) => f.debug_tuple("Flex").field(device).finish(),
194 #[cfg(feature = "ndarray")]
195 Self::NdArray(device) => f.debug_tuple("NdArray").field(device).finish(),
196 #[cfg(feature = "tch")]
197 Self::LibTorch(device) => f.debug_tuple("LibTorch").field(device).finish(),
198 #[cfg(feature = "remote")]
199 Self::Remote(device) => f.debug_tuple("Remote").field(device).finish(),
200 #[cfg(feature = "autodiff")]
201 Self::Autodiff(device) => f.debug_tuple("Autodiff").field(&device.inner).finish(),
203 }
204 }
205}
206
207impl Default for DispatchDevice {
208 #[allow(unreachable_code)]
209 fn default() -> Self {
210 #[cfg(feature = "std")]
217 {
218 if let Ok(device_str) = std::env::var("BURN_DEVICE") {
219 match device_str.to_lowercase().as_str() {
220 "cuda" => {
221 #[cfg(feature = "cuda")]
222 return Self::Cuda(CudaDevice::default());
223 panic!(
224 "BURN_DEVICE=cuda requested, but the 'cuda' feature is not enabled."
225 );
226 }
227 "metal" => {
228 #[cfg(feature = "metal")]
229 return Self::Metal(burn_wgpu::WgpuDevice::default());
230 panic!(
231 "BURN_DEVICE=metal requested, but the 'metal' feature is not enabled."
232 );
233 }
234 "rocm" => {
235 #[cfg(feature = "rocm")]
236 return Self::Rocm(RocmDevice::default());
237 panic!(
238 "BURN_DEVICE=rocm requested, but the 'rocm' feature is not enabled."
239 );
240 }
241 "vulkan" => {
242 #[cfg(feature = "vulkan")]
243 return Self::Vulkan(burn_wgpu::WgpuDevice::default());
244 panic!(
245 "BURN_DEVICE=vulkan requested, but the 'vulkan' feature is not enabled."
246 );
247 }
248 "webgpu" => {
249 #[cfg(feature = "webgpu")]
250 return Self::WebGpu(burn_wgpu::WgpuDevice::default());
251 panic!(
252 "BURN_DEVICE=webgpu requested, but the 'webgpu' feature is not enabled."
253 );
254 }
255 "wgpu" => {
256 #[cfg(feature = "wgpu")]
257 return Self::Wgpu(burn_wgpu::WgpuDevice::default());
258 panic!(
259 "BURN_DEVICE=wgpu requested, but the 'wgpu' feature is not enabled."
260 );
261 }
262 "cpu" => {
263 #[cfg(feature = "cpu")]
264 return Self::Cpu(CpuDevice);
265 panic!("BURN_DEVICE=cpu requested, but the 'cpu' feature is not enabled.");
266 }
267 "tch" => {
268 #[cfg(feature = "tch")]
269 return Self::LibTorch(LibTorchDevice::default());
270 panic!("BURN_DEVICE=tch requested, but the 'tch' feature is not enabled.");
271 }
272 "remote" => {
273 #[cfg(feature = "remote")]
274 return Self::Remote(RemoteDevice::default());
275 panic!(
276 "BURN_DEVICE=remote requested, but the 'remote' feature is not enabled."
277 );
278 }
279 "flex" => {
280 #[cfg(any(feature = "flex", default_backend))]
281 return Self::Flex(FlexDevice);
282 panic!(
283 "BURN_DEVICE=flex requested, but the 'flex' feature is not enabled."
284 );
285 }
286 "ndarray" => {
287 #[cfg(feature = "ndarray")]
288 return Self::NdArray(NdArrayDevice::default());
289 panic!(
290 "BURN_DEVICE=ndarray requested, but the 'ndarray' feature is not enabled."
291 );
292 }
293 _ => panic!("Unknown BURN_DEVICE override: '{}'.", device_str),
294 }
295 }
296 }
297
298 #[cfg(feature = "cuda")]
299 return Self::Cuda(CudaDevice::default());
300
301 #[cfg(feature = "metal")]
302 return Self::Metal(burn_wgpu::WgpuDevice::default());
303
304 #[cfg(feature = "rocm")]
305 return Self::Rocm(RocmDevice::default());
306
307 #[cfg(feature = "vulkan")]
308 return Self::Vulkan(burn_wgpu::WgpuDevice::default());
309
310 #[cfg(feature = "webgpu")]
311 return Self::WebGpu(burn_wgpu::WgpuDevice::default());
312
313 #[cfg(feature = "wgpu")]
314 return Self::Wgpu(burn_wgpu::WgpuDevice::default());
315
316 #[cfg(feature = "cpu")]
317 return Self::Cpu(CpuDevice);
318
319 #[cfg(feature = "tch")]
320 return Self::LibTorch(LibTorchDevice::default());
321
322 #[cfg(any(feature = "flex", default_backend))]
325 return Self::Flex(FlexDevice);
326
327 #[cfg(feature = "remote")]
328 return Self::Remote(RemoteDevice::default());
329
330 #[cfg(feature = "ndarray")]
331 return Self::NdArray(NdArrayDevice::default());
332 }
333}
334
335impl PartialEq for DispatchDevice {
336 fn eq(&self, other: &Self) -> bool {
341 match (self, other) {
342 #[cfg(feature = "autodiff")]
344 (DispatchDevice::Autodiff(a), DispatchDevice::Autodiff(b)) => a == b,
345 #[cfg(feature = "autodiff")]
347 (DispatchDevice::Autodiff(a), b) => a.inner.as_ref() == b,
348 #[cfg(feature = "autodiff")]
349 (a, DispatchDevice::Autodiff(b)) => a == b.inner.as_ref(),
350 #[cfg(feature = "cpu")]
351 (Self::Cpu(a), Self::Cpu(b)) => a == b,
352 #[cfg(feature = "cuda")]
353 (Self::Cuda(a), Self::Cuda(b)) => a == b,
354 #[cfg(feature = "metal")]
355 (Self::Metal(a), Self::Metal(b)) => a == b,
356 #[cfg(feature = "rocm")]
357 (Self::Rocm(a), Self::Rocm(b)) => a == b,
358 #[cfg(feature = "vulkan")]
359 (Self::Vulkan(a), Self::Vulkan(b)) => a == b,
360 #[cfg(feature = "wgpu")]
361 (Self::Wgpu(a), Self::Wgpu(b)) => a == b,
362 #[cfg(feature = "webgpu")]
363 (Self::WebGpu(a), Self::WebGpu(b)) => a == b,
364 #[cfg(any(feature = "flex", default_backend))]
365 (Self::Flex(a), Self::Flex(b)) => a == b,
366 #[cfg(feature = "ndarray")]
367 (Self::NdArray(a), Self::NdArray(b)) => a == b,
368 #[cfg(feature = "tch")]
369 (Self::LibTorch(a), Self::LibTorch(b)) => a == b,
370 #[cfg(feature = "remote")]
371 (Self::Remote(a), Self::Remote(b)) => a == b,
372 #[allow(unreachable_patterns)]
373 (_, _) => false,
374 }
375 }
376}
377
378const INTERNAL_ID_MASK: u16 = 0x00FF;
379const BACKEND_SHIFT: u32 = 8;
380
381impl DispatchDevice {
382 #[cfg(feature = "autodiff")]
383 pub fn autodiff(device: impl Into<DispatchDevice>) -> DispatchDevice {
385 Self::autodiff_checkpointed(device, CheckpointingStrategy::None)
386 }
387 #[cfg(feature = "autodiff")]
388 pub fn autodiff_checkpointed(
390 device: impl Into<DispatchDevice>,
391 checkpointing: CheckpointingStrategy,
392 ) -> DispatchDevice {
393 let device = device.into();
394 DispatchDevice::Autodiff(AutodiffDevice::new(device, checkpointing))
395 }
396
397 pub fn inner(self) -> Self {
399 #[cfg(feature = "autodiff")]
400 if let DispatchDevice::Autodiff(device) = self {
401 return *device.inner;
402 }
403
404 self
405 }
406
407 fn backend_id(&self) -> DispatchDeviceId {
409 match self {
410 #[cfg(feature = "cpu")]
411 Self::Cpu(_) => DispatchDeviceId::Cpu,
412 #[cfg(feature = "cuda")]
413 Self::Cuda(_) => DispatchDeviceId::Cuda,
414 #[cfg(feature = "metal")]
415 Self::Metal(_) => DispatchDeviceId::Metal,
416 #[cfg(feature = "rocm")]
417 Self::Rocm(_) => DispatchDeviceId::Rocm,
418 #[cfg(feature = "vulkan")]
419 Self::Vulkan(_) => DispatchDeviceId::Vulkan,
420 #[cfg(feature = "wgpu")]
421 Self::Wgpu(_) => DispatchDeviceId::Wgpu,
422 #[cfg(feature = "webgpu")]
423 Self::WebGpu(_) => DispatchDeviceId::WebGpu,
424 #[cfg(any(feature = "flex", default_backend))]
425 Self::Flex(_) => DispatchDeviceId::Flex,
426 #[cfg(feature = "ndarray")]
427 Self::NdArray(_) => DispatchDeviceId::NdArray,
428 #[cfg(feature = "tch")]
429 Self::LibTorch(_) => DispatchDeviceId::LibTorch,
430 #[cfg(feature = "remote")]
431 Self::Remote(_) => DispatchDeviceId::Remote,
432 #[cfg(feature = "autodiff")]
433 Self::Autodiff(device) => device.inner.backend_id(),
434 }
435 }
436
437 fn encode_type_id(&self, backend_type_id: u16) -> u16 {
439 let internal_type_id = backend_type_id & INTERNAL_ID_MASK;
441 let backend = u16::from(self.backend_id()) << BACKEND_SHIFT;
443 backend | internal_type_id
444 }
445
446 pub(crate) fn decode_type_id(type_id: u16) -> (DispatchDeviceId, u16) {
448 let backend_raw = type_id >> BACKEND_SHIFT;
449 let internal_type_id = type_id & INTERNAL_ID_MASK;
450
451 let backend = DispatchDeviceId::try_from(backend_raw).expect("Unknown DispatchDevice ID");
452
453 (backend, internal_type_id)
454 }
455}
456
457#[allow(missing_docs)]
458#[derive(Debug, Clone, Copy, PartialEq, Eq)]
459#[repr(u16)]
460pub enum DispatchDeviceId {
461 Cpu = 0,
462 Cuda = 1,
463 Wgpu = 2,
464 Rocm = 3,
465 Flex = 4,
466 LibTorch = 5,
467 NdArray = 6,
468 Metal = 7,
469 Vulkan = 8,
470 WebGpu = 9,
471 Remote = 10,
472}
473
474impl From<DispatchDeviceId> for u16 {
475 fn from(variant: DispatchDeviceId) -> Self {
476 variant as u16
477 }
478}
479
480impl TryFrom<u16> for DispatchDeviceId {
481 type Error = ();
482
483 fn try_from(value: u16) -> Result<Self, Self::Error> {
484 match value {
485 #[cfg(feature = "cpu")]
486 0 => Ok(Self::Cpu),
487 #[cfg(feature = "cuda")]
488 1 => Ok(Self::Cuda),
489 #[cfg(feature = "wgpu")]
490 2 => Ok(Self::Wgpu),
491 #[cfg(feature = "rocm")]
492 3 => Ok(Self::Rocm),
493 #[cfg(any(feature = "flex", default_backend))]
494 4 => Ok(Self::Flex),
495 #[cfg(feature = "tch")]
496 5 => Ok(Self::LibTorch),
497 #[cfg(feature = "ndarray")]
498 6 => Ok(Self::NdArray),
499 #[cfg(feature = "metal")]
500 7 => Ok(Self::Metal),
501 #[cfg(feature = "vulkan")]
502 8 => Ok(Self::Vulkan),
503 #[cfg(feature = "webgpu")]
504 9 => Ok(Self::WebGpu),
505 #[cfg(feature = "remote")]
506 10 => Ok(Self::Remote),
507 _ => Err(()),
508 }
509 }
510}
511
512impl DeviceOps for DispatchDevice {
513 fn defaults(&self) -> DeviceSettings {
514 match self {
515 #[cfg(feature = "cpu")]
516 Self::Cpu(device) => device.defaults(),
517 #[cfg(feature = "cuda")]
518 Self::Cuda(device) => device.defaults(),
519 #[cfg(feature = "metal")]
520 Self::Metal(device) => device.defaults(),
521 #[cfg(feature = "rocm")]
522 Self::Rocm(device) => device.defaults(),
523 #[cfg(feature = "vulkan")]
524 Self::Vulkan(device) => device.defaults(),
525 #[cfg(feature = "wgpu")]
526 Self::Wgpu(device) => device.defaults(),
527 #[cfg(feature = "webgpu")]
528 Self::WebGpu(device) => device.defaults(),
529 #[cfg(any(feature = "flex", default_backend))]
530 Self::Flex(device) => device.defaults(),
531 #[cfg(feature = "ndarray")]
532 Self::NdArray(device) => device.defaults(),
533 #[cfg(feature = "tch")]
534 Self::LibTorch(device) => device.defaults(),
535 #[cfg(feature = "remote")]
536 Self::Remote(device) => device.defaults(),
537 #[cfg(feature = "autodiff")]
538 Self::Autodiff(device) => device.inner.defaults(),
539 }
540 }
541}
542
543impl burn_backend::Device for DispatchDevice {
544 fn from_id(mut device_id: DeviceId) -> Self {
545 let (dispatch_id, backend_type_id) = Self::decode_type_id(device_id.type_id);
546 device_id.type_id = backend_type_id;
547
548 match dispatch_id {
549 #[cfg(feature = "cpu")]
550 DispatchDeviceId::Cpu => Self::Cpu(CpuDevice::from_id(device_id)),
551 #[cfg(feature = "cuda")]
552 DispatchDeviceId::Cuda => Self::Cuda(CudaDevice::from_id(device_id)),
553 #[cfg(feature = "metal")]
554 DispatchDeviceId::Metal => Self::Metal(WgpuDevice::from_id(device_id)),
555 #[cfg(feature = "rocm")]
556 DispatchDeviceId::Rocm => Self::Rocm(RocmDevice::from_id(device_id)),
557 #[cfg(feature = "vulkan")]
558 DispatchDeviceId::Vulkan => Self::Vulkan(WgpuDevice::from_id(device_id)),
559 #[cfg(feature = "wgpu")]
560 DispatchDeviceId::Wgpu => Self::Wgpu(WgpuDevice::from_id(device_id)),
561 #[cfg(feature = "webgpu")]
562 DispatchDeviceId::WebGpu => Self::WebGpu(WgpuDevice::from_id(device_id)),
563 #[cfg(any(feature = "flex", default_backend))]
564 DispatchDeviceId::Flex => Self::Flex(FlexDevice::from_id(device_id)),
565 #[cfg(feature = "ndarray")]
566 DispatchDeviceId::NdArray => Self::NdArray(NdArrayDevice::from_id(device_id)),
567 #[cfg(feature = "tch")]
568 DispatchDeviceId::LibTorch => Self::LibTorch(LibTorchDevice::from_id(device_id)),
569 #[cfg(feature = "remote")]
570 DispatchDeviceId::Remote => Self::Remote(RemoteDevice::from_id(device_id)),
571 _ => unreachable!("No backend feature enabled."),
572 }
573 }
574
575 fn to_id(&self) -> DeviceId {
576 let mut device_id = match self {
577 #[cfg(feature = "cpu")]
578 Self::Cpu(device) => device.to_id(),
579 #[cfg(feature = "cuda")]
580 Self::Cuda(device) => device.to_id(),
581 #[cfg(feature = "metal")]
582 Self::Metal(device) => device.to_id(),
583 #[cfg(feature = "rocm")]
584 Self::Rocm(device) => device.to_id(),
585 #[cfg(feature = "vulkan")]
586 Self::Vulkan(device) => device.to_id(),
587 #[cfg(feature = "wgpu")]
588 Self::Wgpu(device) => device.to_id(),
589 #[cfg(feature = "webgpu")]
590 Self::WebGpu(device) => device.to_id(),
591 #[cfg(any(feature = "flex", default_backend))]
592 Self::Flex(device) => device.to_id(),
593 #[cfg(feature = "ndarray")]
594 Self::NdArray(device) => device.to_id(),
595 #[cfg(feature = "tch")]
596 Self::LibTorch(device) => device.to_id(),
597 #[cfg(feature = "remote")]
598 Self::Remote(device) => device.to_id(),
599 #[cfg(feature = "autodiff")]
600 Self::Autodiff(device) => device.inner.to_id(),
601 };
602 device_id.type_id = self.encode_type_id(device_id.type_id);
603 device_id
604 }
605}
606
607#[cfg(feature = "cpu")]
608impl From<CpuDevice> for DispatchDevice {
609 fn from(device: CpuDevice) -> Self {
610 DispatchDevice::Cpu(device)
611 }
612}
613
614#[cfg(feature = "cuda")]
615impl From<CudaDevice> for DispatchDevice {
616 fn from(device: CudaDevice) -> Self {
617 DispatchDevice::Cuda(device)
618 }
619}
620
621#[cfg(feature = "rocm")]
622impl From<RocmDevice> for DispatchDevice {
623 fn from(device: RocmDevice) -> Self {
624 DispatchDevice::Rocm(device)
625 }
626}
627
628#[cfg(all(
631 feature = "wgpu",
632 not(any(feature = "metal", feature = "vulkan", feature = "webgpu"))
633))]
634impl From<WgpuDevice> for DispatchDevice {
635 fn from(device: WgpuDevice) -> Self {
636 DispatchDevice::Wgpu(device)
637 }
638}
639
640#[cfg(all(feature = "metal", not(any(feature = "vulkan", feature = "webgpu"))))]
641impl From<WgpuDevice> for DispatchDevice {
642 fn from(device: WgpuDevice) -> Self {
643 DispatchDevice::Metal(device)
644 }
645}
646
647#[cfg(all(feature = "vulkan", not(any(feature = "metal", feature = "webgpu"))))]
648impl From<WgpuDevice> for DispatchDevice {
649 fn from(device: WgpuDevice) -> Self {
650 DispatchDevice::Vulkan(device)
651 }
652}
653
654#[cfg(all(feature = "webgpu", not(any(feature = "metal", feature = "vulkan"))))]
655impl From<WgpuDevice> for DispatchDevice {
656 fn from(device: WgpuDevice) -> Self {
657 DispatchDevice::WebGpu(device)
658 }
659}
660
661#[cfg(any(feature = "flex", default_backend))]
662impl From<FlexDevice> for DispatchDevice {
663 fn from(device: FlexDevice) -> Self {
664 DispatchDevice::Flex(device)
665 }
666}
667
668#[cfg(feature = "ndarray")]
669impl From<NdArrayDevice> for DispatchDevice {
670 fn from(device: NdArrayDevice) -> Self {
671 DispatchDevice::NdArray(device)
672 }
673}
674
675#[cfg(feature = "tch")]
676impl From<LibTorchDevice> for DispatchDevice {
677 fn from(device: LibTorchDevice) -> Self {
678 DispatchDevice::LibTorch(device)
679 }
680}
681
682#[cfg(feature = "remote")]
683impl From<RemoteDevice> for DispatchDevice {
684 fn from(device: RemoteDevice) -> Self {
685 DispatchDevice::Remote(device)
686 }
687}