Обновление двойной операции атомарного

В Java обновление переменных double и long может быть не атомарным, поскольку double / long рассматриваются как две отдельные 32-битные переменные.

http://java.sun.com/docs/books/jls/second_edition/html/memory.doc.html#28733

В C ++, если я использую 32-разрядный процессор Intel + компилятор Microsoft Visual C ++, обновляется ли двойная (8-байтная) операция атомарно?

Я не могу найти много упоминаний спецификации на это поведение.

Когда я сказал "атомная переменная "вот что я имею в виду:

Поток A пытается записать 1 в переменную x. Поток B пытается записать 2 в переменную x.

Мы получим значение 1 или 2 из переменной x, но не неопределенное значение.

 Peter Cordes04 сент. 2017 г., 08:05
Да, 32-битный x86 (начиная с оригинального Pentium) имеетэффективная поддержка оборудования без блокировкиstd::atomic загрузить, хранить и CAS. Делает ли ваш компилятор эффективный код или нет - это другая проблема:stackoverflow.com/questions/45055402/..., выровненныйdouble никогда не будетразрывая», но это'безопаснее в использовании.std::atomic

Ответы на вопрос(5)

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

 AProgrammer06 янв. 2012 г., 15:03
Атомарность и согласованность кэша - две разные вещи. Атомность связана с тем, что вы видите либо старое значение, либо новое, а не переходное состояние, которое может быть необходимо, когерентность кэша связана с порядком, в котором вы видите изменение нескольких областей памяти.
 Peter Cordes04 сент. 2017 г., 08:01
@AProgrammer: согласованность означает, что два кэша могуту строки кэша разные значения, поэтому, если хранилище зафиксировало кэш L1D в одном ЦП, другой ЦП не сможет загрузить другое значение.en.wikipedia.org/wiki/MESI_protocol, Упорядочение между модификациями разных строк кэша - это еще один уровень функциональности, созданный поверх когерентных кэшей.
 Peter Cordes04 сент. 2017 г., 07:56
х86 имеет связные кеши. Ты нене нужно беспокоиться об этом. Пока читатель действительно перезагружается из памяти, вместо повторного использования значения, которое компилятор может хранить в регистре, он в конечном итоге увидит обновление. (Вот почему вы должны использоватьstd::atomic теперь, когда C ++ 11 существует. Хотя современные компиляторы делают для него неэффективный код:stackoverflow.com/questions/45055402/...)
Решение Вопроса

86 и x86_64 8-байтовые записи или чтения гарантированно будут атомарными, если они выровнены. Цитирование из Белой книги по заказу памяти архитектуры Intel:

Упорядочение памяти Intel 64 гарантирует, что для каждой из следующих инструкций по доступу к памяти операция с памятью, по-видимому, будет выполняться как единый доступ к памяти независимо от типа памяти:

Инструкции, которые читают или записывают один байт.

Инструкции, которые читают или пишут слово (2 байта), адрес которого выровнен по границе 2 байта.

Инструкции, которые читают или пишут двойное слово (4 байта), адрес которого выровнен по границе 4 байта.

Инструкции, которые читают или пишут четырехзначное слово (8 байтов), адрес которого выровнен на границе 8 байтов.

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

 Ben Voigt03 нояб. 2010 г., 02:57
Вот'Правило для IA-32 (по вопросу): <цитата>Процессор Pentium (и более новые процессоры с тех пор) гарантирует, что следующие дополнительные операции с памятью всегда будут выполняться атомарно: • Чтение или запись четырех слов, выровненных по 64-битной границе 16-битный доступ к не кэшированным областям памяти, которые вписываются в 32-битную шину данных. Процессоры семейства P6 (и более новые процессоры с тех пор) гарантируют, что следующая дополнительная операция с памятью всегда будет выполняться атомарно: • Нераспределенные 16-, 32- и 64-битные обращения к кешируемой памяти, которые помещаются в строку кеша </ Цитата>
 Gunther Piez18 авг. 2009 г., 14:07
Да, это указано в компиляторах ABI. Для неавтоматических переменных двойники всегда выровнены, за исключением случаев, когда явно указано, что они не выровнены. Для переменных в стеке в 32-битной системе они могут стать невыровненными, если стек каким-то образом выравнивается, например, вызывается функция из внешней, не-C-программы. Но ты нев любом случае, я не хочу возвращать объекты из стека в функции ...
 Steve Jessop18 авг. 2009 г., 12:53
Это также зависит от компилятора, который не требуется для того, чтобы гарантировать, что парные числа в первую очередь выровнены по 8, или использовать один оператор quadword для их чтения или записи. Хотя тыЯ думаю, что это, вероятно, будет, а также я ожидаю, что Visual C ++ документы, делает ли это или нет.
 Steve Jessop18 авг. 2009 г., 15:58
Ты нене возвращает автоматику, но вы можете передать указатель на них в функцию, которую вы вызываете. Но я думаю, что тыЯ сказал, что этого достаточно для спрашивающего - пока он контролирует их определение, он может гарантировать, что доступ к его двойникам является атомарным.

что в любой архитектуре переключение потоков / контекстов прервало бы обновление регистра наполовину, так что вы получили, например, 18 бит обновленных 32 бит, которые он собирался обновить. То же самое для обновления ячейки памяти (при условии, что этоs базовый блок доступа, 8,16,32,64 бита и т. д.).

 AProgrammer18 авг. 2009 г., 11:46
Проблема не в переключении контекста, а в многоядерности и многоядерности.
 Steve Jessop18 авг. 2009 г., 12:56
Так что, если двойник лежит за границей двух строк кэша? Я сомневаюсь, что MSVC ++ сделает это, потому что все будет выровнено по размеру в степени 2. Но если выобобщая, этоне является обязательным требованием стандарта C ++ (и, по крайней мере, в одном из ARI ABI длинные и двойные должны быть только с 4-мя, а не с 8-ю).
 Indy900018 авг. 2009 г., 12:32
Даже в многоядерной / многопроцессорной архитектуре доступ к физической памяти должен быть сериализован контроллером памяти. Это's электрически невозможно позволить нескольким устройствам получать доступ к одной и той же схеме в одно и то же время. Контроллер памяти обращается к памяти в блоках и в единицах ширины шины данных, поэтому он 'невозможно частичное обновление ячейки памяти.

Можно с уверенностью предположить, что обновление double никогда не бывает атомарным, даже еслиРазмер s такой же, как у int с атомарной гарантией. Причина в том, что если имеет другой путь обработки, так какнекритический и дорогой тип данных. Например, даже данные барьеры обычно упоминают, что они неприменяется к данным / операциям с плавающей запятой в целом.

Visual C ++ будет выравнивать примитивные типы (см.статья) и хотя это должно гарантировать, чтобиты выигралне записывается при записи в память (8-байтовое выравнивание всегда находится в одной 64- или 128-битной строке кэша), остальное зависит от того, как процессор обрабатывает неатомарные данные в нем 's кеш и является ли чтение / сброс строки кеша прерываемым. Так что, если вы покопаетесь в документации Intel на то, какое ядро вы используете, и это дает вам такую гарантию, тогда вы в безопасности.

Причина, по которой спецификация Java настолько консервативна, заключается в том, чтоОн должен работать одинаково на старом 386 и на Corei7. Что, конечно, бредовое, но обещание - это обещание, поэтому оно обещает меньше :-)

Причина яЯ хочу сказать, что вам нужно искать документацию по процессору, это то, что ваш процессор может быть старым 386 или похожим :-))не забывайте, что на 32-битном процессоре ваш 8-байтовый блок занимает 2 "раундов» чтобы получить доступ, так что вы зависите от механики доступа к кешу.

Очистка строки кэша, обеспечивающая гораздо более высокую гарантию согласованности данных, применяется только к относительно свежему ЦП с гарантией Intel-ian (автоматическая согласованность кэша).

 Peter Cordes04 сент. 2017 г., 07:52
Этобезопасный предположить, что обновлениеdouble никогда не бывает атомарным, но чрезмерно консервативным.
 Peter Cordes04 сент. 2017 г., 07:51
Некоторые из первых 386 систем могли иметь только 16-битную шину данных (и не иметь внутреннего кеша), так что это означало быdouble заняло 4 цикла памяти. Во всяком случае, так как современный MSVC ++ неЕсли вы не собираетесь создавать код, который даже работает на чем-то более старом, чем Pentium (P5), вы гарантированно, что выровненные загрузки / хранилища 8B являются атомарными, даже если это делается с x87 или SSE. (Понятия не имею, почему вы говорите, что FP менее оптимизирован. X86 имеет высокопроизводительную плавающую точку годами.) Как только данные достигают кеша, они нене помню, как он туда попал, так чтонеатомарные данные в кеше странно.
 Peter Cordes04 сент. 2017 г., 07:46
Быть 32-битным процессором x86 неЭто означает, что все внутренние пути данных являются только 32-битными. Например, Pentium 4 (даже ранний 32-битный P4) делаетmovaps 16-байтовая выровненная загрузка в единственном доступе к ее кешу L1D. Вы делаете хорошее замечание, что атомарный доступ к кешу неt гарантирует атомарность в целом (например, AMD K10 имеет атомную загрузку / память SSE 16B в одном сокете, но протокол когерентности вводитразрывая на 8В границы для нитей на разных розетках)

меняющую двойное:

#include 

int main(int argc, char** argv)
{
    double i = 3.14159265358979323;
    i += 84626.433;
}

Я скомпилировал его без оптимизации (gcc -O0), и все операции присваивания выполняются с помощью инструкций с одним ассемблером, таких какfldl .LC0 а такжеfaddp %st, %st(1), (i += 84626.433 конечно сделано две операции,faddp а такжеfstpl).

Может ли поток действительно быть прерван внутри одной инструкции, такой как? "faddp

 Suma30 июн. 2010 г., 10:41
Может ли поток действительно прерваться внутри одной инструкции, такой как faddp? ", Нет, нить нельзяпрерываться» внутри одной инструкции два ЦП могут выполнять свои инструкции одновременно, и один ЦП может видеть только часть результата второго ЦП, если инструкция не выполняется в одной транзакции шины.

Ваш ответ на вопрос