False Sharing в Go: как независимые счётчики начинают мешать

False Sharing в Go: как независимые счётчики начинают мешать друг другу Представим структуру с двумя счётчиками: type Counters struct { a int64 b int64 } Одна goroutine обновляет a, другая - b. На уровне кода эти поля никак не связаны. Но CPU работает не с отдельными переменными, а с блоками памяти, которые называются cache lines. Обычно размер кэш линии - 64 байта. Если a и b попали в одну cache line: CPU 1 CPU 2 a b ↓ ↓ ┌──────────────────────────┐ │ a │ b │ ... │ └──────────────────────────┘ ↑ одна cache line CPU 1 изменяет a, и кэш линия становится недействительной для CPU 2. CPU 2 изменяет b, и та же кэш линия становится недействительной уже для CPU 1. Так происходит снова и снова. Потоки работают с разными переменными, но из-за расположения данных в памяти постоянно синхронизируют одну и ту же кэш линию. Это и называется False Sharing. Как избежать? Иногда поля разделяют с помощью padding: type Counters struct { a int64 _ [56]byte b int64 } Теперь a и b с высокой вероятностью окажутся в разных кэш линиях. Но добавлять padding наугад не стоит. Сначала нужно подтвердить проблему с помощью бенчмарков или профайлера, а уже потом менять layout структуры. Главная мысль: конкурентность - это не только goroutines и mutex. Даже независимые значения могут влиять друг на друга из-за того, как они расположены в памяти и работают с CPU cache.