From cppreference.com
Defined in header
inlineconstexprstd::size_thardware_destructive_interference_size=/*implementation-defined*/; (1) (since C++17)inlineconstexprstd::size_thardware_constructive_interference_size=/*implementation-defined*/; (2) (since C++17)1) Minimum offset between two objects to avoid false sharing. Guaranteed to be at least alignof(std::max_align_t)
structkeep_apart{alignas(std::hardware_destructive_interference_size)std::atomic<int>cat;alignas(std::hardware_destructive_interference_size)std::atomic<int>dog;};2) Maximum size of contiguous memory to promote true sharing. Guaranteed to be at least alignof(std::max_align_t)
structtogether{std::atomic<int>dog;intpuppy;};structkennel{// Other data members...alignas(sizeof(together))togetherpack;// Other data members...};static_assert(sizeof(together)<=std::hardware_constructive_interference_size);Notes
These constants provide a portable way to access the L1 data cache line size.
macroValueStdFeature
__cpp_lib_hardware_interference_size
(C++17)constexprstd::hardware_constructive_interference_size and constexprstd::hardware_destructive_interference_size
Example
The program uses two threads that atomically write to the data members of the given global objects. The first object fits in one cache line, which results in "hardware interference". The second object keeps its data members on separate cache lines, so possible "cache synchronization" after thread writes is avoided.
Run this code
#include<atomic>#include<chrono>#include<cstddef>#include<iomanip>#include<iostream>#include<mutex>#include<new>#include<thread>#ifdef __cpp_lib_hardware_interference_sizeusingstd::hardware_constructive_interference_size;usingstd::hardware_destructive_interference_size;#else// 64 bytes on x86-64 │ L1_CACHE_BYTES │ L1_CACHE_SHIFT │ __cacheline_aligned │ ...constexprstd::size_thardware_constructive_interference_size=64;constexprstd::size_thardware_destructive_interference_size=64;#endifstd::mutexcout_mutex;constexprintmax_write_iterations{10'000'000};// the benchmark time tuningstructalignas(hardware_constructive_interference_size)OneCacheLiner// occupies one cache line{std::atomic_uint64_tx{};std::atomic_uint64_ty{};}oneCacheLiner;structTwoCacheLiner// occupies two cache lines{alignas(hardware_destructive_interference_size)std::atomic_uint64_tx{};alignas(hardware_destructive_interference_size)std::atomic_uint64_ty{};}twoCacheLiner;inlineautonow()noexcept{returnstd::chrono::high_resolution_clock::now();}template<boolxy>voidoneCacheLinerThread(){constautostart{now()};for(uint64_tcount{};count!=max_write_iterations;++count)ifconstexpr(xy)oneCacheLiner.x.fetch_add(1,std::memory_order_relaxed);elseoneCacheLiner.y.fetch_add(1,std::memory_order_relaxed);conststd::chrono::duration<double,std::milli>elapsed{now()-start};std::lock_guardlk{cout_mutex};std::cout<<"oneCacheLinerThread() spent "<<elapsed.count()<<" ms\n";ifconstexpr(xy)oneCacheLiner.x=elapsed.count();elseoneCacheLiner.y=elapsed.count();}template<boolxy>voidtwoCacheLinerThread(){constautostart{now()};for(uint64_tcount{};count!=max_write_iterations;++count)ifconstexpr(xy)twoCacheLiner.x.fetch_add(1,std::memory_order_relaxed);elsetwoCacheLiner.y.fetch_add(1,std::memory_order_relaxed);conststd::chrono::duration<double,std::milli>elapsed{now()-start};std::lock_guardlk{cout_mutex};std::cout<<"twoCacheLinerThread() spent "<<elapsed.count()<<" ms\n";ifconstexpr(xy)twoCacheLiner.x=elapsed.count();elsetwoCacheLiner.y=elapsed.count();}intmain(){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";# endifstd::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";constexprintmax_runs{4};intoneCacheLiner_average{0};for(autoi{0};i!=max_runs;++i){std::threadth1{oneCacheLinerThread<0>};std::threadth2{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";inttwoCacheLiner_average{0};for(autoi{0};i!=max_runs;++i){std::threadth1{twoCacheLinerThread<0>};std::threadth2{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';}Possible output:
__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 See also
[static]
returns the number of concurrent threads supported by the implementation
(public static member function of std::thread)
[static]
returns the number of concurrent threads supported by the implementation
(public static member function of std::jthread)