Coverage Report

Created: 2025-12-28 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/num_cpus-1.16.0/src/lib.rs
Line
Count
Source
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
#![allow(non_snake_case)]
32
33
#[cfg(not(windows))]
34
extern crate libc;
35
36
#[cfg(target_os = "hermit")]
37
extern crate hermit_abi;
38
39
#[cfg(target_os = "linux")]
40
mod linux;
41
#[cfg(target_os = "linux")]
42
use linux::{get_num_cpus, get_num_physical_cpus};
43
44
/// Returns the number of available CPUs of the current system.
45
///
46
/// This function will get the number of logical cores. Sometimes this is different from the number
47
/// of physical cores (See [Simultaneous multithreading on Wikipedia][smt]).
48
///
49
/// This will always return at least `1`.
50
///
51
/// # Examples
52
///
53
/// ```
54
/// let cpus = num_cpus::get();
55
/// if cpus > 1 {
56
///     println!("We are on a multicore system with {} CPUs", cpus);
57
/// } else {
58
///     println!("We are on a single core system");
59
/// }
60
/// ```
61
///
62
/// # Note
63
///
64
/// This will check [sched affinity] on Linux, showing a lower number of CPUs if the current
65
/// thread does not have access to all the computer's CPUs.
66
///
67
/// This will also check [cgroups], frequently used in containers to constrain CPU usage.
68
///
69
/// [smt]: https://en.wikipedia.org/wiki/Simultaneous_multithreading
70
/// [sched affinity]: http://www.gnu.org/software/libc/manual/html_node/CPU-Affinity.html
71
/// [cgroups]: https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt
72
#[inline]
73
0
pub fn get() -> usize {
74
0
    get_num_cpus()
75
0
}
Unexecuted instantiation: num_cpus::get
Unexecuted instantiation: num_cpus::get
76
77
/// Returns the number of physical cores of the current system.
78
///
79
/// This will always return at least `1`.
80
///
81
/// # Note
82
///
83
/// Physical count is supported only on Linux, mac OS and Windows platforms.
84
/// On other platforms, or if the physical count fails on supported platforms,
85
/// this function returns the same as [`get()`], which is the number of logical
86
/// CPUS.
87
///
88
/// # Examples
89
///
90
/// ```
91
/// let logical_cpus = num_cpus::get();
92
/// let physical_cpus = num_cpus::get_physical();
93
/// if logical_cpus > physical_cpus {
94
///     println!("We have simultaneous multithreading with about {:.2} \
95
///               logical cores to 1 physical core.", 
96
///               (logical_cpus as f64) / (physical_cpus as f64));
97
/// } else if logical_cpus == physical_cpus {
98
///     println!("Either we don't have simultaneous multithreading, or our \
99
///               system doesn't support getting the number of physical CPUs.");
100
/// } else {
101
///     println!("We have less logical CPUs than physical CPUs, maybe we only have access to \
102
///               some of the CPUs on our system.");
103
/// }
104
/// ```
105
///
106
/// [`get()`]: fn.get.html
107
#[inline]
108
0
pub fn get_physical() -> usize {
109
0
    get_num_physical_cpus()
110
0
}
Unexecuted instantiation: num_cpus::get_physical
Unexecuted instantiation: num_cpus::get_physical
111
112
113
#[cfg(not(any(
114
    target_os = "linux",
115
    target_os = "windows",
116
    target_os = "macos",
117
    target_os = "openbsd",
118
    target_os = "aix")))]
119
#[inline]
120
fn get_num_physical_cpus() -> usize {
121
    // Not implemented, fall back
122
    get_num_cpus()
123
}
124
125
#[cfg(target_os = "windows")]
126
fn get_num_physical_cpus() -> usize {
127
    match get_num_physical_cpus_windows() {
128
        Some(num) => num,
129
        None => get_num_cpus()
130
    }
131
}
132
133
#[cfg(target_os = "windows")]
134
fn get_num_physical_cpus_windows() -> Option<usize> {
135
    // Inspired by https://msdn.microsoft.com/en-us/library/ms683194
136
137
    use std::ptr;
138
    use std::mem;
139
140
    #[allow(non_upper_case_globals)]
141
    const RelationProcessorCore: u32 = 0;
142
143
    #[repr(C)]
144
    #[allow(non_camel_case_types)]
145
    struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION {
146
        mask: usize,
147
        relationship: u32,
148
        _unused: [u64; 2]
149
    }
150
151
    extern "system" {
152
        fn GetLogicalProcessorInformation(
153
            info: *mut SYSTEM_LOGICAL_PROCESSOR_INFORMATION,
154
            length: &mut u32
155
        ) -> u32;
156
    }
157
158
    // First we need to determine how much space to reserve.
159
160
    // The required size of the buffer, in bytes.
161
    let mut needed_size = 0;
162
163
    unsafe {
164
        GetLogicalProcessorInformation(ptr::null_mut(), &mut needed_size);
165
    }
166
167
    let struct_size = mem::size_of::<SYSTEM_LOGICAL_PROCESSOR_INFORMATION>() as u32;
168
169
    // Could be 0, or some other bogus size.
170
    if needed_size == 0 || needed_size < struct_size || needed_size % struct_size != 0 {
171
        return None;
172
    }
173
174
    let count = needed_size / struct_size;
175
176
    // Allocate some memory where we will store the processor info.
177
    let mut buf = Vec::with_capacity(count as usize);
178
179
    let result;
180
181
    unsafe {
182
        result = GetLogicalProcessorInformation(buf.as_mut_ptr(), &mut needed_size);
183
    }
184
185
    // Failed for any reason.
186
    if result == 0 {
187
        return None;
188
    }
189
190
    let count = needed_size / struct_size;
191
192
    unsafe {
193
        buf.set_len(count as usize);
194
    }
195
196
    let phys_proc_count = buf.iter()
197
        // Only interested in processor packages (physical processors.)
198
        .filter(|proc_info| proc_info.relationship == RelationProcessorCore)
199
        .count();
200
201
    if phys_proc_count == 0 {
202
        None
203
    } else {
204
        Some(phys_proc_count)
205
    }
206
}
207
208
#[cfg(windows)]
209
fn get_num_cpus() -> usize {
210
    #[repr(C)]
211
    struct SYSTEM_INFO {
212
        wProcessorArchitecture: u16,
213
        wReserved: u16,
214
        dwPageSize: u32,
215
        lpMinimumApplicationAddress: *mut u8,
216
        lpMaximumApplicationAddress: *mut u8,
217
        dwActiveProcessorMask: *mut u8,
218
        dwNumberOfProcessors: u32,
219
        dwProcessorType: u32,
220
        dwAllocationGranularity: u32,
221
        wProcessorLevel: u16,
222
        wProcessorRevision: u16,
223
    }
224
225
    extern "system" {
226
        fn GetSystemInfo(lpSystemInfo: *mut SYSTEM_INFO);
227
    }
228
229
    unsafe {
230
        let mut sysinfo: SYSTEM_INFO = std::mem::zeroed();
231
        GetSystemInfo(&mut sysinfo);
232
        sysinfo.dwNumberOfProcessors as usize
233
    }
234
}
235
236
#[cfg(any(target_os = "freebsd",
237
          target_os = "dragonfly",
238
          target_os = "netbsd"))]
239
fn get_num_cpus() -> usize {
240
    use std::ptr;
241
242
    let mut cpus: libc::c_uint = 0;
243
    let mut cpus_size = std::mem::size_of_val(&cpus);
244
245
    unsafe {
246
        cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
247
    }
248
    if cpus < 1 {
249
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
250
        unsafe {
251
            libc::sysctl(mib.as_mut_ptr(),
252
                         2,
253
                         &mut cpus as *mut _ as *mut _,
254
                         &mut cpus_size as *mut _ as *mut _,
255
                         ptr::null_mut(),
256
                         0);
257
        }
258
        if cpus < 1 {
259
            cpus = 1;
260
        }
261
    }
262
    cpus as usize
263
}
264
265
#[cfg(target_os = "openbsd")]
266
fn get_num_cpus() -> usize {
267
    use std::ptr;
268
269
    let mut cpus: libc::c_uint = 0;
270
    let mut cpus_size = std::mem::size_of_val(&cpus);
271
    let mut mib = [libc::CTL_HW, libc::HW_NCPUONLINE, 0, 0];
272
    let rc: libc::c_int;
273
274
    unsafe {
275
        rc = libc::sysctl(mib.as_mut_ptr(),
276
                          2,
277
                          &mut cpus as *mut _ as *mut _,
278
                          &mut cpus_size as *mut _ as *mut _,
279
                          ptr::null_mut(),
280
                          0);
281
    }
282
    if rc < 0 {
283
        cpus = 1;
284
    }
285
    cpus as usize
286
}
287
288
#[cfg(target_os = "openbsd")]
289
fn get_num_physical_cpus() -> usize {
290
    use std::ptr;
291
292
    let mut cpus: libc::c_uint = 0;
293
    let mut cpus_size = std::mem::size_of_val(&cpus);
294
    let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
295
    let rc: libc::c_int;
296
297
    unsafe {
298
        rc = libc::sysctl(mib.as_mut_ptr(),
299
                          2,
300
                          &mut cpus as *mut _ as *mut _,
301
                          &mut cpus_size as *mut _ as *mut _,
302
                          ptr::null_mut(),
303
                          0);
304
    }
305
    if rc < 0 {
306
        cpus = 1;
307
    }
308
    cpus as usize
309
}
310
311
312
#[cfg(target_os = "macos")]
313
fn get_num_physical_cpus() -> usize {
314
    use std::ffi::CStr;
315
    use std::ptr;
316
317
    let mut cpus: i32 = 0;
318
    let mut cpus_size = std::mem::size_of_val(&cpus);
319
320
    let sysctl_name = CStr::from_bytes_with_nul(b"hw.physicalcpu\0")
321
        .expect("byte literal is missing NUL");
322
323
    unsafe {
324
        if 0 != libc::sysctlbyname(sysctl_name.as_ptr(),
325
                                   &mut cpus as *mut _ as *mut _,
326
                                   &mut cpus_size as *mut _ as *mut _,
327
                                   ptr::null_mut(),
328
                                   0) {
329
            return get_num_cpus();
330
        }
331
    }
332
    cpus as usize
333
}
334
335
#[cfg(target_os = "aix")]
336
fn get_num_physical_cpus() -> usize {
337
    match get_smt_threads_aix() {
338
        Some(num) => get_num_cpus() / num,
339
        None => get_num_cpus(),
340
    }
341
}
342
343
#[cfg(target_os = "aix")]
344
fn get_smt_threads_aix() -> Option<usize> {
345
    let smt = unsafe {
346
        libc::getsystemcfg(libc::SC_SMT_TC)
347
    };
348
    if smt == u64::MAX {
349
        return None;
350
    }
351
    Some(smt as usize)
352
}
353
354
#[cfg(any(
355
    target_os = "nacl",
356
    target_os = "macos",
357
    target_os = "ios",
358
    target_os = "android",
359
    target_os = "aix",
360
    target_os = "solaris",
361
    target_os = "illumos",
362
    target_os = "fuchsia")
363
)]
364
fn get_num_cpus() -> usize {
365
    // On ARM targets, processors could be turned off to save power.
366
    // Use `_SC_NPROCESSORS_CONF` to get the real number.
367
    #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
368
    const CONF_NAME: libc::c_int = libc::_SC_NPROCESSORS_CONF;
369
    #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
370
    const CONF_NAME: libc::c_int = libc::_SC_NPROCESSORS_ONLN;
371
372
    let cpus = unsafe { libc::sysconf(CONF_NAME) };
373
    if cpus < 1 {
374
        1
375
    } else {
376
        cpus as usize
377
    }
378
}
379
380
#[cfg(target_os = "haiku")]
381
fn get_num_cpus() -> usize {
382
    use std::mem;
383
384
    #[allow(non_camel_case_types)]
385
    type bigtime_t = i64;
386
    #[allow(non_camel_case_types)]
387
    type status_t = i32;
388
389
    #[repr(C)]
390
    pub struct system_info {
391
        pub boot_time: bigtime_t,
392
        pub cpu_count: u32,
393
        pub max_pages: u64,
394
        pub used_pages: u64,
395
        pub cached_pages: u64,
396
        pub block_cache_pages: u64,
397
        pub ignored_pages: u64,
398
        pub needed_memory: u64,
399
        pub free_memory: u64,
400
        pub max_swap_pages: u64,
401
        pub free_swap_pages: u64,
402
        pub page_faults: u32,
403
        pub max_sems: u32,
404
        pub used_sems: u32,
405
        pub max_ports: u32,
406
        pub used_ports: u32,
407
        pub max_threads: u32,
408
        pub used_threads: u32,
409
        pub max_teams: u32,
410
        pub used_teams: u32,
411
        pub kernel_name: [::std::os::raw::c_char; 256usize],
412
        pub kernel_build_date: [::std::os::raw::c_char; 32usize],
413
        pub kernel_build_time: [::std::os::raw::c_char; 32usize],
414
        pub kernel_version: i64,
415
        pub abi: u32,
416
    }
417
418
    extern {
419
        fn get_system_info(info: *mut system_info) -> status_t;
420
    }
421
422
    let mut info: system_info = unsafe { mem::zeroed() };
423
    let status = unsafe { get_system_info(&mut info as *mut _) };
424
    if status == 0 {
425
        info.cpu_count as usize
426
    } else {
427
        1
428
    }
429
}
430
431
#[cfg(target_os = "hermit")]
432
fn get_num_cpus() -> usize {
433
    unsafe { hermit_abi::get_processor_count() }
434
}
435
436
#[cfg(not(any(
437
    target_os = "nacl",
438
    target_os = "macos",
439
    target_os = "ios",
440
    target_os = "android",
441
    target_os = "aix",
442
    target_os = "solaris",
443
    target_os = "illumos",
444
    target_os = "fuchsia",
445
    target_os = "linux",
446
    target_os = "openbsd",
447
    target_os = "freebsd",
448
    target_os = "dragonfly",
449
    target_os = "netbsd",
450
    target_os = "haiku",
451
    target_os = "hermit",
452
    windows,
453
)))]
454
fn get_num_cpus() -> usize {
455
    1
456
}
457
458
#[cfg(test)]
459
mod tests {
460
    fn env_var(name: &'static str) -> Option<usize> {
461
        ::std::env::var(name).ok().map(|val| val.parse().unwrap())
462
    }
463
464
    #[test]
465
    fn test_get() {
466
        let num = super::get();
467
        if let Some(n) = env_var("NUM_CPUS_TEST_GET") {
468
            assert_eq!(num, n);
469
        } else {
470
            assert!(num > 0);
471
            assert!(num < 236_451);
472
        }
473
    }
474
475
    #[test]
476
    fn test_get_physical() {
477
        let num = super::get_physical();
478
        if let Some(n) = env_var("NUM_CPUS_TEST_GET_PHYSICAL") {
479
            assert_eq!(num, n);
480
        } else {
481
            assert!(num > 0);
482
            assert!(num < 236_451);
483
        }
484
    }
485
}