Namespaces
Variants

std:: hardware_destructive_interference_size, std:: hardware_constructive_interference_size

From cppreference.net
Concurrency support library
Threads
(C++11)
(C++20)
hardware_destructive_interference_size hardware_constructive_interference_size
(C++17) (C++17)
this_thread namespace
(C++11)
(C++11)
Cooperative cancellation
Mutual exclusion
Generic lock management
Condition variables
(C++11)
Semaphores
Latches and Barriers
(C++20)
(C++20)
Futures
(C++11)
(C++11)
(C++11)
Safe reclamation
Hazard pointers
Atomic types
(C++11)
(C++20)
Initialization of atomic types
(C++11) (deprecated in C++20)
(C++11) (deprecated in C++20)
Memory ordering
(C++11) (deprecated in C++26)
Free functions for atomic operations
Free functions for atomic flags
Défini dans l'en-tête <new>
inline constexpr std:: size_t
hardware_destructive_interference_size = /*implementation-defined*/ ;
(1) (depuis C++17)
inline constexpr std:: size_t
hardware_constructive_interference_size = /*implementation-defined*/ ;
(2) (depuis C++17)
1) Décalage minimal entre deux objets pour éviter le faux partage. Garanti d'être au moins 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) Taille maximale de la mémoire contiguë pour favoriser le vrai partage. Garantie d'être au moins alignof ( std:: max_align_t )
struct together
{
    std::atomic<int> dog;
    int puppy;
};
struct kennel
{
    // Other data members...
    alignas(sizeof(together)) together pack;
    // Other data members...
};
static_assert(sizeof(together) <= std::hardware_constructive_interference_size);

Notes

Ces constantes fournissent un moyen portable d'accéder à la taille de ligne du cache de données L1.

Macro de test de fonctionnalité Valeur Std Fonctionnalité
__cpp_lib_hardware_interference_size 201703L (C++17) constexpr std :: hardware_constructive_interference_size et

constexpr std :: hardware_destructive_interference_size

Exemple

Le programme utilise deux threads qui écrivent de manière atomique dans les membres de données des objets globaux donnés. Le premier objet tient dans une ligne de cache, ce qui entraîne une "interférence matérielle". Le deuxième objet maintient ses membres de données sur des lignes de cache séparées, évitant ainsi une éventuelle "synchronisation du cache" après les écritures des threads.

#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 octets sur x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ...
    constexpr std::size_t hardware_constructive_interference_size = 64;
    constexpr std::size_t hardware_destructive_interference_size = 64;
#endif
std::mutex cout_mutex;
constexpr int max_write_iterations{10'000'000}; // l'ajustement du temps de référence
struct alignas(hardware_constructive_interference_size)
OneCacheLiner // occupe une ligne de cache
{
    std::atomic_uint64_t x{};
    std::atomic_uint64_t y{};
}
oneCacheLiner;
struct TwoCacheLiner // occupe deux lignes de cache
{
    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() a passé " << 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() a passé " << 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
        "n'est pas défini, utilisez " << hardware_destructive_interference_size
                               << " comme solution de repli\n";
#   endif
    std::cout << "hardware_destructive_interference_size == "
              << hardware_destructive_interference_size << '\n'
              << "hardware_constructive_interference_size == "
              << hardware_constructive_interference_size << "\n\n"
              << 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 << "Temps moyen T1 : "
              << (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 << "Temps T2 moyen : "
              << (twoCacheLiner_average / max_runs / 2) << " ms\n\n"
              << "Ratio T1/T2:~ "
              << 1.0 * oneCacheLiner_average / twoCacheLiner_average << '\n';
}

Sortie possible :

__cpp_lib_hardware_interference_size = 201703
hardware_destructive_interference_size == 64
hardware_constructive_interference_size == 64
sizeof( OneCacheLiner ) == 64
sizeof( TwoCacheLiner ) == 128
oneCacheLinerThread() a pris 517,83 ms
oneCacheLinerThread() a pris 533,43 ms
oneCacheLinerThread() a pris 527,36 ms
oneCacheLinerThread() a pris 555,69 ms
oneCacheLinerThread() a pris 574,74 ms
oneCacheLinerThread() a pris 591,66 ms
oneCacheLinerThread() a pris 555,63 ms
oneCacheLinerThread() a pris 555,76 ms
Temps moyen T1 : 550 ms
twoCacheLinerThread() a pris 89,79 ms
twoCacheLinerThread() a pris 89,94 ms
twoCacheLinerThread() a pris 89,46 ms
twoCacheLinerThread() a pris 90,28 ms
twoCacheLinerThread() a pris 89,73 ms
twoCacheLinerThread() a pris 91,11 ms
twoCacheLinerThread() a pris 89,17 ms
twoCacheLinerThread() a pris 90,09 ms
Temps moyen T2 : 89 ms
Ratio T1/T2:~ 6,16

Voir aussi

renvoie le nombre de threads concurrents pris en charge par l'implémentation
(fonction membre publique statique de std::thread )
renvoie le nombre de threads concurrents pris en charge par l'implémentation
(fonction membre publique statique de std::jthread )