Question

How to Find the Number of CPU Cores in C and C++

ccppmultithreadingmultiplatform

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
  • it may return 0 if the value cannot be determined

In C

Standard C does not provide a portable API for CPU core count. You usually need platform-specific APIs such as:

  • GetSystemInfo or GetLogicalProcessorInformation on Windows
  • sysconf(_SC_NPROCESSORS_ONLN) on many Unix/Linux systems
  • sysctl on macOS and BSD systems

Core idea

There is no single standard C API for this. There is a practical standard C++ answer for concurrency hints. For exact hardware details, you often need per-platform code.

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.

Take Quiz

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 <iostream>
#include <unistd.h>

int main() {
    long n = sysconf(_SC_NPROCESSORS_ONLN);

    if (n < 1) {
        std::cout << "Could not detect processor count.\n";
    } else {
        std::cout << "Online logical processors: " << n << "\n";
    }

    return 0;
}

Notes

  • _SC_NPROCESSORS_ONLN means processors currently online
  • some systems also support _SC_NPROCESSORS_CONF for configured processors

Windows example

#include <iostream>
#include <windows.h>

int main() {
    SYSTEM_INFO sysInfo;
    GetSystemInfo(&sysInfo);

    std::cout << "Logical processors: "
              << sysInfo.dwNumberOfProcessors << "\n";

    return 0;
}

Notes

  • dwNumberOfProcessors gives the number of logical processors visible to the process environment
  • for deeper topology, Windows has more advanced APIs such as GetLogicalProcessorInformation

macOS example

#include <iostream>
#include <sys/types.h>
#include <sys/sysctl.h>

int main() {
    int nm[2];
    size_t len = 4;
    uint32_t count;

    nm[0] = CTL_HW;
    nm[1] = HW_AVAILCPU;
    sysctl(nm, 2, &count, &len, NULL, 0);

    if (count < 1) {
        nm[1] = HW_NCPU;
        sysctl(nm, 2, &count, &len, NULL, 0);
    }

    std::cout << "Available logical processors: " << count << "\n";
    return 0;
}

Notes

  • HW_AVAILCPU asks for available CPUs
  • HW_NCPU is a fallback for total CPUs
  • on Apple platforms, exact physical-vs-logical details may require other sysctl keys depending on the system version

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> provides hardware_concurrency()

2. Ask the implementation for hardware concurrency

unsigned int count = std::thread::hardware_concurrency();

The C++ runtime asks the operating system or platform layer for a concurrency hint.

Possible results:

  • 8 on an 8-thread machine
  • 16 on a CPU with 8 physical cores and 16 logical threads
  • 0 if the value cannot be determined

3. Handle the unknown case

if (count == 0) {
    std::cout << "Unknown\n";
}

This matters because the standard does not guarantee a nonzero result.

4. Print the count if known

else {
    std::cout << count << "\n";
}

If detection succeeds, the program prints the value.

Example trace

Suppose the machine exposes 12 logical processors.

  • hardware_concurrency() returns 12
  • count == 0 is false
  • the program prints 12

Suppose detection fails.

  • hardware_concurrency() returns 0
  • count == 0 is true
  • the program prints Unknown

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.

Take Quiz

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

Real applications often let users override automatic detection through:

  • config files
  • command-line options
  • environment variables

That is helpful because automatic detection is not always the best tuning choice.

Platform abstraction layer

Larger codebases wrap this logic in one function:

unsigned int get_worker_count();

Internally, that function may:

  • try standard C++ first
  • fall back to platform-specific APIs
  • apply project-specific rules

This keeps the rest of the code portable and clean.

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

Beginners sometimes think more threads always means more speed.

Broken idea:

for (int i = 0; i < 1000; ++i) {
    // create a thread
}

Too many threads can cause:

  • context switching overhead
  • memory waste
  • worse performance

Use a thread pool or bounded number of workers.

Mistake 4: Ignoring platform differences

Code like this is not portable:

#include <windows.h>

If you need portability, isolate OS-specific code behind #ifdef blocks or use standard C++ where possible.

Mistake 5: Confusing online CPUs with configured CPUs

On Unix-like systems:

  • _SC_NPROCESSORS_CONF can mean configured processors
  • _SC_NPROCESSORS_ONLN means currently online processors

If you want usable processors right now, use the online value.

Mistake 6: Treating detected count as a perfect tuning answer

The detected number is just a hint. Actual performance depends on:

  • workload type
  • I/O vs CPU-bound work
  • memory bandwidth
  • process limits
  • system load

Comparisons

ApproachLanguagePortable?Usually ReturnsNotes
std::thread::hardware_concurrency()C++YesLogical processors hintMay return 0
sysconf(_SC_NPROCESSORS_ONLN)C/C++ on Unix/LinuxNoOnline logical processorsCommon POSIX-style solution
GetSystemInfo()C/C++ on WindowsNoLogical processorsSimple Windows API
sysctl()C/C++ on macOS/BSDNoAvailable or total processorsKey names vary by platform

Logical processors vs physical cores

MeasureMeaningTypical use
Logical processorsExecution contexts visible to OSThread pool sizing
Physical coresActual hardware coresHardware reporting, topology analysis
Available CPUs to processCPUs your program can use nowProduction tuning, containers, affinity-aware apps

Portable vs platform-specific

OptionBest whenTradeoff
Standard C++You want one code pathLess hardware detail
Platform-specific APIsYou need exact OS-specific informationMore code and maintenance
Abstraction wrapperYou want bothSlightly more design work upfront
Take Quiz

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

  • “core count” may mean different things
  • standard C has no portable API for this
  • standard C++ has hardware_concurrency()
  • logical processors are usually more relevant for parallel code
  • always handle unknown results or fallback cases

Safe default pattern

unsigned int n = std::thread::hardware_concurrency();
if (n == 0) n = 4;

Best practice

  • prefer portable C++ if possible
  • wrap platform-specific code in one helper function
  • allow user override for performance-sensitive programs

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.

Take Quiz

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.
Take Quiz

Keep learning

Related questions

View more