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
定義済みヘッダー <new>
inline constexpr std:: size_t
hardware_destructive_interference_size = /*implementation-defined*/ ;
(1) (C++17以降)
inline constexpr std:: size_t
hardware_constructive_interference_size = /*implementation-defined*/ ;
(2) (C++17以降)
1) 偽共有を避けるための2つのオブジェクト間の最小オフセット。少なくとも 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) 真の共有を促進するための連続メモリの最大サイズ。少なくとも alignof ( std:: max_align_t ) 以上であることが保証されます。
struct together
{
    std::atomic<int> dog;
    int puppy;
};
struct kennel
{
    // 他のデータメンバー...
    alignas(sizeof(together)) together pack;
    // 他のデータメンバー...
};
static_assert(sizeof(together) <= std::hardware_constructive_interference_size);

注記

これらの定数は、L1データキャッシュラインサイズにアクセスするための移植性の高い方法を提供します。

機能テスト マクロ 標準 機能
__cpp_lib_hardware_interference_size 201703L (C++17) constexpr std :: hardware_constructive_interference_size および

constexpr std :: hardware_destructive_interference_size

このプログラムは、与えられたグローバルオブジェクトのデータメンバーに対してアトミック書き込みを行う2つのスレッドを使用します。最初のオブジェクトは1つのキャッシュラインに収まるため、「ハードウェア干渉」が発生します。2番目のオブジェクトはデータメンバーを別々のキャッシュラインに保持するため、スレッド書き込み後の「キャッシュ同期」の可能性が回避されます。

#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
    // x86-64上の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}; // ベンチマーク時間の調整
struct alignas(hardware_constructive_interference_size)
OneCacheLiner // 1つのキャッシュラインを占有する
{
    std::atomic_uint64_t x{};
    std::atomic_uint64_t y{};
}
oneCacheLiner;
struct TwoCacheLiner // 2つのキャッシュラインを占有する
{
    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
(注:指示に従い、HTMLタグ・属性、C++固有用語、タグ内のテキストは翻訳せず、元のフォーマットを保持しています)(); }
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() が費やした " << 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() の実行時間 " << 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
        "は定義されていません。使用してください" << hardware_destructive_interference_size
                               << " をフォールバックとして\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 << "平均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 << "平均T2時間: "
              << (twoCacheLiner_average / max_runs / 2) << " ms\n\n"
              << "比率 T1/T2: ~ "
              << 1.0 * oneCacheLiner_average / twoCacheLiner_average << '\n';
}

出力例:

__cpp_lib_hardware_interference_size = 201703
hardware_destructive_interference_size == 64
hardware_constructive_interference_size == 64
sizeof( OneCacheLiner ) == 64
sizeof( TwoCacheLiner ) == 128
oneCacheLinerThread() 所要時間: 517.83 ms
oneCacheLinerThread() 所要時間: 533.43 ms
oneCacheLinerThread() 所要時間: 527.36 ms
oneCacheLinerThread() 所要時間: 555.69 ms
oneCacheLinerThread() 所要時間: 574.74 ms
oneCacheLinerThread() 所要時間: 591.66 ms
oneCacheLinerThread() 所要時間: 555.63 ms
oneCacheLinerThread() 所要時間: 555.76 ms
平均 T1 時間: 550 ms
twoCacheLinerThread() 所要時間: 89.79 ms
twoCacheLinerThread() 所要時間: 89.94 ms
twoCacheLinerThread() 所要時間: 89.46 ms
twoCacheLinerThread() 所要時間: 90.28 ms
twoCacheLinerThread() 所要時間: 89.73 ms
twoCacheLinerThread() 所要時間: 91.11 ms
twoCacheLinerThread() 所要時間: 89.17 ms
twoCacheLinerThread() 所要時間: 90.09 ms
平均 T2 時間: 89 ms
比率 T1/T2: ~6.16

関連項目

実装によってサポートされる同時実行スレッドの数を返す
( std::thread のpublic staticメンバー関数)
実装によってサポートされる同時実行スレッドの数を返す
( std::jthread のpublic staticメンバー関数)