1 //! A crate with utilities to determine the number of CPUs available on the
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.
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
17 //! Fetch the number of logical CPUs.
20 //! let cpus = num_cpus::get();
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).
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)]
38 /// Returns the number of available CPUs of the current system.
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]).
46 /// let cpus = num_cpus::get();
48 /// println!("We are on a multicore system with {} CPUs", cpus);
50 /// println!("We are on a single core system");
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.
59 /// [smt]: https://en.wikipedia.org/wiki/Simultaneous_multithreading
60 /// [sched affinity]: http://www.gnu.org/software/libc/manual/html_node/CPU-Affinity.html
62 pub fn get() -> usize {
66 /// Returns the number of physical cores of the current system.
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
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.");
88 /// println!("We have less logical CPUs than physical CPUs, maybe we only have access to \
89 /// some of the CPUs on our system.");
93 /// [`get()`]: fn.get.html
95 pub fn get_physical() -> usize {
96 get_num_physical_cpus()
100 #[cfg(not(any(target_os = "linux", target_os = "windows", target_os="macos")))]
102 fn get_num_physical_cpus() -> usize {
103 // Not implemented, fall back
107 #[cfg(target_os = "windows")]
108 fn get_num_physical_cpus() -> usize {
109 match get_num_physical_cpus_windows() {
111 None
=> get_num_cpus()
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
122 #[allow(non_upper_case_globals)]
123 const RelationProcessorCore
: u32 = 0;
126 #[allow(non_camel_case_types)]
127 struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION
{
134 fn GetLogicalProcessorInformation(
135 info
: *mut SYSTEM_LOGICAL_PROCESSOR_INFORMATION
,
140 // First we need to determine how much space to reserve.
142 // The required size of the buffer, in bytes.
143 let mut needed_size
= 0;
146 GetLogicalProcessorInformation(ptr
::null_mut(), &mut needed_size
);
149 let struct_size
= mem
::size_of
::<SYSTEM_LOGICAL_PROCESSOR_INFORMATION
>() as u32;
151 // Could be 0, or some other bogus size.
152 if needed_size
== 0 || needed_size
< struct_size
|| needed_size
% struct_size
!= 0 {
156 let count
= needed_size
/ struct_size
;
158 // Allocate some memory where we will store the processor info.
159 let mut buf
= Vec
::with_capacity(count
as usize);
164 result
= GetLogicalProcessorInformation(buf
.as_mut_ptr(), &mut needed_size
);
167 // Failed for any reason.
172 let count
= needed_size
/ struct_size
;
175 buf
.set_len(count
as usize);
178 let phys_proc_count
= buf
.iter()
179 // Only interested in processor packages (physical processors.)
180 .filter(|proc_info
| proc_info
.relationship
== RelationProcessorCore
)
183 if phys_proc_count
== 0 {
186 Some(phys_proc_count
)
190 #[cfg(target_os = "linux")]
191 fn get_num_physical_cpus() -> usize {
192 use std
::io
::BufReader
;
193 use std
::io
::BufRead
;
195 use std
::collections
::HashSet
;
197 let file
= match File
::open("/proc/cpuinfo") {
199 Err(_
) => {return get_num_cpus()}
,
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 {
211 if parts
[0] == "core id" || parts
[0] == "physical id" {
212 let value
= match parts
[1].trim().parse() {
217 "core id" => coreid
= value
,
218 "physical id" => physid
= value
,
224 set
.insert((physid
, coreid
));
228 let count
= set
.len();
229 if count
== 0 { get_num_cpus() }
else { count }
233 fn get_num_cpus() -> usize {
236 wProcessorArchitecture
: u16,
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,
250 fn GetSystemInfo(lpSystemInfo
: *mut SYSTEM_INFO
);
254 let mut sysinfo
: SYSTEM_INFO
= std
::mem
::uninitialized();
255 GetSystemInfo(&mut sysinfo
);
256 sysinfo
.dwNumberOfProcessors
as usize
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
);
269 cpus
= libc
::sysconf(libc
::_SC_NPROCESSORS_ONLN
) as libc
::c_uint
;
272 let mut mib
= [libc
::CTL_HW
, libc
::HW_NCPU
, 0, 0];
274 libc
::sysctl(mib
.as_mut_ptr(),
276 &mut cpus
as *mut _
as *mut _
,
277 &mut cpus_size
as *mut _
as *mut _
,
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];
295 libc
::sysctl(mib
.as_mut_ptr(),
297 &mut cpus
as *mut _
as *mut _
,
298 &mut cpus_size
as *mut _
as *mut _
,
309 #[cfg(target_os = "macos")]
310 fn get_num_physical_cpus() -> usize {
314 let mut cpus
: i32 = 0;
315 let mut cpus_size
= std
::mem
::size_of_val(&cpus
);
317 let sysctl_name
= CStr
::from_bytes_with_nul(b
"hw.physicalcpu\0")
318 .expect("byte literal is missing NUL");
321 if 0 != libc
::sysctlbyname(sysctl_name
.as_ptr(),
322 &mut cpus
as *mut _
as *mut _
,
323 &mut cpus_size
as *mut _
as *mut _
,
326 return get_num_cpus();
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) }
{
344 let cpus
= unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) }
;
357 target_os
= "android",
358 target_os
= "solaris",
359 target_os
= "fuchsia")
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
;
369 let cpus
= unsafe { libc::sysconf(CONF_NAME) }
;
377 #[cfg(any(target_os = "emscripten", target_os = "redox", target_os = "haiku"))]
378 fn get_num_cpus() -> usize {
382 #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
383 fn get_num_cpus() -> usize {
389 fn env_var(name
: &'
static str) -> Option
<usize> {
390 ::std
::env
::var(name
).ok().map(|val
| val
.parse().unwrap())
395 let num
= super::get();
396 if let Some(n
) = env_var("NUM_CPUS_TEST_GET") {
400 assert
!(num
< 236_451);
405 fn test_get_physical() {
406 let num
= super::get_physical();
407 if let Some(n
) = env_var("NUM_CPUS_TEST_GET_PHYSICAL") {
411 assert
!(num
< 236_451);