Хто-небудь може сказати мені, чи std :: atomic :: is_lock_free () не є статичним, а також constexpr? Наявність його нестатичного та / або як non-constexpr для мене не має сенсу.
Хто-небудь може сказати мені, чи std :: atomic :: is_lock_free () не є статичним, а також constexpr? Наявність його нестатичного та / або як non-constexpr для мене не має сенсу.
Відповіді:
Як пояснено на cppreference :
Усі типи атомів, за винятком std :: atomic_flag, можуть бути реалізовані за допомогою мутексів або інших операцій блокування, а не з використанням атомних інструкцій щодо блокування атомних процесорів. Атомні типи також можуть бути іноді без замків, наприклад, якщо для заданої архітектури лише вирівняні пам’яті пам’яті природно є атомними, нерівні об’єкти одного типу повинні використовувати блоки.
Стандарт C ++ рекомендує (але не вимагає), щоб атомні операції без блокування були також без адреси, тобто підходять для зв'язку між процесами за допомогою спільної пам'яті.
Як вже згадували багато інших, це std::is_always_lock_free
може бути саме те, що ви справді шукаєте.
Редагування: Для уточнення, типи об'єктів C ++ мають значення вирівнювання, яке обмежує адреси їхніх примірників лише певними кратними повноваженнями двох ( [basic.align]
). Ці значення вирівнювання визначені реалізацією для основних типів і не повинні дорівнювати розміру типу. Вони також можуть бути більш суворими, ніж те, що апаратне забезпечення насправді може підтримувати.
Наприклад, x86 (в основному) підтримує нестандартний доступ. Однак ви знайдете більшість компіляторів, що мають alignof(double) == sizeof(double) == 8
x86, оскільки нестандартний доступ має безліч недоліків (швидкість, кешування, атомність ...). Але, наприклад, #pragma pack(1) struct X { char a; double b; };
або alignas(1) double x;
дозволяє мати "неприєднаних" double
s. Отже, коли cppreference говорить про "вирівнювання доступу до пам'яті", імовірно, це робиться з точки зору природного вирівнювання типу для обладнання, не використовуючи тип C ++ таким чином, що суперечить його вимогам вирівнювання (що було б UB).
Ось додаткова інформація: Який фактичний ефект від успішного нерівного доступу на x86?
Будь ласка, ознайомтеся з проникливими коментарями @Peter Cordes нижче!
alignof(double)==4
. Але std::atomic<double>
все ж alignof() = 8
замість того, щоб перевіряти вирівнювання під час виконання. Використання упакованої структури, яка не вирівнює атома, розбиває ABI і не підтримується. (GCC для 32-бітного x86 вважає за краще надати 8-байтовим об'єктам природне вирівнювання, але правила упаковки структури перекривають це та ґрунтуються лише на alignof(T)
, наприклад, у системі i386 V. G ++ використовується помилка, де atomic<int64_t>
всередині структури може бути не атомним тому що це тільки припустили. GCC (для C не C ++) все ще має цю помилку!)
std::atomic_ref<double>
або відкине недостатньо вирівнювання double
повністю, або перевірятиме вирівнювання під час виконання на платформах, де це законно для звичайного double
та int64_t
має бути менше, ніж природним чином. (Оскільки atomic_ref<T>
діє на об'єкт, який був оголошений як звичайний T
, і має лише мінімальне вирівнювання, alignof(T)
не маючи можливості надати йому додаткове вирівнювання.)
_Atomic int64_t
коли компілюється зі струмом gcc -m32
. У всякому разі, моя точка зору в тому , що справжні компілятор не підтримують під вирівняну Atomics, і не робити перевірки під час виконання (поки що?), Так #pragma pack
і __attribute__((packed))
будуть просто привести до не-атомарности; об’єкти все ще повідомлять, що вони є lock_free
.
is_lock_free()
полягає в тому, щоб дозволити реалізаціям працювати інакше, ніж діючі фактично; з перевірками виконання на основі фактичного вирівнювання для використання атомних інструкцій, підтримуваних HW, або для використання блокування.
Ви можете використовувати std::is_always_lock_free
is_lock_free
залежить від фактичної системи і її неможливо визначити під час компіляції.
Відповідне пояснення:
Атомні типи також можуть бути іноді без замків, наприклад, якщо для заданої архітектури лише вирівняні пам’яті пам’яті природно є атомними, нерівні об’єкти одного типу повинні використовувати блоки.
std::numeric_limits<int>::max
залежить від архітектури, але є статичним і constexpr
. Я думаю, у відповіді немає нічого поганого, але я не купую першу частину міркувань
is_lock_free
має сенс на цьому компілятор .
Я встановив Visual Studio 2019 на свій Windows-ПК, і цей devenv також має ARMv8-компілятор. ARMv8 дозволяє несанкціонований доступ, але порівнювати та змінювати місцями, замінені доданки та ін. А також чистий навантаження / чистий склад, що використовує ldp
або stp
(навантаження пари або запас 32-бітних регістрів), гарантовано є атомним лише тоді, коли вони природно вирівняні.
Тому я написав невелику програму, щоб перевірити, що is_lock_free () повертає для довільного атомарного покажчика. Отже ось код:
#include <atomic>
#include <cstddef>
using namespace std;
bool isLockFreeAtomic( atomic<uint64_t> *a64 )
{
return a64->is_lock_free();
}
І це розбирання isLockFreeAtomic
|?isLockFreeAtomic@@YA_NPAU?$atomic@_K@std@@@Z| PROC
movs r0,#1
bx lr
ENDP
Це просто returns true
, ака 1
.
Ця реалізація вибрана для використання, alignof( atomic<int64_t> ) == 8
тому кожна atomic<int64_t>
коректно вирівняна. Це дозволяє уникнути необхідності перевірки вирівнювання часу виконання кожного завантаження та зберігання.
(Примітка редактора: це звичайне явище; більшість реалізацій C ++ в реальному житті працюють таким чином. Ось чому std::is_always_lock_free
це так корисно: тому що, як правило, це стосується типів, де is_lock_free()
це колись правда.)
atomic<uint64_t>
і alignof() == 8
тому їм не потрібно перевіряти вирівнювання під час виконання. Цей старий API дає їм можливість цього не робити, але в поточних HW є набагато більш сенсом просто вимагати вирівнювання (інакше UB, наприклад, без атомності). Навіть у 32-бітовому коді, де int64_t
може бути лише 4-байтне вирівнювання, atomic<int64_t>
потрібен 8-байт. Дивіться мої коментарі до іншої відповіді
alignof
значення для фундаментального типу таким же, як "добре" вирівнювання обладнання, то is_lock_free
завжди буде true
(і так буде is_always_lock_free
). Ваш компілятор тут робить саме це. Але API існує, щоб інші компілятори могли робити різні речі.
alignof(std::atomic<double>) == 1
(тому не було б "неприєднаного доступу" в сенсі C ++, отже, і UB), навіть якщо апаратне забезпечення може гарантувати лише безблокові атомні операції для double
s на 4 або 8 байтових меж. Потім компілятору доведеться використовувати блокування у нерівномірних випадках (і повертати відповідне булеве значення з is_lock_free
, залежно від місця пам'яті об'єкта).
is_always_lock_free
?