Чому std :: atomic <T> :: is_lock_free () не є статичним, а також constexpr?


9

Хто-небудь може сказати мені, чи std :: atomic :: is_lock_free () не є статичним, а також constexpr? Наявність його нестатичного та / або як non-constexpr для мене не має сенсу.



3
Я збираюся викинути "вирівнювання" там.
Макс Лангхоф

@MaxLanghof Ви маєте на увазі, що не всі екземпляри будуть вирівняні однаково?
curiousguy

1
Майк, ні, я не знав, але дякую за цей натяк; це дуже корисно для мене. Але я запитую себе, чому існує рішення між is_lock_free () та is_always_lock_free. Це не може бути через нерівномірну атоміку, що тут запропонували інші, оскільки мова визначає нерівномірний доступ до того, щоб мати все-таки невизначене поведінку.
Боніта Монтеро

Відповіді:


10

Як пояснено на cppreference :

Усі типи атомів, за винятком std :: atomic_flag, можуть бути реалізовані за допомогою мутексів або інших операцій блокування, а не з використанням атомних інструкцій щодо блокування атомних процесорів. Атомні типи також можуть бути іноді без замків, наприклад, якщо для заданої архітектури лише вирівняні пам’яті пам’яті природно є атомними, нерівні об’єкти одного типу повинні використовувати блоки.

Стандарт C ++ рекомендує (але не вимагає), щоб атомні операції без блокування були також без адреси, тобто підходять для зв'язку між процесами за допомогою спільної пам'яті.

Як вже згадували багато інших, це std::is_always_lock_freeможе бути саме те, що ви справді шукаєте.


Редагування: Для уточнення, типи об'єктів C ++ мають значення вирівнювання, яке обмежує адреси їхніх примірників лише певними кратними повноваженнями двох ( [basic.align]). Ці значення вирівнювання визначені реалізацією для основних типів і не повинні дорівнювати розміру типу. Вони також можуть бути більш суворими, ніж те, що апаратне забезпечення насправді може підтримувати.

Наприклад, x86 (в основному) підтримує нестандартний доступ. Однак ви знайдете більшість компіляторів, що мають alignof(double) == sizeof(double) == 8x86, оскільки нестандартний доступ має безліч недоліків (швидкість, кешування, атомність ...). Але, наприклад, #pragma pack(1) struct X { char a; double b; };або alignas(1) double x;дозволяє мати "неприєднаних" doubles. Отже, коли cppreference говорить про "вирівнювання доступу до пам'яті", імовірно, це робиться з точки зору природного вирівнювання типу для обладнання, не використовуючи тип C ++ таким чином, що суперечить його вимогам вирівнювання (що було б UB).

Ось додаткова інформація: Який фактичний ефект від успішного нерівного доступу на x86?

Будь ласка, ознайомтеся з проникливими коментарями @Peter Cordes нижче!


1
32-розрядний x86 - хороший приклад того, де ви знаходите ABI alignof(double)==4. Але std::atomic<double>все ж alignof() = 8замість того, щоб перевіряти вирівнювання під час виконання. Використання упакованої структури, яка не вирівнює атома, розбиває ABI і не підтримується. (GCC для 32-бітного x86 вважає за краще надати 8-байтовим об'єктам природне вирівнювання, але правила упаковки структури перекривають це та ґрунтуються лише на alignof(T), наприклад, у системі i386 V. G ++ використовується помилка, де atomic<int64_t>всередині структури може бути не атомним тому що це тільки припустили. GCC (для C не C ++) все ще має цю помилку!)
Пітер Кордес

2
Але правильна реалізація C ++ 20 std::atomic_ref<double>або відкине недостатньо вирівнювання doubleповністю, або перевірятиме вирівнювання під час виконання на платформах, де це законно для звичайного doubleта int64_tмає бути менше, ніж природним чином. (Оскільки atomic_ref<T>діє на об'єкт, який був оголошений як звичайний T, і має лише мінімальне вирівнювання, alignof(T)не маючи можливості надати йому додаткове вирівнювання.)
Пітер Кордес,

2
Дивіться gcc.gnu.org/bugzilla/show_bug.cgi?id=62259 для тепер виправленої помилки libstdc ++ та gcc.gnu.org/bugzilla/show_bug.cgi?id=65146 для все ще невдалої помилки C, включаючи a чистий тест ISO C11, який показує розрив а, _Atomic int64_tколи компілюється зі струмом gcc -m32. У всякому разі, моя точка зору в тому , що справжні компілятор не підтримують під вирівняну Atomics, і не робити перевірки під час виконання (поки що?), Так #pragma packі __attribute__((packed))будуть просто привести до не-атомарности; об’єкти все ще повідомлять, що вони є lock_free.
Пітер Кордес

1
Але так, мета is_lock_free()полягає в тому, щоб дозволити реалізаціям працювати інакше, ніж діючі фактично; з перевірками виконання на основі фактичного вирівнювання для використання атомних інструкцій, підтримуваних HW, або для використання блокування.
Пітер Кордес

3

Ви можете використовувати std::is_always_lock_free

is_lock_free залежить від фактичної системи і її неможливо визначити під час компіляції.

Відповідне пояснення:

Атомні типи також можуть бути іноді без замків, наприклад, якщо для заданої архітектури лише вирівняні пам’яті пам’яті природно є атомними, нерівні об’єкти одного типу повинні використовувати блоки.


1
std::numeric_limits<int>::maxзалежить від архітектури, але є статичним і constexpr. Я думаю, у відповіді немає нічого поганого, але я не купую першу частину міркувань
idclev 463035818

1
Чи не визначає мовний нестандартний доступ таким чином мати невизначену поведінку, так що оцінка безвізності чи ні під час виконання не була б дурницею?
Боніта Монтеро

1
Немає сенсу вирішувати між узгодженими та нерівними доступами, оскільки мова визначає останніх як невизначену поведінку.
Боніта Монтеро

@BonitaMontero Існує сенс "нерівного у вирівнюванні об'єкта C ++" та "нерівного в тому, що апаратне забезпечення подобається". Вони не обов'язково однакові, але на практиці вони часто є. Приклад ви показуєте один такий випадок , коли компілятор , по- видимому , має вбудований в припущенні , що два є тим же - які тільки означає , що НЕ is_lock_freeмає сенс на цьому компілятор .
Макс Лангхоф

1
Ви можете бути впевнені, що атомник мав би відповідне вирівнювання, якщо є вимога вирівнювання.
Боніта Монтеро

1

Я встановив 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()це колись правда.)


1
Так, більшість реалізацій вирішили надати, 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 існує, щоб інші компілятори могли робити різні речі.
Макс Лангхоф

1
Ви можете бути впевнені, що якщо мова говорить про те, що нестандартний доступ має невизначене поведінку, усі атоми повинні бути правильно вирівняні. З-за цього жодна реалізація не проводитиме перевірок виконання.
Боніта Монтеро

@BonitaMontero Так, але в мові немає нічого, що забороняє alignof(std::atomic<double>) == 1(тому не було б "неприєднаного доступу" в сенсі C ++, отже, і UB), навіть якщо апаратне забезпечення може гарантувати лише безблокові атомні операції для doubles на 4 або 8 байтових меж. Потім компілятору доведеться використовувати блокування у нерівномірних випадках (і повертати відповідне булеве значення з is_lock_free, залежно від місця пам'яті об'єкта).
Макс Лангхоф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.