LINUX.ORG.RU

C++ std::atomic<uint64_t> в шареной памяти.

 


0

4

Есть кусок шареной памяти, часть которого хочется рассматривать как массив 64-битных атомарных переменных.

Обычно я юзал GCC atomic buildins, например __sync_fetch_and_add для сложения с моей атомарной переменной. Кстати, должен ли я юзать при этом только выровненные адреса? Или MOV на платформе x86 атомарен на любых кривых адресах? По-моему не должен, ну ладно.

Так вот, такой код перестал казаться кошерным, захотелось заюзать std::atomic<uint64_t> в шареной памяти. Есть способы трактовать уже выделенный кусок памяти как последовательность нескольких таких объектов? Или надо вызвать несколько раз placelent new, чтобы отработал конструктор? На моей платформе std::atomic в конструкторе делает ничего (как я понял), стейта никакого не хранит и оверхеда по памяти ноль. Казалось бы, я могу просто скастить указатель и счастье, но на других платформах такое может рвануть не по-детски.

Как я понял, нормального кросс-платформенного решения на тему «трактовать уже выделенную память как массив std::atomic - ов для простого интегрального типа» нет. Народ говорит о каких-то полиморфных аллокаторах, которые на стадии экспериментов и вне стандарта пока.

Выскажитесь по сабжу.



Последнее исправление: hlamotron (всего исправлений: 2)

Если атомик lock free, то его можно шарить. Об этом явно сказано в стандарте.

Так что, я думаю, по правильно выровненному адресу можно делать атомики с помощью placement new.

utf8nowhere ★★★
()
Ответ на: комментарий от utf8nowhere

Притом, что на x86 std::atomic<uint64_t>.store() будет скомпилён в один MOV и потом MFENCE. Объекта там никакого нет, в памяти реально будет лежать только uint64_t.

hlamotron
() автор топика
Ответ на: комментарий от hlamotron

Объекта там никакого нет, в памяти реально будет лежать только uint64_t.

1.8 The C ++ object model [intro.object]
1 The constructs in a program create, destroy, refer to, access, and manipulate objects. An object is a region of storage.

Как понимать «объекта нет», когда он есть?

utf8nowhere ★★★
()
Ответ на: комментарий от hlamotron

Если твой невыровненный атомик попадёт на границу кэшлайнов, то процессор, конечно, попотеет и обеспечит-таки атомарность, но есть подозрение, что далеко не бесплатно.

Softwayer ★★
()

Думаю, если использовать атомарные переменные размером от int и больше (atomic<uint8_t> может быть реализован через uint32_t) и выровнять их по их размеру, то все будет, как ты ожидаешь.

Я нагуглил http://stackoverflow.com/questions/8749038/how-to-use-stdatomic-efficiently. Там первый ответ от товарища Вильямса, который можно воспринимать с той же достоверностью, что и стандарт C++.

Также по поводу атомарности не выравненных атомарных переменных. Вот тут в разделе Notes написано

Atomic types are also allowed to be sometimes lock-free, e.g. if only aligned memory accesses are naturally atomic on a given architecture, misaligned objects of the same type have to use locks.

А также то, что is_lock_free() не является статическим методом и наличие статической is_always_lock_free() в C++17 наталкивает на мысль, что будут ли использоваться блокировки или нет, зависит от выравнивания (но это уже предположения, так как проверить я это не смог).

В общем, я считаю, что если использовать atomic_int и крупнее, выровненный по границе кратной его размеру, то все будет как надо, причем кроссплатформенно.

chimera19
()
Ответ на: комментарий от chimera19

будут ли использоваться блокировки или нет, зависит от выравнивания

Изначальный проползал [1] такое предполагает:

The proposal provides lock-free query functions on individual objects rather than whole types to permit unavoidably misaligned atomic variables without penalizing the performance of aligned atomic variables.

Но при принятии в стандарт эту возможность закрыли:

29.4 Lock-free property (atomics.lockfree) параграф 2

The function atomic_is_lock_free (29.6) indicates whether the object is lock-free. In any given program execution, the result of the lock-free query shall be consistent for all pointers of the same type.

При этом между запусками программы результат всё же может меняться. Объясняется это, я думаю, этим параграфом из пропозала

The proposal provides run-time lock-free query functions rather than compile-time constants because subsequent implementations of a platform may upgrade locking operations with lock-free operations, so it is common for systems to abstract such facilities behind dynamic libraries, and we wish to leave that possiblility open. Furthermore, we recommend that implementations without hardware atomic support use that technique.

Итак, с is_lock_free() разобрались.

Теперь насчёт выравнивания и что понималось под misaligned в пропозале. Тут надо понимать что misaligned не с точки зрения абстрактной машины C++: создание и использование невыровненных объекты это UB, вне зависимости, atomic это или любой другой тип. Имелось в виду, что требование к выравниванию std::atomic<> в C++ может быть слабее, чем требование нижележащей платформы к атомарному доступу. std::atomic<int> может требовать выравнивания по 16 битам, когда атомарный доступ платформа обеспечивает только по 32 битам. Но, слава Яхве, это мозгокрутство в стандарт не вошло.

[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2427.html#DiscussLoc...

utf8nowhere ★★★
()
Ответ на: комментарий от chimera19

наличие статической is_always_lock_free() в C++17

В неё просто завернули результат макроса, который существует с C++11:

#define ATOMIC_[type]_LOCK_FREE unspecified

utf8nowhere ★★★
()

Кстати, должен ли я юзать при этом только выровненные адреса?

да

anonymous
()

судя по постановке вопроса, ты один из тех людей, которые «хорошо знают язык программирования», но не понимают, как работает процессор. соответственно, написание программ превращается в колдовство и гадание по гайдлайнам.

в общем, надо писать на жаве, там можно исключения в деструкторах кидать.

атомарные переменные std::atomic это тонкие обертки над просто числами, к которым получают доступ с помощью атомарных инструкций. без разницы как это сделано на конкретном языке программирования.

i36_zubov
()
Ответ на: комментарий от utf8nowhere

жесть. как же херово программистам на жабе. сидят, гадают по «стандартам». исключения в штаны роняют в деструкторах.

As another example, it’s well-known that on x86, a 32-bit mov instruction is atomic if the memory operand is naturally aligned, but non-atomic otherwise. In other words, atomicity is only guaranteed when the 32-bit integer is located at an address which is an exact multiple of 4.

http://preshing.com/20130618/atomic-vs-non-atomic-operations/

The integrity of the LOCK prefix is not affected by the alignment of the memory field.

атомарные операции всегда атомарны, load-store - только когда выровнены на х86(если быть точным - не пересекают границу кэш-линейки).

На ARM:

The address of an exclusive access must be aligned to the total number of bytes in the transaction.

i36_zubov
()
Ответ на: комментарий от utf8nowhere

Насколько я понял из того, что ты написал, atomic<T> без правильного выравнивания - это UB (так же как и int, например). Если же атомарные переменные правильно выравнены, то is_lock_free() всегда возвращает одинаковый результат для переменных одного типа, т.е. эта функция-член могла бы быть статической. Однако ее результат может измениться на каких-то платформах после перезагрузки, например.

chimera19
()

Всем спасибо, хороший полезный срачъ. Буду почёрпывать.

hlamotron
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.