Question
How can I determine how many CPU cores a machine has from C or C++ code in a platform-independent way?
If there is no fully portable solution, what are the common platform-specific ways to detect the number of cores on:
- Windows
- Unix/Linux
- macOS
A practical answer should clarify the difference between logical processors and physical CPU cores, since that affects what a program may want to measure.
Short Answer
By the end of this page, you will understand how C and C++ programs can detect available CPU parallelism, what “core count” really means, and which APIs are commonly used on Windows, Linux/Unix, and macOS. You will also learn the difference between physical cores and logical processors, when a portable answer is possible, and how to write simple fallback code.
Concept
When developers ask for the “number of cores,” they often mean one of several different things:
- Logical processors: the number of hardware execution units the OS exposes to your program
- Physical cores: actual CPU cores on the chip
- Available CPUs to this process: how many processors the OS currently allows your process to use
These are not always the same.
For example, a machine with:
- 1 CPU package
- 4 physical cores
- Hyper-Threading enabled
may appear as 8 logical processors to the operating system.
Why this matters
Programs often use this information to:
- choose a thread pool size
- parallelize computation
- limit background work
- tune performance settings
But there is an important detail:
- If you want to know how much parallel work your program can likely run, logical processor count is usually the most useful number.
- If you want hardware topology details, such as true physical core count, the solution becomes more platform-specific and sometimes more complex.
Is there a platform-independent way?
In modern C++
Yes, for logical concurrency, C++ provides:
#include <thread>
unsigned int n = std::thread::hardware_concurrency();
This is the closest thing to a portable answer in standard C++.
However:
- it returns a hint, not a guarantee
- it usually reports logical processors, not physical cores
Mental Model
Think of the CPU like a workplace.
- Physical cores are the actual workers.
- Logical processors are the desks the manager says can be used at once.
- Available CPUs to your process are the desks currently assigned to your team.
If a worker can juggle two tasks with technologies like Hyper-Threading, the office may report more usable desks than actual workers.
So when your program asks, “How many cores are there?”, the real question is often:
- How many workers exist?
- How many desks are visible?
- How many desks can my program use right now?
That is why this question has different answers depending on what exactly you want to measure.
Syntax and Examples
Portable C++ option
If you are using modern C++, the simplest portable approach is:
#include <iostream>
#include <thread>
int main() {
unsigned int n = std::thread::hardware_concurrency();
if (n == 0) {
std::cout << "Could not detect hardware concurrency.\n";
} else {
std::cout << "Logical processors available: " << n << "\n";
}
return 0;
}
What this does
- Includes
<thread> - Calls
std::thread::hardware_concurrency() - Prints the number of logical processors suggested by the implementation
This is usually the best first choice in portable C++.
Unix/Linux example
A common C or C++ approach on Unix-like systems is sysconf:
#include
{
n = (_SC_NPROCESSORS_ONLN);
(n < ) {
std::cout << ;
} {
std::cout << << n << ;
}
;
}
Step by Step Execution
Consider this C++ example:
#include <iostream>
#include <thread>
int main() {
unsigned int count = std::thread::hardware_concurrency();
if (count == 0) {
std::cout << "Unknown\n";
} else {
std::cout << count << "\n";
}
}
Step by step
1. Include headers
#include <iostream>
#include <thread>
<iostream>lets us print output<thread>provideshardware_concurrency()
2. Ask the implementation for hardware concurrency
unsigned int count = std::thread::hardware_concurrency();
Real World Use Cases
Thread pool sizing
A server or desktop application may choose a default thread pool size based on available processors.
unsigned int workers = std::thread::hardware_concurrency();
if (workers == 0) workers = 4;
Parallel data processing
A program that compresses files, processes images, or runs simulations can divide work across CPUs.
Examples:
- image resizing pipeline
- scientific calculations
- build systems compiling multiple files at once
Game engines and media tools
Games and creative tools often split work into systems such as:
- rendering preparation
- physics
- audio processing
- asset streaming
Knowing available concurrency helps choose a sensible job system size.
CLI tools and batch scripts
Command-line tools often support flags like:
mytool --jobs 8
If the user does not provide a value, the tool may detect available processors and pick a default.
Containers and restricted environments
In production, a process may not be allowed to use all system CPUs. That means the useful number may be the CPUs available to the process, not the machine's total hardware.
Real Codebase Usage
In real projects, developers usually do not blindly create one thread per core forever. Instead, they use processor count as a starting point.
Common patterns
Guard clause with fallback
unsigned int n = std::thread::hardware_concurrency();
if (n == 0) n = 4;
This is common because detection may fail.
Cap resource usage
unsigned int n = std::thread::hardware_concurrency();
if (n == 0) n = 4;
if (n > 8) n = 8;
A codebase may avoid using every processor to keep the system responsive.
Reserve one core for the OS
unsigned int n = std::thread::hardware_concurrency();
if (n == 0) n = 4;
if (n > 1) --n;
Some applications intentionally leave one logical processor free.
User-configurable override
Common Mistakes
Mistake 1: Assuming “core count” always means physical cores
Many APIs return logical processors, not physical cores.
Broken assumption:
unsigned int cores = std::thread::hardware_concurrency();
This variable name may be misleading. A better name is:
unsigned int logical_processors = std::thread::hardware_concurrency();
Mistake 2: Not handling a return value of 0
Broken code:
unsigned int n = std::thread::hardware_concurrency();
std::vector<int> data(n);
If n is 0, your logic may break or create useless behavior.
Better:
unsigned int n = std::thread::hardware_concurrency();
if (n == 0) n = 4;
Mistake 3: Creating too many threads
Comparisons
| Approach | Language | Portable? | Usually Returns | Notes |
|---|---|---|---|---|
std::thread::hardware_concurrency() | C++ | Yes | Logical processors hint | May return 0 |
sysconf(_SC_NPROCESSORS_ONLN) | C/C++ on Unix/Linux | No | Online logical processors | Common POSIX-style solution |
GetSystemInfo() | C/C++ on Windows | No | Logical processors | Simple Windows API |
sysctl() | C/C++ on macOS/BSD | No |
Cheat Sheet
Quick reference
Standard C++
#include <thread>
unsigned int n = std::thread::hardware_concurrency();
- portable in C++
- usually returns logical processors
- may return
0
Unix/Linux
#include <unistd.h>
long n = sysconf(_SC_NPROCESSORS_ONLN);
- returns online logical processors
- check for values less than
1
Windows
SYSTEM_INFO info;
GetSystemInfo(&info);
DWORD n = info.dwNumberOfProcessors;
- returns logical processors
macOS/BSD
sysctl(... HW_AVAILCPU ...)
sysctl(... HW_NCPU ...)
- available CPU count, then fallback to total
Rules to remember
FAQ
Does C have a standard function to get the number of CPU cores?
No. Standard C does not define a portable CPU-count API. You usually need platform-specific system calls.
Is std::thread::hardware_concurrency() the number of physical cores?
Usually no. It commonly reports logical processors, not guaranteed physical cores.
Why can std::thread::hardware_concurrency() return 0?
Because the C++ standard defines it as a hint. If the implementation cannot determine the value, it may return 0.
What is the difference between logical processors and physical cores?
Physical cores are actual hardware cores. Logical processors are execution units visible to the OS, which may be higher due to technologies like Hyper-Threading.
Which count should I use for thread pools?
Usually logical processor count is a practical starting point, then adjust based on workload and testing.
Is there one API that works on Windows, Linux, and macOS in C?
No standard C API exists for all of them. You usually write platform-specific code with conditional compilation.
Should I always create one thread per core?
Not always. Too many threads can reduce performance. Thread pools and bounded worker counts are usually better.
Can CPU affinity or containers affect the result?
Yes. A machine may have many CPUs, but your process may only be allowed to use some of them.
Mini Project
Description
Build a small cross-platform C++ utility function that reports the machine's available processor count. This project demonstrates how to prefer a portable standard C++ solution first, then apply a fallback, and finally use the result in a realistic thread-count decision.
Goal
Create a C++ program that detects available logical processors and chooses a safe worker-thread count.
Requirements
- Write a function that returns the detected logical processor count.
- Use
std::thread::hardware_concurrency()as the primary method. - Fall back to a reasonable default if detection fails.
- Print both the detected count and the chosen worker count.
- Leave one processor free when more than one is available.
Keep learning
Related questions
Array-to-Pointer Conversion in C and C++ Explained
Learn what array-to-pointer conversion means in C and C++, how array decay works, and how it differs from a pointer to an array.
Building More Fault-Tolerant Embedded C++ Applications for Radiation-Prone ARM Systems
Learn practical C++ and compile-time techniques to reduce soft-error damage in embedded ARM systems exposed to radiation.
C Pointer to Array vs Array of Pointers: How to Read Complex Declarations
Learn the difference between pointer-to-array and array-of-pointers in C, plus a simple rule for reading complex declarations correctly.