Espacios de nombres
Variantes
Acciones

std::hardware_destructive_interference_size, std::hardware_constructive_interference_size

De cppreference.com
< cpp‎ | thread
 
 
Biblioteca de apoyo de concurrencia
Hilos
(C++11)
(C++20)
hardware_destructive_interference_sizehardware_constructive_interference_size
(C++17)(C++17)
Espacio de nombres this_thread
(C++11)
(C++11)
(C++11)
Cancelación cooperativa
Exclusión mutua
(C++11)
Gestión genérica de bloqueo
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
Variables de condición
(C++11)
Semáforos
Pestillos y barreras
(C++20)
(C++20)
Futuros
(C++11)
(C++11)
(C++11)
(C++11)
Recuperación segura
(C++26)
Punteros de riesgo
Tipos atómicos
(C++11)
(C++20)
Inicialización de tipos atómicos
(C++11)(en desuso en C++20)
(C++11)(en desuso en C++20)
Orden de memoria
Funciones independientes para operaciones atómicas
Funciones independientes para indicadores atómicos
 
Definido en el archivo de encabezado <new>
inline constexpr std::size_t
    hardware_destructive_interference_size = /*definido por la implementación*/;
(1) (desde C++17)
inline constexpr std::size_t
    hardware_constructive_interference_size = /*definido por la implementación*/;
(2) (desde C++17)
1) Desplazamiento mínimo entre dos objetos para evitar compartimiento falso. Se garantiza que sea al menos alignof(std::max_align_t)
struct keep_apart {
  alignas(std::hardware_destructive_interference_size) std::atomic<int> cat;
  alignas(std::hardware_destructive_interference_size) std::atomic<int> dog;
};
2) Tamaño máximo de memoria contigua para promover un compartimiento verdadero. Se garantiza que sea al menos alignof(std::max_align_t)
struct together {
  std::atomic<int> dog;
  int puppy;
};
struct kennel {
  // Otros datos miembro...
  alignas(sizeof(together)) together pack;
  // Otros datos miembro...
};
static_assert(sizeof(together) <= std::hardware_constructive_interference_size);

[editar] Notas

Estas constantes proporcionan una forma portátil de acceder al tamaño de la línea de la caché de datos L1.

[editar] Ejemplo

El programa usa dos hilos/subprocesos que (atómicamente) escriben en los datos miembro de los objetos globales dados. El primer objeto cabe en una línea de caché, lo que da como resultado una "interferencia de hardware". El segundo objeto mantiene sus datos miembro en líneas de caché separadas, por lo que se evita una posible "sincronización de caché" después de las escrituras de los hilos/subprocesos.

#include <atomic>
#include <chrono>
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <new>
#include <thread>
 
#ifdef __cpp_lib_hardware_interference_size
    using std::hardware_constructive_interference_size;
    using std::hardware_destructive_interference_size;
#else
    // 64 bytes en x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ...
    constexpr std::size_t hardware_constructive_interference_size
        = 2 * sizeof(std::max_align_t);
    constexpr std::size_t hardware_destructive_interference_size
        = 2 * sizeof(std::max_align_t);
#endif
 
std::mutex cout_mutex;
 
constexpr int max_write_iterations{10'000'000}; // ajuste de tiempo de referencia
 
struct alignas(hardware_constructive_interference_size)
OneCacheLiner { // ocupa una línea de caché
    std::atomic_uint64_t x{};
    std::atomic_uint64_t y{};
} oneCacheLiner;
 
struct TwoCacheLiner { // ocupa dos líneas de caché
    alignas(hardware_destructive_interference_size) std::atomic_uint64_t x{};
    alignas(hardware_destructive_interference_size) std::atomic_uint64_t y{};
} twoCacheLiner;
 
inline auto now() noexcept { return std::chrono::high_resolution_clock::now(); }
 
template<bool xy>
void oneCacheLinerThread() {
    const auto start { now() };
 
    for (uint64_t count{}; count != max_write_iterations; ++count)
        if constexpr (xy)
             oneCacheLiner.x.fetch_add(1, std::memory_order_relaxed);
        else oneCacheLiner.y.fetch_add(1, std::memory_order_relaxed);
 
    const std::chrono::duration<double, std::milli> elapsed { now() - start };
    std::lock_guard lk{cout_mutex};
    std::cout << "oneCacheLinerThread() usó " << elapsed.count() << " ms\n";
    if constexpr (xy)
         oneCacheLiner.x = elapsed.count();
    else oneCacheLiner.y = elapsed.count();
}
 
template<bool xy>
void twoCacheLinerThread() {
    const auto start { now() };
 
    for (uint64_t count{}; count != max_write_iterations; ++count)
        if constexpr (xy)
             twoCacheLiner.x.fetch_add(1, std::memory_order_relaxed);
        else twoCacheLiner.y.fetch_add(1, std::memory_order_relaxed);
 
    const std::chrono::duration<double, std::milli> elapsed { now() - start };
    std::lock_guard lk{cout_mutex};
    std::cout << "twoCacheLinerThread() usó " << elapsed.count() << " ms\n";
    if constexpr (xy)
         twoCacheLiner.x = elapsed.count();
    else twoCacheLiner.y = elapsed.count();
}
 
int main() {
    std::cout << "__cpp_lib_hardware_interference_size "
#   ifdef __cpp_lib_hardware_interference_size
        " = " << __cpp_lib_hardware_interference_size << "\n";
#   else
        "no está definido\n";
#   endif
 
    std::cout
        << "hardware_destructive_interference_size == "
        << hardware_destructive_interference_size << '\n'
        << "hardware_constructive_interference_size == "
        << hardware_constructive_interference_size << '\n'
        << "sizeof( std::max_align_t ) == " << sizeof(std::max_align_t) << "\n\n";
 
    std::cout
        << std::fixed << std::setprecision(2)
        << "sizeof( OneCacheLiner ) == " << sizeof( OneCacheLiner ) << '\n'
        << "sizeof( TwoCacheLiner ) == " << sizeof( TwoCacheLiner ) << "\n\n";
 
    constexpr int max_runs{4};
 
    int oneCacheLiner_average{0};
    for (auto i{0}; i != max_runs; ++i) {
        std::thread th1{oneCacheLinerThread<0>};
        std::thread th2{oneCacheLinerThread<1>};
        th1.join(); th2.join();
        oneCacheLiner_average += oneCacheLiner.x + oneCacheLiner.y;
    }
    std::cout << "Tiempo promedio: " << (oneCacheLiner_average / max_runs / 2) << " ms\n\n";
 
    int twoCacheLiner_average{0};
    for (auto i{0}; i != max_runs; ++i) {
        std::thread th1{twoCacheLinerThread<0>};
        std::thread th2{twoCacheLinerThread<1>};
        th1.join(); th2.join();
        twoCacheLiner_average += twoCacheLiner.x + twoCacheLiner.y;
    }
    std::cout << "Tiempo promedio: " << (twoCacheLiner_average / max_runs / 2) << " ms\n\n";
}

Posible salida:

__cpp_lib_hardware_interference_size is not defined
hardware_destructive_interference_size == 64
hardware_constructive_interference_size == 64
sizeof( std::max_align_t ) == 32
 
sizeof( OneCacheLiner ) == 64
sizeof( TwoCacheLiner ) == 128
 
oneCacheLinerThread() usó 275.23 ms
oneCacheLinerThread() usó 330.37 ms
oneCacheLinerThread() usó 320.65 ms
oneCacheLinerThread() usó 389.14 ms
oneCacheLinerThread() usó 388.48 ms
oneCacheLinerThread() usó 448.34 ms
oneCacheLinerThread() usó 420.10 ms
oneCacheLinerThread() usó 459.01 ms
Tiempo promedio: 378 ms
 
twoCacheLinerThread() usó 123.79 ms
twoCacheLinerThread() usó 130.48 ms
twoCacheLinerThread() usó 119.03 ms
twoCacheLinerThread() usó 132.32 ms
twoCacheLinerThread() usó 116.26 ms
twoCacheLinerThread() usó 122.64 ms
twoCacheLinerThread() usó 116.42 ms
twoCacheLinerThread() usó 128.11 ms
Tiempo promedio: 123 ms

[editar] Véase también

Devuelve el número de hilos simultáneos admitidos por la implementación.
(función miembro estática pública de std::thread) [editar]
Devuelve el número de hilos simultáneos admitidos por la implementación.
(función miembro estática pública de std::thread) [editar]