num_cpus/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/1.*/rayon/struct.ThreadPool.html
29#![cfg_attr(test, deny(warnings))]
30#![deny(missing_docs)]
31
32#[cfg(target_os = "linux")]
33mod linux;
34#[cfg(target_os = "linux")]
35use linux::get_num_physical_cpus;
36
37/// Returns the number of available CPUs of the current system.
38///
39/// This function will get the number of logical cores. Sometimes this is different from the number
40/// of physical cores (See [Simultaneous multithreading on Wikipedia][smt]).
41///
42/// This will always return at least `1`.
43///
44/// # Examples
45///
46/// ```
47/// let cpus = num_cpus::get();
48/// if cpus > 1 {
49/// println!("We are on a multicore system with {} CPUs", cpus);
50/// } else {
51/// println!("We are on a single core system");
52/// }
53/// ```
54///
55/// # Note
56///
57/// This delegates to [`std::thread::available_parallelism`]. On Linux and other supported
58/// platforms that accounts for the current thread's CPU affinity mask as well as CPU quotas
59/// enforced by cgroups, so it may report fewer CPUs than are physically present.
60///
61/// [smt]: https://en.wikipedia.org/wiki/Simultaneous_multithreading
62#[inline]
63pub fn get() -> usize {
64 get_num_cpus()
65}
66
67/// Returns the number of logical CPUs available to the current thread.
68///
69/// This is the standard library's [`available_parallelism`], falling back to `1` when the
70/// platform does not report a value.
71///
72/// [`available_parallelism`]: std::thread::available_parallelism
73#[inline]
74fn get_num_cpus() -> usize {
75 std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1)
76}
77
78/// Returns the number of physical cores of the current system.
79///
80/// This will always return at least `1`.
81///
82/// # Note
83///
84/// Physical count is supported only on Linux, macOS, OpenBSD and AIX platforms.
85/// On other platforms, or if the physical count fails on supported platforms,
86/// this function returns the same as [`get()`], which is the number of logical
87/// CPUS.
88///
89/// # Examples
90///
91/// ```
92/// let logical_cpus = num_cpus::get();
93/// let physical_cpus = num_cpus::get_physical();
94/// if logical_cpus > physical_cpus {
95/// println!("We have simultaneous multithreading with about {:.2} \
96/// logical cores to 1 physical core.",
97/// (logical_cpus as f64) / (physical_cpus as f64));
98/// } else if logical_cpus == physical_cpus {
99/// println!("Either we don't have simultaneous multithreading, or our \
100/// system doesn't support getting the number of physical CPUs.");
101/// } else {
102/// println!("We have less logical CPUs than physical CPUs, maybe we only have access to \
103/// some of the CPUs on our system.");
104/// }
105/// ```
106///
107/// [`get()`]: fn.get.html
108#[inline]
109pub fn get_physical() -> usize {
110 get_num_physical_cpus()
111}
112
113#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "openbsd", target_os = "aix")))]
114#[inline]
115fn get_num_physical_cpus() -> usize {
116 // Not implemented, fall back
117 get_num_cpus()
118}
119
120#[cfg(target_os = "openbsd")]
121fn get_num_physical_cpus() -> usize {
122 use std::ptr;
123
124 let mut cpus: libc::c_uint = 0;
125 let mut cpus_size = std::mem::size_of_val(&cpus);
126 let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
127 let rc: libc::c_int;
128
129 unsafe {
130 rc = libc::sysctl(
131 mib.as_mut_ptr(),
132 2,
133 &mut cpus as *mut _ as *mut _,
134 &mut cpus_size as *mut _ as *mut _,
135 ptr::null_mut(),
136 0,
137 );
138 }
139 if rc < 0 {
140 cpus = 1;
141 }
142 cpus as usize
143}
144
145#[cfg(target_os = "macos")]
146fn get_num_physical_cpus() -> usize {
147 use std::{ffi::CStr, ptr};
148
149 let mut cpus: i32 = 0;
150 let mut cpus_size = std::mem::size_of_val(&cpus);
151
152 let sysctl_name = CStr::from_bytes_with_nul(b"hw.physicalcpu\0").expect("byte literal is missing NUL");
153
154 unsafe {
155 if 0 != libc::sysctlbyname(
156 sysctl_name.as_ptr(),
157 &mut cpus as *mut _ as *mut _,
158 &mut cpus_size as *mut _ as *mut _,
159 ptr::null_mut(),
160 0,
161 ) {
162 return get_num_cpus();
163 }
164 }
165 cpus as usize
166}
167
168#[cfg(target_os = "aix")]
169fn get_num_physical_cpus() -> usize {
170 match get_smt_threads_aix() {
171 Some(num) => get_num_cpus() / num,
172 None => get_num_cpus(),
173 }
174}
175
176#[cfg(target_os = "aix")]
177fn get_smt_threads_aix() -> Option<usize> {
178 let smt = unsafe { libc::getsystemcfg(libc::SC_SMT_TC) };
179 if smt == u64::MAX {
180 return None;
181 }
182 Some(smt as usize)
183}
184
185#[cfg(test)]
186mod tests {
187 fn env_var(name: &'static str) -> Option<usize> {
188 ::std::env::var(name).ok().map(|val| val.parse().unwrap())
189 }
190
191 #[test]
192 fn test_get() {
193 let num = super::get();
194 if let Some(n) = env_var("NUM_CPUS_TEST_GET") {
195 assert_eq!(num, n);
196 } else {
197 assert!(num > 0);
198 assert!(num < 236_451);
199 }
200 }
201
202 #[test]
203 fn test_get_physical() {
204 let num = super::get_physical();
205 if let Some(n) = env_var("NUM_CPUS_TEST_GET_PHYSICAL") {
206 assert_eq!(num, n);
207 } else {
208 assert!(num > 0);
209 assert!(num < 236_451);
210 }
211 }
212}