Выбор ответа Middle Производительность и кеш обновлено

False sharing

Вопрос

Четыре потока увеличивают каждый свой элемент массива std::atomic<long> counters[4]. Почему это медленнее, чем ожидалось?

Попробуйте сами

Выберите вариант, и откроется разбор.

Показать ответ и разбор

Коротко

Правильный ответ: Все счётчики в одной кеш-линии, ядра отбирают её друг у друга. Счётчики по 8 байт лежат в одной 64-байтной линии.

Варианты ответа

  1. Атомики всегда выполняются последовательно, даже на разных адресах
  2. Потоки не могут параллельно писать в один массив, ядра его блокируют
  3. Компилятор вставляет скрытый мьютекс в каждый std::atomic<long>
  4. Все счётчики в одной кеш-линии, ядра отбирают её друг у другаправильный ответ

Разбор

Счётчики по 8 байт лежат в одной 64-байтной линии. Каждая запись требует эксклюзивного владения линией, и протокол когерентности гоняет её между ядрами, хотя данные логически не общие. Лечится alignas(64) для каждого счётчика (или std::hardware_destructive_interference_size) либо локальными счётчиками с одной записью в конце.

  • Атомики разных переменных выполняются параллельно, мешает именно общая линия.
  • Работать параллельно с массивом можно.
  • std::atomic<long> на x86-64 lock-free, мьютекса нет.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.