std :: atomic обрабатывает пару атомарных int32 как один атомарный int64? - PullRequest
5 голосов
/ 14 января 2012

У меня есть пара без знака int32

std::atomic<u32> _start;
std::atomic<u32> _end;

Иногда я хочу установить начало или конец сравнительного обмена, поэтому я не хочу ложных сбоев, которые могут быть вызваны использованием CAS на всей 64-битной паре. Я просто хочу использовать 32-битный CAS.

_end.compare_exchange_strong(old_end, new_end);

Теперь я могу получить начало и конец как одно атомное 64-битное чтение. Или два отдельных 32-битных чтения. Разве не было бы быстрее сделать одну 64-битную атомарную выборку (с компилятором, добавляющим соответствующий предел памяти), а не две отдельные 32-атомные операции чтения битов с двумя заборами памяти (или компилятор оптимизировал бы это?)

Если так, как бы я сделал это в C ++ 11?

Ответы [ 2 ]

3 голосов
/ 14 января 2012

Стандарт не гарантирует, что std::atomics имеет тот же размер, что и базовый тип, и что операции на atomic не имеют блокировки (хотя они, вероятно, будут по крайней мере для uint32). Поэтому я почти уверен, что нет никакого подходящего способа объединить их в одну 64bit атомарную операцию. Поэтому вам нужно решить, хотите ли вы вручную объединить две переменные в 64-битную (и работать только с 64-битными операциями) или нет.

В качестве примера, платформа может не поддерживать 64bit CAS (для x86, который был добавлен с первым Pentium IIRC, поэтому он не будет доступен при компиляции 486. Совместимый. В этом случае он должен как-то блокироваться, поэтому атомарный может содержать как переменную 64bit, так и блокировку. Из

Относительно ограждений: Это зависит от memory_order, который вы указываете для своей операции. Если порядок памяти указывает, что две операции должны быть видимы в том порядке, в котором они выполняются, компилятор, очевидно, не сможет оптимизировать ограждение, иначе это может произойти. Конечно, при условии, что вы нацелены на x86, только memory_order_seq_cst фактически выдаст инструкцию-барьер из того, что я помню, поэтому все, что меньше, будет препятствовать переупорядочению команд, выполняемому компилятором, но не будет иметь реального штрафа).

Конечно, в зависимости от вашей платформы вам может не понравиться обработка двух std::atomic<int32> как одного из int64, выполняющего приведение либо через union, либо reinterpret_cast, просто имейте в виду, что это поведение не требуется стандартом и может (по крайней мере теоретически) прекратить работу в любое время (новая версия компилятора, другие параметры оптимизации, ...)

0 голосов
/ 16 марта 2012

Если ваши два целых числа требуют атомарных обновлений, то вы должны рассматривать их как одно атомное 64-битное значение, у вас действительно нет другого выбора.Отдельные целочисленные обновления не являются атомарными и нежизнеспособными.Я согласен с тем, что союзы здесь не подходят, и предлагаю вместо этого просто привести пару целых чисел к типу (INT64) и выполнить свой Cas64.

Использование критической секции является излишним - используйте Cas64, они стоят всего около 33 машинных циклов (неоперированных), в то время как критические секции стоят больше, чем 100 непопулярных циклов.

Обратите внимание, что это обычное деловыполнить ту же самую операцию над версионными указателями, которые в 32-разрядном режиме состоят из 32-разрядного указателя и 32-разрядного указателя, обновленных вместе как единое целое, используя Cas64, как описано.Кроме того, они действительно должны «выстраиваться в линию», потому что вы никогда не хотите, чтобы такие значения выходили за границы строки кэша.

...