Вот уместные вопросы для меня:
Получает ли процессор слово, с которым вы работаете, в одной операции?
Некоторый процессор может разрешить доступ к памяти переменной, которая оказывается не выровненной в памяти, делая две выборки одну за другой - конечно, не атомарно.
В этом случае могут возникнуть проблемы, если другой поток прерывает запись в эту область памяти, в то время как Первый поток извлек уже первую часть слова, а затем извлекает вторую часть, когда другой поток уже изменил слово.
thread 1 fetches first part of a XXXX
thread 1 fetches second part of a YYYY
thread 2 fetches first part of a XXXX
thread 1 increments double represented as XXXXYYYY that becomes ZZZZWWWW by adding b
thread 1 writes back in memory ZZZZ
thread 1 writes back in memory WWWW
thread 2 fetches second part of a that is now WWWW
thread 2 increments double represented as XXXXWWWW that becomes VVVVPPPP by adding b
thread 2 writes back in memory VVVV
thread 2 writes back in memory PPPP
Для сохранения его компактности я использовал один символ для представления 8 битов.
Теперь XXXXWWWW
и VVVVPPPP
будут представлять общие значения с плавающей запятой, отличные от ожидаемых. Это потому, что вы в итоге смешали две части двух разных двоичных представлений ( IEEE-754 ) двойных переменных.
Сказал, что я знаю, что в некоторых архитектурах на основе ARM доступ к данным не разрешен (это может привести к генерации ловушек), но я подозреваю, что процессоры Intel разрешают это вместо этого.
Поэтому, если ваша переменная a
выровнена, ваш результат может быть любым из
a + 1,23, a + 2,34, + 1,23 + 2,34
, если ваш переменная может быть выровнена неправильно (то есть имеет адрес, не кратный 8), ваш результат может быть любым из
a + 1,23, + 2,34, + 1,23 + 2,34 или rubbi sh значение
В качестве еще одного примечания, пожалуйста, имейте в виду, что даже если ваша среда alignof(double) == 8
, этого не обязательно достаточно, чтобы сделать вывод, что вы не собирается иметь проблемы смещения. Все зависит от того, откуда берется ваша конкретная переменная. Рассмотрим следующее (или запустите здесь ):
#pragma push()
#pragma pack(1)
struct Packet
{
unsigned char val1;
unsigned char val2;
double val3;
unsigned char val4;
unsigned char val5;
};
#pragma pop()
int main()
{
static_assert(alignof(double) == 8);
double d;
add(d,1.23); // your a parameter is aligned
Packet p;
add(p.val3,1.23); // your a parameter is now NOT aligned
return 0;
}
Поэтому утверждение alignof()
не обязательно гарантирует, что ваша переменная выровнена. Если ваша переменная не включена в какую-либо упаковку, то вы должны быть в порядке.
Пожалуйста, позвольте мне заявление об отказе для тех, кто еще читает этот ответ: использование std::atomic<double>
в этих ситуациях является лучший компромисс с точки зрения усилий по реализации и производительности для достижения безопасности потоков. Существуют архитектуры ЦП, которые имеют специальные эффективные инструкции для работы с переменными atomi c без введения тяжелых ограждений. Это может в конечном итоге удовлетворить ваши требования к производительности.