std:: hardware_destructive_interference_size, std:: hardware_constructive_interference_size
|
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) |
struct keep_apart { alignas(std::hardware_destructive_interference_size) std::atomic<int> cat; alignas(std::hardware_destructive_interference_size) std::atomic<int> dog; };
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
|
[static]
|
renvoie le nombre de threads concurrents pris en charge par l'implémentation
(fonction membre publique statique de
std::thread
)
|
|
[static]
|
renvoie le nombre de threads concurrents pris en charge par l'implémentation
(fonction membre publique statique de
std::jthread
)
|