]> git.proxmox.com Git - rustc.git/blob - vendor/num_cpus/src/lib.rs
New upstream version 1.32.0~beta.2+dfsg1
[rustc.git] / vendor / num_cpus / src / lib.rs
1 //! A crate with utilities to determine the number of CPUs available on the
2 //! current system.
3 //!
4 //! Sometimes the CPU will exaggerate the number of CPUs it contains, because it can use
5 //! [processor tricks] to deliver increased performance when there are more threads. This
6 //! crate provides methods to get both the logical and physical numbers of cores.
7 //!
8 //! This information can be used as a guide to how many tasks can be run in parallel.
9 //! There are many properties of the system architecture that will affect parallelism,
10 //! for example memory access speeds (for all the caches and RAM) and the physical
11 //! architecture of the processor, so the number of CPUs should be used as a rough guide
12 //! only.
13 //!
14 //!
15 //! ## Examples
16 //!
17 //! Fetch the number of logical CPUs.
18 //!
19 //! ```
20 //! let cpus = num_cpus::get();
21 //! ```
22 //!
23 //! See [`rayon::Threadpool`] for an example of where the number of CPUs could be
24 //! used when setting up parallel jobs (Where the threadpool example uses a fixed
25 //! number 8, it could use the number of CPUs).
26 //!
27 //! [processor tricks]: https://en.wikipedia.org/wiki/Simultaneous_multithreading
28 //! [`rayon::ThreadPool`]: https://docs.rs/rayon/0.8.2/rayon/struct.ThreadPool.html
29 #![cfg_attr(test, deny(warnings))]
30 #![deny(missing_docs)]
31 #![doc(html_root_url = "https://docs.rs/num_cpus/1.8.0")]
32 #![allow(non_snake_case)]
33
34 #[cfg(not(windows))]
35 extern crate libc;
36
37
38 /// Returns the number of available CPUs of the current system.
39 ///
40 /// This function will get the number of logical cores. Sometimes this is different from the number
41 /// of physical cores (See [Simultaneous multithreading on Wikipedia][smt]).
42 ///
43 /// # Examples
44 ///
45 /// ```
46 /// let cpus = num_cpus::get();
47 /// if cpus > 1 {
48 /// println!("We are on a multicore system with {} CPUs", cpus);
49 /// } else {
50 /// println!("We are on a single core system");
51 /// }
52 /// ```
53 ///
54 /// # Note
55 ///
56 /// This will check [sched affinity] on Linux, showing a lower number of CPUs if the current
57 /// thread does not have access to all the computer's CPUs.
58 ///
59 /// [smt]: https://en.wikipedia.org/wiki/Simultaneous_multithreading
60 /// [sched affinity]: http://www.gnu.org/software/libc/manual/html_node/CPU-Affinity.html
61 #[inline]
62 pub fn get() -> usize {
63 get_num_cpus()
64 }
65
66 /// Returns the number of physical cores of the current system.
67 ///
68 /// # Note
69 ///
70 /// Physical count is supported only on Linux, mac OS and Windows platforms.
71 /// On other platforms, or if the physical count fails on supported platforms,
72 /// this function returns the same as [`get()`], which is the number of logical
73 /// CPUS.
74 ///
75 /// # Examples
76 ///
77 /// ```
78 /// let logical_cpus = num_cpus::get();
79 /// let physical_cpus = num_cpus::get_physical();
80 /// if logical_cpus > physical_cpus {
81 /// println!("We have simultaneous multithreading with about {:.2} \
82 /// logical cores to 1 physical core.",
83 /// (logical_cpus as f64) / (physical_cpus as f64));
84 /// } else if logical_cpus == physical_cpus {
85 /// println!("Either we don't have simultaneous multithreading, or our \
86 /// system doesn't support getting the number of physical CPUs.");
87 /// } else {
88 /// println!("We have less logical CPUs than physical CPUs, maybe we only have access to \
89 /// some of the CPUs on our system.");
90 /// }
91 /// ```
92 ///
93 /// [`get()`]: fn.get.html
94 #[inline]
95 pub fn get_physical() -> usize {
96 get_num_physical_cpus()
97 }
98
99
100 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os="macos")))]
101 #[inline]
102 fn get_num_physical_cpus() -> usize {
103 // Not implemented, fall back
104 get_num_cpus()
105 }
106
107 #[cfg(target_os = "windows")]
108 fn get_num_physical_cpus() -> usize {
109 match get_num_physical_cpus_windows() {
110 Some(num) => num,
111 None => get_num_cpus()
112 }
113 }
114
115 #[cfg(target_os = "windows")]
116 fn get_num_physical_cpus_windows() -> Option<usize> {
117 // Inspired by https://msdn.microsoft.com/en-us/library/ms683194
118
119 use std::ptr;
120 use std::mem;
121
122 #[allow(non_upper_case_globals)]
123 const RelationProcessorCore: u32 = 0;
124
125 #[repr(C)]
126 #[allow(non_camel_case_types)]
127 struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
128 mask: usize,
129 relationship: u32,
130 _unused: [u64; 2]
131 }
132
133 extern "system" {
134 fn GetLogicalProcessorInformation(
135 info: *mut SYSTEM_LOGICAL_PROCESSOR_INFORMATION,
136 length: &mut u32
137 ) -> u32;
138 }
139
140 // First we need to determine how much space to reserve.
141
142 // The required size of the buffer, in bytes.
143 let mut needed_size = 0;
144
145 unsafe {
146 GetLogicalProcessorInformation(ptr::null_mut(), &mut needed_size);
147 }
148
149 let struct_size = mem::size_of::<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>() as u32;
150
151 // Could be 0, or some other bogus size.
152 if needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0 {
153 return None;
154 }
155
156 let count = needed_size / struct_size;
157
158 // Allocate some memory where we will store the processor info.
159 let mut buf = Vec::with_capacity(count as usize);
160
161 let result;
162
163 unsafe {
164 result = GetLogicalProcessorInformation(buf.as_mut_ptr(), &mut needed_size);
165 }
166
167 // Failed for any reason.
168 if result == 0 {
169 return None;
170 }
171
172 let count = needed_size / struct_size;
173
174 unsafe {
175 buf.set_len(count as usize);
176 }
177
178 let phys_proc_count = buf.iter()
179 // Only interested in processor packages (physical processors.)
180 .filter(|proc_info| proc_info.relationship == RelationProcessorCore)
181 .count();
182
183 if phys_proc_count == 0 {
184 None
185 } else {
186 Some(phys_proc_count)
187 }
188 }
189
190 #[cfg(target_os = "linux")]
191 fn get_num_physical_cpus() -> usize {
192 use std::io::BufReader;
193 use std::io::BufRead;
194 use std::fs::File;
195 use std::collections::HashSet;
196
197 let file = match File::open("/proc/cpuinfo") {
198 Ok(val) => val,
199 Err(_) => {return get_num_cpus()},
200 };
201 let reader = BufReader::new(file);
202 let mut set = HashSet::new();
203 let mut coreid: u32 = 0;
204 let mut physid: u32 = 0;
205 let mut chgcount = 0;
206 for line in reader.lines().filter_map(|result| result.ok()) {
207 let parts: Vec<&str> = line.split(':').map(|s| s.trim()).collect();
208 if parts.len() != 2 {
209 continue
210 }
211 if parts[0] == "core id" || parts[0] == "physical id" {
212 let value = match parts[1].trim().parse() {
213 Ok(val) => val,
214 Err(_) => break,
215 };
216 match parts[0] {
217 "core id" => coreid = value,
218 "physical id" => physid = value,
219 _ => {},
220 }
221 chgcount += 1;
222 }
223 if chgcount == 2 {
224 set.insert((physid, coreid));
225 chgcount = 0;
226 }
227 }
228 let count = set.len();
229 if count == 0 { get_num_cpus() } else { count }
230 }
231
232 #[cfg(windows)]
233 fn get_num_cpus() -> usize {
234 #[repr(C)]
235 struct SYSTEM_INFO {
236 wProcessorArchitecture: u16,
237 wReserved: u16,
238 dwPageSize: u32,
239 lpMinimumApplicationAddress: *mut u8,
240 lpMaximumApplicationAddress: *mut u8,
241 dwActiveProcessorMask: *mut u8,
242 dwNumberOfProcessors: u32,
243 dwProcessorType: u32,
244 dwAllocationGranularity: u32,
245 wProcessorLevel: u16,
246 wProcessorRevision: u16,
247 }
248
249 extern "system" {
250 fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
251 }
252
253 unsafe {
254 let mut sysinfo: SYSTEM_INFO = std::mem::uninitialized();
255 GetSystemInfo(&mut sysinfo);
256 sysinfo.dwNumberOfProcessors as usize
257 }
258 }
259
260 #[cfg(any(target_os = "freebsd",
261 target_os = "dragonfly",
262 target_os = "bitrig",
263 target_os = "netbsd"))]
264 fn get_num_cpus() -> usize {
265 let mut cpus: libc::c_uint = 0;
266 let mut cpus_size = std::mem::size_of_val(&cpus);
267
268 unsafe {
269 cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
270 }
271 if cpus < 1 {
272 let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
273 unsafe {
274 libc::sysctl(mib.as_mut_ptr(),
275 2,
276 &mut cpus as *mut _ as *mut _,
277 &mut cpus_size as *mut _ as *mut _,
278 0 as *mut _,
279 0);
280 }
281 if cpus < 1 {
282 cpus = 1;
283 }
284 }
285 cpus as usize
286 }
287
288 #[cfg(target_os = "openbsd")]
289 fn get_num_cpus() -> usize {
290 let mut cpus: libc::c_uint = 0;
291 let mut cpus_size = std::mem::size_of_val(&cpus);
292 let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
293
294 unsafe {
295 libc::sysctl(mib.as_mut_ptr(),
296 2,
297 &mut cpus as *mut _ as *mut _,
298 &mut cpus_size as *mut _ as *mut _,
299 0 as *mut _,
300 0);
301 }
302 if cpus < 1 {
303 cpus = 1;
304 }
305 cpus as usize
306 }
307
308
309 #[cfg(target_os = "macos")]
310 fn get_num_physical_cpus() -> usize {
311 use std::ffi::CStr;
312 use std::ptr;
313
314 let mut cpus: i32 = 0;
315 let mut cpus_size = std::mem::size_of_val(&cpus);
316
317 let sysctl_name = CStr::from_bytes_with_nul(b"hw.physicalcpu\0")
318 .expect("byte literal is missing NUL");
319
320 unsafe {
321 if 0 != libc::sysctlbyname(sysctl_name.as_ptr(),
322 &mut cpus as *mut _ as *mut _,
323 &mut cpus_size as *mut _ as *mut _,
324 ptr::null_mut(),
325 0) {
326 return get_num_cpus();
327 }
328 }
329 cpus as usize
330 }
331
332 #[cfg(target_os = "linux")]
333 fn get_num_cpus() -> usize {
334 let mut set: libc::cpu_set_t = unsafe { std::mem::zeroed() };
335 if unsafe { libc::sched_getaffinity(0, std::mem::size_of::<libc::cpu_set_t>(), &mut set) } == 0 {
336 let mut count: u32 = 0;
337 for i in 0..libc::CPU_SETSIZE as usize {
338 if unsafe { libc::CPU_ISSET(i, &set) } {
339 count += 1
340 }
341 }
342 count as usize
343 } else {
344 let cpus = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) };
345 if cpus < 1 {
346 1
347 } else {
348 cpus as usize
349 }
350 }
351 }
352
353 #[cfg(any(
354 target_os = "nacl",
355 target_os = "macos",
356 target_os = "ios",
357 target_os = "android",
358 target_os = "solaris",
359 target_os = "fuchsia")
360 )]
361 fn get_num_cpus() -> usize {
362 // On ARM targets, processors could be turned off to save power.
363 // Use `_SC_NPROCESSORS_CONF` to get the real number.
364 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
365 const CONF_NAME: libc::c_int = libc::_SC_NPROCESSORS_CONF;
366 #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
367 const CONF_NAME: libc::c_int = libc::_SC_NPROCESSORS_ONLN;
368
369 let cpus = unsafe { libc::sysconf(CONF_NAME) };
370 if cpus < 1 {
371 1
372 } else {
373 cpus as usize
374 }
375 }
376
377 #[cfg(any(target_os = "emscripten", target_os = "redox", target_os = "haiku"))]
378 fn get_num_cpus() -> usize {
379 1
380 }
381
382 #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
383 fn get_num_cpus() -> usize {
384 1
385 }
386
387 #[cfg(test)]
388 mod tests {
389 fn env_var(name: &'static str) -> Option<usize> {
390 ::std::env::var(name).ok().map(|val| val.parse().unwrap())
391 }
392
393 #[test]
394 fn test_get() {
395 let num = super::get();
396 if let Some(n) = env_var("NUM_CPUS_TEST_GET") {
397 assert_eq!(num, n);
398 } else {
399 assert!(num > 0);
400 assert!(num < 236_451);
401 }
402 }
403
404 #[test]
405 fn test_get_physical() {
406 let num = super::get_physical();
407 if let Some(n) = env_var("NUM_CPUS_TEST_GET_PHYSICAL") {
408 assert_eq!(num, n);
409 } else {
410 assert!(num > 0);
411 assert!(num < 236_451);
412 }
413 }
414 }