Урок 22: гонки в глобальной памяти — зачем нужны atomics
До сих пор почти каждый kernel, который вы писали, давал каждому thread СВОЮ ячейку вывода: c[i] = a[i] + b[i], где thread i — единственный, кто когда-либо трогает c[i]. Поскольку никакие два thread не делят один пункт назначения, порядок их выполнения не важен — результат всегда верен. Но некоторые
Представьте 30 детей, которые делят один листок для подсчёта, где написано 5. Двое заглядывают в него вместе, оба видят 5, оба пишут 6 — и один отсчёт пропал. Атомарная операция — это правило, по которому только один ребёнок держит карандаш в каждый момент (прочитать, прибавить, записать), так что ни один отсчёт никогда не теряется.
- состояние гонки (race condition)
- Баг, при котором результат зависит от непредсказуемого тайминга и порядка, в котором threads обращаются к одним и тем же данным — в один запуск он может сработать, а на следующем дать сбой.
- чтение-изменение-запись (read-modify-write)
- x++ на самом деле — это три шага: прочитать x, прибавить 1, записать x. Другой thread может вклиниться между этими шагами.
- потерянное обновление (lost update)
- Два thread читают одно и то же старое значение и оба записывают обратно, так что один из инкрементов просто исчезает, и счётчик получается слишком маленьким.
- атомарная операция (atomic operation)
- Неделимая операция read-modify-write, которую не может прервать никакой другой thread — решение для гонки, например atomicAdd.