std::hardware_destructive_interference_size in C++
This article explores the utility of
std::hardware_destructive_interference_size, introduced in
C++17, which provides the recommended byte boundary to prevent false
sharing in multithreaded applications. By defining the minimum alignment
required to isolate concurrently modified variables onto distinct cache
lines, this constant enables developers to write portable,
high-performance concurrent code that mitigates costly cache-coherence
penalties.
The Problem: False Sharing
Modern CPU architectures manage memory in chunks called cache lines, typically 64 bytes in size. When multiple processor cores access or modify variables situated within the exact same cache line, the CPU's cache-coherence protocol steps in.
Even if two threads are updating completely independent variables, sharing a physical cache line forces the hardware to invalidate and synchronize that entire line between cores. This phenomenon is known as "false sharing" or destructive interference. It causes significant, unnecessary latency and severely degrades multithreaded performance.
The Role of std::hardware_destructive_interference_size
Defined in the <new> header,
std::hardware_destructive_interference_size is a
constexpr std::size_t value that returns the minimum byte
offset required between two concurrently accessed objects to avoid false
sharing.
Before C++17, developers typically hardcoded assumed cache line sizes
(such as 64 bytes) or relied on architecture-specific preprocessor
macros. std::hardware_destructive_interference_size
standardizes this practice, making alignment requirements explicit,
self-documenting, and portable across platforms.
How to Use It
The most common application is combining the constant with the
alignas specifier to separate frequently modified fields
within a data structure.
#include <new>
#include <atomic>
struct ThreadCounters {
// Thread 1 writes here
alignas(std::hardware_destructive_interference_size) std::atomic<int> counterA{0};
// Thread 2 writes here
alignas(std::hardware_destructive_interference_size) std::atomic<int> counterB{0};
};In this example, counterA and counterB are
guaranteed to occupy distinct cache lines. When two threads concurrently
increment their respective counters, neither core invalidates the
other's cache, avoiding contention and maximizing throughput.
Destructive vs. Constructive Interference
Alongside the destructive constant, C++17 introduced
std::hardware_constructive_interference_size:
- Destructive interference size: The minimum spacing needed to avoid cache line contention when data is accessed independently by concurrent threads.
- Constructive interference size: The maximum recommended size to keep related data within the same cache line, promoting spatial locality so that accessing one value pulls associated values into the cache automatically.
Compilation and Portability Considerations
Because std::hardware_destructive_interference_size
depends on the underlying hardware target, compiling code for a generic
architecture (such as standard x86-64 without a specific
-march flag) may cause some compilers to issue warnings.
Compilers often must assume a conservative default, which may not match
every specific sub-architecture. For mission-critical performance
tuning, compile with target-specific optimization flags to ensure the
value reflects the target CPU's actual cache line dimensions.