std::hardware_destructive_interference_size、std::hardware_constructive_interference_size
来自 cppreference.com
在头文件 <new> 中定义 |
||
inline constexpr std::size_t hardware_destructive_interference_size = /*实现定义*/; |
(1) | (自 C++17) |
inline constexpr std::size_t hardware_constructive_interference_size = /*实现定义*/; |
(2) | (自 C++17) |
1) 避免虚假共享的两个对象之间的最小偏移量。保证至少为 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 { // Other data members... alignas(sizeof(together)) together pack; // Other data members... }; static_assert(sizeof(together) <= std::hardware_constructive_interference_size);
[编辑] 备注
这些常量提供了一种可移植的方式来访问 L1 数据缓存行大小。
功能测试 宏 | 值 | Std | 功能 |
---|---|---|---|
__cpp_lib_hardware_interference_size |
201703L | (C++17) | constexpr std::hardware_constructive_interference_size 和
|
[编辑] 示例
该程序使用两个线程原子地写入给定全局对象的成员数据。第一个对象适合一个缓存行,这会导致“硬件干扰”。第二个对象将其成员数据保存在单独的缓存行上,因此可以避免线程写入后可能的“缓存同步”。
运行此代码
#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 on 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}; // the benchmark time tuning struct alignas(hardware_constructive_interference_size) OneCacheLiner // occupies one cache line { std::atomic_uint64_t x{}; std::atomic_uint64_t y{}; } oneCacheLiner; struct TwoCacheLiner // occupies two cache lines { 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() spent " << 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() spent " << 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 "is not defined, use " << hardware_destructive_interference_size << " as fallback\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 << "Average T1 time: " << (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 << "Average T2 time: " << (twoCacheLiner_average / max_runs / 2) << " ms\n\n" << "Ratio 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() spent 517.83 ms oneCacheLinerThread() spent 533.43 ms oneCacheLinerThread() spent 527.36 ms oneCacheLinerThread() spent 555.69 ms oneCacheLinerThread() spent 574.74 ms oneCacheLinerThread() spent 591.66 ms oneCacheLinerThread() spent 555.63 ms oneCacheLinerThread() spent 555.76 ms Average T1 time: 550 ms twoCacheLinerThread() spent 89.79 ms twoCacheLinerThread() spent 89.94 ms twoCacheLinerThread() spent 89.46 ms twoCacheLinerThread() spent 90.28 ms twoCacheLinerThread() spent 89.73 ms twoCacheLinerThread() spent 91.11 ms twoCacheLinerThread() spent 89.17 ms twoCacheLinerThread() spent 90.09 ms Average T2 time: 89 ms Ratio T1/T2:~ 6.16
[编辑] 另请参阅
返回实现支持的并发线程数 ( std::thread 的公共静态成员函数) | |
返回实现支持的并发线程数 ( std::jthread 的公共静态成员函数) |