Гарантированные атомарные операции на типах POD, естественно выровненных на Intel - PullRequest
0 голосов
/ 13 февраля 2019

У меня есть многопоточные приложения C ++, работающие на ядрах Intel Xeon 32, скомпилированные с GCC 4.8.2 с включенной оптимизацией.

У меня есть несколько потоков (скажем, A, B, C), которые обновляют некоторые PODтипы и другой поток D, который каждые K секунд читает эти переменные и отправляет их в графический интерфейс.Потоки создаются через несколько ядер и сокетов.Записи защищены спин-блокировкой.Поток A, B, C чувствителен к задержке, где высокая производительность является критическим аспектом.Поток D. не чувствителен к задержке.

Что-то вроде:

Thread A,B,C
...
// a,b,c are up to 64 bits (let's say double)
spin-lock
a = computeValue();
b = computeValue();
c = computeValue();
spin-unlock
....

Thread D
...
// a,b,c are up to 64 bits (let's say double)
currValueA = a;
currValueB = b;
currValueC = c;
sendToGui(currValueA ,currValueB ,currValueC );
....

Я хочу воспользоваться пунктом 8.1.1 https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-3a-part-1-manual.html, о гарантированных атомарных операциях и избегатьблокировка, защищающая чтения, сделанные потоком D.

Насколько я понимаю, если a, b, c выровнены естественным образом (с размером не больше 64 бит), то нет риска, что поток D сможет прочитать значениедля a, b, c, который берется на полпути во время записи.Другими словами, запись и чтение будут выполняться атомарно.Поток D будет читать либо старое, либо новое значение.

Правильно ли мое понимание?

Я оставил компилятору GCC 4.8.2 позаботиться о выравнивании, т.е.t использовать любые встроенные директивы или функции gcc, такие как std :: alignas, sts :: alignof и т. д.

Я знаю, что код не переносим.Я бы предпочел не использовать std :: atomic, чтобы избежать ненужных накладных расходов.

1 Ответ

0 голосов
/ 13 февраля 2019

Чтение значения «взятого наполовину во время записи» - это только один аспект атомарности.

В наши дни процессоры сохраняют значения в кэшах, специфичных для процессора, поэтому в многопроцессорной системе два разных процессора вполне могутимеют разные значения для a, которые они разделяют.Маркировка a как атомарной гарантирует, что разные процессоры видят «одно и то же» значение.

Кроме того, компилятор и процессор часто переупорядочивают вычисления, чтобы лучше использовать возможности обработки.Все в порядке, если результат этих расчетов не изменился.(Это правило «как будто» в C ++).Но «не изменено» относится к выполнению в пределах одного потока.Оптимизации, которые работают в одном потоке, не обязательно работают, когда несколько потоков работают над одним и тем же объектом.И, как правило, вы не хотите, чтобы ваш однопоточный код компилировался параноидальным компилятором, который не выполняет общую оптимизацию, потому что он может нарушить многопоточный код.Вместо этого, пометка объекта как атомарного говорит о том, что компилятор должен быть очень осторожным в отношении того, что он перемещает, потому что значение этого объекта может быть изменено негласно другим кодом.

Итак, у вас есть выбор: hand- прокрутите свой код и надейтесь, что вы все поняли правильно, или примите, что автор библиотеки atomic, вероятно, знает больше об атомарности в вашей целевой системе, что вы делаете, и, вероятно, сделает лучшую работу.

...