std:: hardware_destructive_interference_size, std:: hardware_constructive_interference_size
From cppreference.net
|
Definiert im Header
<new>
|
||
|
inline
constexpr
std::
size_t
hardware_destructive_interference_size = /*implementation-defined*/ ; |
(1) | (seit C++17) |
|
inline
constexpr
std::
size_t
hardware_constructive_interference_size = /*implementation-defined*/ ; |
(2) | (seit C++17) |
1)
Mindestabstand zwischen zwei Objekten zur Vermeidung von False Sharing. Garantiert mindestens
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)
Maximale Größe zusammenhängenden Speichers zur Förderung von True Sharing. Garantiert mindestens
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);
Hinweise
Diese Konstanten bieten eine portable Möglichkeit, auf die L1-Daten-Cache-Zeilengröße zuzugreifen.
| Feature-Test Makro | Wert | Std | Funktion |
|---|---|---|---|
__cpp_lib_hardware_interference_size
|
201703L
|
(C++17) |
constexpr
std
::
hardware_constructive_interference_size
und
constexpr std :: hardware_destructive_interference_size |
Beispiel
Das Programm verwendet zwei Threads, die atomar in die Datenmitglieder der gegebenen globalen Objekte schreiben. Das erste Objekt passt in eine Cache-Zeile, was zu "Hardware Interference" führt. Das zweite Objekt hält seine Datenmitglieder auf separaten Cache-Zeilen, wodurch mögliche "Cache-Synchronisierung" nach Thread-Schreibvorgängen vermieden wird.
Diesen Code ausführen
#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 auf 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}; // die Benchmark-Zeitabstimmung struct alignas(hardware_constructive_interference_size) OneCacheLiner // belegt eine Cache-Zeile { std::atomic_uint64_t x{}; std::atomic_uint64_t y{}; } oneCacheLiner; struct TwoCacheLiner // belegt zwei Cache-Zeilen { 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 (Anmerkung: Der Text enthält nur C++-spezifische Begriffe und HTML-Tags, die gemäß den Anweisungen nicht übersetzt werden müssen. Daher bleibt die Ausgabe identisch mit der Eingabe.)<double, std::milli> elapsed{now() - start}; std::lock_guard lk{cout_mutex}; std::cout << "oneCacheLinerThread() verbrachte " << 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 *Hinweis: Der Text innerhalb der HTML-Tags wurde nicht übersetzt, da es sich um C++-spezifische Begriffe handelt, die gemäß den Anweisungen unverändert bleiben sollen.*<double, std::milli> elapsed{now() - start}; std::lock_guard lk{cout_mutex}; std::cout << "twoCacheLinerThread() verbrachte " << 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 "ist nicht definiert, verwenden Sie " << hardware_destructive_interference_size << " als 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 << "Durchschnittliche T1-Zeit: " << (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 << "Durchschnittliche T2-Zeit: " << (twoCacheLiner_average / max_runs / 2) << " ms\n\n" << "Verhältnis T1/T2:~ " << 1.0 * oneCacheLiner_average / twoCacheLiner_average << '\n'; }
Mögliche Ausgabe:
__cpp_lib_hardware_interference_size = 201703 hardware_destructive_interference_size == 64 hardware_constructive_interference_size == 64 sizeof( OneCacheLiner ) == 64 sizeof( TwoCacheLiner ) == 128 oneCacheLinerThread() benötigte 517,83 ms oneCacheLinerThread() benötigte 533,43 ms oneCacheLinerThread() benötigte 527,36 ms oneCacheLinerThread() benötigte 555,69 ms oneCacheLinerThread() benötigte 574,74 ms oneCacheLinerThread() benötigte 591,66 ms oneCacheLinerThread() benötigte 555,63 ms oneCacheLinerThread() benötigte 555,76 ms Durchschnittliche T1-Zeit: 550 ms twoCacheLinerThread() benötigte 89,79 ms twoCacheLinerThread() benötigte 89,94 ms twoCacheLinerThread() benötigte 89,46 ms twoCacheLinerThread() benötigte 90,28 ms twoCacheLinerThread() benötigte 89,73 ms twoCacheLinerThread() benötigte 91,11 ms twoCacheLinerThread() benötigte 89,17 ms twoCacheLinerThread() benötigte 90,09 ms Durchschnittliche T2-Zeit: 89 ms Verhältnis T1/T2:~ 6,16
Siehe auch
|
[static]
|
gibt die Anzahl der gleichzeitig unterstützten Threads durch die Implementierung zurück
(öffentliche statische Memberfunktion von
std::thread
)
|
|
[static]
|
gibt die Anzahl der gleichzeitig unterstützten Threads durch die Implementierung zurück
(öffentliche statische Memberfunktion von
std::jthread
)
|