]>
Commit | Line | Data |
---|---|---|
041b39d2 XL |
1 | //! # num_cpus |
2 | //! | |
3 | //! A crate with utilities to determine the number of CPUs available on the | |
4 | //! current system. | |
5 | //! | |
6 | //! ## Example | |
7 | //! | |
8 | //! ``` | |
9 | //! let cpus = num_cpus::get(); | |
10 | //! ``` | |
476ff2be SL |
11 | #![cfg_attr(test, deny(warnings))] |
12 | #![deny(missing_docs)] | |
13 | #![allow(non_snake_case)] | |
14 | ||
15 | #[cfg(not(windows))] | |
16 | extern crate libc; | |
17 | ||
041b39d2 XL |
18 | /// Returns the number of available CPUs of the current system. |
19 | /// | |
20 | /// # Note | |
21 | /// | |
22 | /// This will check sched affinity on Linux. | |
476ff2be SL |
23 | #[inline] |
24 | pub fn get() -> usize { | |
25 | get_num_cpus() | |
26 | } | |
27 | ||
041b39d2 XL |
28 | /// Returns the number of physical cores of the current system. |
29 | /// | |
30 | /// If not possible on the particular architecture, returns same as `get()` | |
31 | /// which is the logical CPUs. | |
7cac9316 XL |
32 | #[inline] |
33 | pub fn get_physical() -> usize { | |
34 | get_num_physical_cpus() | |
35 | } | |
36 | ||
37 | ||
041b39d2 | 38 | #[cfg(not(any(target_os = "linux", target_os = "windows")))] |
7cac9316 XL |
39 | #[inline] |
40 | fn get_num_physical_cpus() -> usize { | |
41 | // Not implemented, fallback | |
42 | get_num_cpus() | |
43 | } | |
44 | ||
041b39d2 XL |
45 | #[cfg(target_os = "windows")] |
46 | fn get_num_physical_cpus() -> usize { | |
47 | match get_num_physical_cpus_windows() { | |
48 | Some(num) => num, | |
49 | None => get_num_cpus() | |
50 | } | |
51 | } | |
52 | ||
53 | #[cfg(target_os = "windows")] | |
54 | fn get_num_physical_cpus_windows() -> Option<usize> { | |
55 | // Inspired by https://msdn.microsoft.com/en-us/library/ms683194 | |
56 | ||
57 | use std::ptr; | |
58 | use std::mem; | |
59 | ||
60 | #[allow(non_upper_case_globals)] | |
61 | const RelationProcessorCore: u32 = 0; | |
62 | ||
63 | #[repr(C)] | |
64 | #[allow(non_camel_case_types)] | |
65 | struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION { | |
66 | mask: usize, | |
67 | relationship: u32, | |
68 | _unused: [u64; 2] | |
69 | } | |
70 | ||
71 | extern "system" { | |
72 | fn GetLogicalProcessorInformation( | |
73 | info: *mut SYSTEM_LOGICAL_PROCESSOR_INFORMATION, | |
74 | length: &mut u32 | |
75 | ) -> u32; | |
76 | } | |
77 | ||
78 | // First we need to determine how much space to reserve. | |
79 | ||
80 | // The required size of the buffer, in bytes. | |
81 | let mut needed_size = 0; | |
82 | ||
83 | unsafe { | |
84 | GetLogicalProcessorInformation(ptr::null_mut(), &mut needed_size); | |
85 | } | |
86 | ||
87 | let struct_size = mem::size_of::<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>() as u32; | |
88 | ||
89 | // Could be 0, or some other bogus size. | |
90 | if needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0 { | |
91 | return None; | |
92 | } | |
93 | ||
94 | let count = needed_size / struct_size; | |
95 | ||
96 | // Allocate some memory where we will store the processor info. | |
97 | let mut buf = Vec::with_capacity(count as usize); | |
98 | ||
99 | let result; | |
100 | ||
101 | unsafe { | |
102 | result = GetLogicalProcessorInformation(buf.as_mut_ptr(), &mut needed_size); | |
103 | } | |
104 | ||
105 | // Failed for any reason. | |
106 | if result == 0 { | |
107 | return None; | |
108 | } | |
109 | ||
110 | let count = needed_size / struct_size; | |
111 | ||
112 | unsafe { | |
113 | buf.set_len(count as usize); | |
114 | } | |
115 | ||
116 | let phys_proc_count = buf.iter() | |
117 | // Only interested in processor packages (physical processors.) | |
118 | .filter(|proc_info| proc_info.relationship == RelationProcessorCore) | |
119 | .count(); | |
120 | ||
121 | if phys_proc_count == 0 { | |
122 | None | |
123 | } else { | |
124 | Some(phys_proc_count) | |
125 | } | |
126 | } | |
127 | ||
7cac9316 XL |
128 | #[cfg(target_os = "linux")] |
129 | fn get_num_physical_cpus() -> usize { | |
130 | use std::io::BufReader; | |
131 | use std::io::BufRead; | |
132 | use std::fs::File; | |
133 | use std::collections::HashSet; | |
134 | ||
135 | let file = match File::open("/proc/cpuinfo") { | |
136 | Ok(val) => val, | |
137 | Err(_) => {return get_num_cpus()}, | |
138 | }; | |
139 | let reader = BufReader::new(file); | |
140 | let mut set = HashSet::new(); | |
141 | let mut coreid: u32 = 0; | |
142 | let mut physid: u32 = 0; | |
143 | let mut chgcount = 0; | |
144 | for line in reader.lines().filter_map(|result| result.ok()) { | |
145 | let parts: Vec<&str> = line.split(':').map(|s| s.trim()).collect(); | |
146 | if parts.len() != 2 { | |
147 | continue | |
148 | } | |
149 | if parts[0] == "core id" || parts[0] == "physical id" { | |
150 | let value = match parts[1].trim().parse() { | |
151 | Ok(val) => val, | |
152 | Err(_) => break, | |
153 | }; | |
154 | match parts[0] { | |
155 | "core id" => coreid = value, | |
156 | "physical id" => physid = value, | |
157 | _ => {}, | |
158 | } | |
159 | chgcount += 1; | |
160 | } | |
161 | if chgcount == 2 { | |
162 | set.insert((physid, coreid)); | |
163 | chgcount = 0; | |
164 | } | |
165 | } | |
166 | let count = set.len(); | |
167 | if count == 0 { get_num_cpus() } else { count } | |
168 | } | |
169 | ||
476ff2be SL |
170 | #[cfg(windows)] |
171 | fn get_num_cpus() -> usize { | |
172 | #[repr(C)] | |
173 | struct SYSTEM_INFO { | |
174 | wProcessorArchitecture: u16, | |
175 | wReserved: u16, | |
176 | dwPageSize: u32, | |
177 | lpMinimumApplicationAddress: *mut u8, | |
178 | lpMaximumApplicationAddress: *mut u8, | |
179 | dwActiveProcessorMask: *mut u8, | |
180 | dwNumberOfProcessors: u32, | |
181 | dwProcessorType: u32, | |
182 | dwAllocationGranularity: u32, | |
183 | wProcessorLevel: u16, | |
184 | wProcessorRevision: u16, | |
185 | } | |
186 | ||
187 | extern "system" { | |
188 | fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO); | |
189 | } | |
190 | ||
191 | unsafe { | |
192 | let mut sysinfo: SYSTEM_INFO = std::mem::uninitialized(); | |
193 | GetSystemInfo(&mut sysinfo); | |
194 | sysinfo.dwNumberOfProcessors as usize | |
195 | } | |
196 | } | |
197 | ||
198 | #[cfg(any(target_os = "freebsd", | |
199 | target_os = "dragonfly", | |
200 | target_os = "bitrig", | |
201 | target_os = "netbsd"))] | |
202 | fn get_num_cpus() -> usize { | |
203 | let mut cpus: libc::c_uint = 0; | |
204 | let mut cpus_size = std::mem::size_of_val(&cpus); | |
205 | ||
206 | unsafe { | |
207 | cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint; | |
208 | } | |
209 | if cpus < 1 { | |
210 | let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0]; | |
211 | unsafe { | |
212 | libc::sysctl(mib.as_mut_ptr(), | |
213 | 2, | |
214 | &mut cpus as *mut _ as *mut _, | |
215 | &mut cpus_size as *mut _ as *mut _, | |
216 | 0 as *mut _, | |
217 | 0); | |
218 | } | |
219 | if cpus < 1 { | |
220 | cpus = 1; | |
221 | } | |
222 | } | |
223 | cpus as usize | |
224 | } | |
225 | ||
226 | #[cfg(target_os = "openbsd")] | |
227 | fn get_num_cpus() -> usize { | |
228 | let mut cpus: libc::c_uint = 0; | |
229 | let mut cpus_size = std::mem::size_of_val(&cpus); | |
230 | let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0]; | |
231 | ||
232 | unsafe { | |
233 | libc::sysctl(mib.as_mut_ptr(), | |
234 | 2, | |
235 | &mut cpus as *mut _ as *mut _, | |
236 | &mut cpus_size as *mut _ as *mut _, | |
237 | 0 as *mut _, | |
238 | 0); | |
239 | } | |
240 | if cpus < 1 { | |
241 | cpus = 1; | |
242 | } | |
243 | cpus as usize | |
244 | } | |
245 | ||
041b39d2 XL |
246 | #[cfg(target_os = "linux")] |
247 | fn get_num_cpus() -> usize { | |
248 | let mut set: libc::cpu_set_t = unsafe { std::mem::zeroed() }; | |
249 | if unsafe { libc::sched_getaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &mut set) } == 0 { | |
250 | let mut count: u32 = 0; | |
251 | for i in 0..libc::CPU_SETSIZE as usize { | |
252 | if unsafe { libc::CPU_ISSET(i, &set) } { | |
253 | count += 1 | |
254 | } | |
255 | } | |
256 | count as usize | |
257 | } else { | |
258 | let cpus = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) }; | |
259 | if cpus < 1 { | |
260 | 1 | |
261 | } else { | |
262 | cpus as usize | |
263 | } | |
264 | } | |
265 | } | |
266 | ||
267 | #[cfg(any( | |
268 | target_os = "nacl", | |
269 | target_os = "macos", | |
270 | target_os = "ios", | |
271 | target_os = "android", | |
272 | target_os = "solaris", | |
273 | target_os = "fuchsia") | |
476ff2be SL |
274 | )] |
275 | fn get_num_cpus() -> usize { | |
041b39d2 XL |
276 | let cpus = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) }; |
277 | if cpus < 1 { | |
278 | 1 | |
279 | } else { | |
280 | cpus as usize | |
476ff2be SL |
281 | } |
282 | } | |
041b39d2 | 283 | |
7cac9316 XL |
284 | #[cfg(any(target_os = "emscripten", target_os = "redox", target_os = "haiku"))] |
285 | fn get_num_cpus() -> usize { | |
286 | 1 | |
287 | } | |
476ff2be | 288 | |
041b39d2 XL |
289 | #[cfg(test)] |
290 | mod tests { | |
291 | fn env_var(name: &'static str) -> Option<usize> { | |
292 | ::std::env::var(name).ok().map(|val| val.parse().unwrap()) | |
293 | } | |
476ff2be | 294 | |
041b39d2 XL |
295 | #[test] |
296 | fn test_get() { | |
297 | let num = super::get(); | |
298 | if let Some(n) = env_var("NUM_CPUS_TEST_GET") { | |
299 | assert_eq!(num, n); | |
300 | } else { | |
301 | assert!(num > 0); | |
302 | assert!(num < 236_451); | |
303 | } | |
304 | } | |
476ff2be | 305 | |
041b39d2 XL |
306 | #[test] |
307 | fn test_get_physical() { | |
308 | let num = super::get_physical(); | |
309 | if let Some(n) = env_var("NUM_CPUS_TEST_GET_PHYSICAL") { | |
310 | assert_eq!(num, n); | |
311 | } else { | |
312 | assert!(num > 0); | |
313 | assert!(num < 236_451); | |
314 | } | |
315 | } | |
476ff2be | 316 | } |