ultralytics_inference/
device.rs1use std::fmt;
5use std::str::FromStr;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum Device {
10 Cpu,
12 Cuda(usize),
15 CoreMl,
17 DirectMl(usize),
20 IntelCpu,
22 IntelGpu,
24 IntelNpu,
26 Xnnpack,
28 TensorRt(usize),
31 Rocm(usize),
34}
35
36impl fmt::Display for Device {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 match self {
39 Self::Cpu => write!(f, "cpu"),
40 Self::Cuda(i) => write!(f, "cuda:{i}"),
41 Self::CoreMl => write!(f, "coreml"),
42 Self::DirectMl(i) => write!(f, "directml:{i}"),
43 Self::IntelCpu => write!(f, "intel:cpu"),
44 Self::IntelGpu => write!(f, "intel:gpu"),
45 Self::IntelNpu => write!(f, "intel:npu"),
46 Self::Xnnpack => write!(f, "xnnpack"),
47 Self::TensorRt(i) => write!(f, "tensorrt:{i}"),
48 Self::Rocm(i) => write!(f, "rocm:{i}"),
49 }
50 }
51}
52
53impl FromStr for Device {
54 type Err = String;
55
56 fn from_str(s: &str) -> Result<Self, Self::Err> {
57 let s = s.to_lowercase();
58 if let Some(rest) = s.strip_prefix("cuda") {
59 return Ok(Self::Cuda(parse_device_index(rest, &s)?));
60 }
61 if let Some(rest) = s.strip_prefix("directml") {
62 return Ok(Self::DirectMl(parse_device_index(rest, &s)?));
63 }
64 if let Some(rest) = s.strip_prefix("tensorrt") {
65 return Ok(Self::TensorRt(parse_device_index(rest, &s)?));
66 }
67 if let Some(rest) = s.strip_prefix("rocm") {
68 return Ok(Self::Rocm(parse_device_index(rest, &s)?));
69 }
70 match s.as_str() {
71 "cpu" => Ok(Self::Cpu),
72 "coreml" => Ok(Self::CoreMl),
73 "xnnpack" => Ok(Self::Xnnpack),
74 "intel:cpu" => Ok(Self::IntelCpu),
76 "intel:gpu" => Ok(Self::IntelGpu),
77 "intel:npu" => Ok(Self::IntelNpu),
78 _ => Err(format!("Unknown device: {s}")),
79 }
80 }
81}
82
83fn parse_device_index(s: &str, full: &str) -> Result<usize, String> {
89 match s.strip_prefix(':') {
90 None if s.is_empty() => Ok(0),
91 Some(index) => index
92 .parse()
93 .map_err(|_| format!("Invalid device index in '{full}': expected an integer")),
94 None => Err(format!("Unknown device: {full}")),
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_parse_device() {
104 assert_eq!(Device::from_str("cpu").unwrap(), Device::Cpu);
105 assert_eq!(Device::from_str("cuda").unwrap(), Device::Cuda(0));
106 assert_eq!(Device::from_str("cuda:0").unwrap(), Device::Cuda(0));
107 assert_eq!(Device::from_str("cuda:1").unwrap(), Device::Cuda(1));
108 assert_eq!(Device::from_str("coreml").unwrap(), Device::CoreMl);
109 assert_eq!(Device::from_str("directml").unwrap(), Device::DirectMl(0));
110 assert_eq!(Device::from_str("directml:1").unwrap(), Device::DirectMl(1));
111 assert_eq!(Device::from_str("intel:cpu").unwrap(), Device::IntelCpu);
113 assert_eq!(Device::from_str("intel:gpu").unwrap(), Device::IntelGpu);
114 assert_eq!(Device::from_str("intel:npu").unwrap(), Device::IntelNpu);
115 assert!(Device::from_str("intel").is_err());
116 assert!(Device::from_str("intel:tpu").is_err());
117 assert!(Device::from_str("openvino").is_err());
118 }
119
120 #[test]
122 fn test_parse_device_rejects_bad_index() {
123 for s in [
124 "cuda:abc",
125 "cuda:-1",
126 "cuda:",
127 "cuda:1.5",
128 "cudax",
129 "cuda0",
130 "tensorrt:x",
131 "rocm:-2",
132 "directmlfoo",
133 "cuda:99999999999999999999",
134 ] {
135 assert!(Device::from_str(s).is_err(), "{s} should not parse");
136 }
137 }
138
139 #[test]
140 fn test_device_display_roundtrip() {
141 for s in [
142 "cpu",
143 "cuda:0",
144 "cuda:1",
145 "coreml",
146 "directml:0",
147 "tensorrt:2",
148 "rocm:3",
149 "intel:cpu",
150 "intel:gpu",
151 "intel:npu",
152 "xnnpack",
153 ] {
154 assert_eq!(Device::from_str(s).unwrap().to_string(), s);
155 }
156 }
157}