CodeNebula
C++17stladvanced

hardware_destructive_interference_size / hardware_constructive_interference_size

C++17 · Practical Modern · Compiler flag: -std=c++17

Explanation

Compile-time constants representing the minimum offset to avoid false sharing (`hardware_destructive_interference_size`, typically a cache line size) and the maximum size for promoting true sharing (`hardware_constructive_interference_size`). These allow writing portable, cache-friendly concurrent data structures.

Real-World Scenario

Align per-thread counters to avoid false sharing in a high-throughput concurrent counter array.

Quick Snippet

snippet.cpp
#include <new>

struct alignas(std::hardware_destructive_interference_size) AlignedCounter {
    std::atomic<int> value{0};
};

// Array of counters, each on its own cache line to avoid false sharing
AlignedCounter perThreadCounters[8];

Full Example

interference_sizes.cpp
// CodeNebula - C++17 Example: hardware_destructive/constructive_interference_size
// Compile: g++ -std=c++17 -Wall -Wextra -o example interference_sizes.cpp

// Demonstrates the C++17 cache-line constants for avoiding false sharing
// (destructive interference) and promoting true sharing (constructive
// interference). Note: not all compilers define these; we provide fallbacks.

#include <iostream>
#include <new>

// Many compilers (notably GCC/Clang) do not yet define these constants.
// We provide a portable fallback to demonstrate the concept.
#ifdef __cpp_lib_hardware_interference_size
    constexpr std::size_t destructive_size =
        std::hardware_destructive_interference_size;
    constexpr std::size_t constructive_size =
        std::hardware_constructive_interference_size;
    constexpr bool has_stdlib_support = true;
#else
    // Typical cache line size on x86-64 and ARM64
    constexpr std::size_t destructive_size  = 64;
    constexpr std::size_t constructive_size = 64;
    constexpr bool has_stdlib_support = false;
#endif

// ------------------------------------------------------------------
// Avoid false sharing: pad each counter to its own cache line
// ------------------------------------------------------------------
struct alignas(destructive_size) PaddedCounter {
    int value = 0;
    // Padding fills the rest of the cache line, preventing false sharing
    // when adjacent PaddedCounters are modified by different threads.
};

// ------------------------------------------------------------------
// Promote true sharing: keep related data within one cache line
// ------------------------------------------------------------------
struct alignas(constructive_size) CoLocatedPair {
    int x = 0;
    int y = 0;
    // Both x and y live in the same cache line, so one thread
    // accessing both fields benefits from spatial locality.
};

int main()
{
    // ------------------------------------------------------------------
    // 1. Report the interference sizes
    // ------------------------------------------------------------------
    std::cout << "=== Hardware Interference Sizes ===" << std::endl;
    std::cout << "  stdlib support: " << (has_stdlib_support ? "yes" : "no (using fallback)") << std::endl;
    std::cout << "  destructive_interference_size  = " << destructive_size << " bytes" << std::endl;
    std::cout << "  constructive_interference_size = " << constructive_size << " bytes" << std::endl;

    // ------------------------------------------------------------------
    // 2. Demonstrate alignment of padded counters
    // ------------------------------------------------------------------
    PaddedCounter counters[4];

    std::cout << "\n=== Padded Counters (avoid false sharing) ===" << std::endl;
    std::cout << "  sizeof(PaddedCounter)  = " << sizeof(PaddedCounter) << std::endl;
    std::cout << "  alignof(PaddedCounter) = " << alignof(PaddedCounter) << std::endl;
    for (int i = 0; i < 4; ++i) {
        auto addr = reinterpret_cast<std::uintptr_t>(&counters[i]);
        std::cout << "  counters[" << i << "] at offset "
                  << (addr - reinterpret_cast<std::uintptr_t>(&counters[0]))
                  << std::endl;
    }

    // ------------------------------------------------------------------
    // 3. Co-located pair for true sharing
    // ------------------------------------------------------------------
    CoLocatedPair pair;
    auto base = reinterpret_cast<std::uintptr_t>(&pair);
    auto x_off = reinterpret_cast<std::uintptr_t>(&pair.x) - base;
    auto y_off = reinterpret_cast<std::uintptr_t>(&pair.y) - base;

    std::cout << "\n=== Co-Located Pair (promote true sharing) ===" << std::endl;
    std::cout << "  sizeof(CoLocatedPair)  = " << sizeof(CoLocatedPair) << std::endl;
    std::cout << "  x at offset " << x_off << std::endl;
    std::cout << "  y at offset " << y_off << std::endl;
    std::cout << "  Both within " << constructive_size << "-byte line: "
              << ((y_off + sizeof(int) <= constructive_size) ? "yes" : "no")
              << std::endl;

    return 0;
}

/*
Expected output (typical x86-64):

=== Hardware Interference Sizes ===
  stdlib support: no (using fallback)
  destructive_interference_size  = 64 bytes
  constructive_interference_size = 64 bytes

=== Padded Counters (avoid false sharing) ===
  sizeof(PaddedCounter)  = 64
  alignof(PaddedCounter) = 64
  counters[0] at offset 0
  counters[1] at offset 64
  counters[2] at offset 128
  counters[3] at offset 192

=== Co-Located Pair (promote true sharing) ===
  sizeof(CoLocatedPair)  = 64
  x at offset 0
  y at offset 4
  Both within 64-byte line: yes
*/
Run command
g++ -std=c++17 -Wall -Wextra -o example interference_sizes.cpp