Что такое переменная GOMAXPROCS и как она работает в Go
Что проверяют
Понимаете ли вы модель планировщика Go и отличаете ли конкурентность от параллелизма. На практике вопрос почти всегда приводит к настройке сервиса в контейнере с ограничением по процессору.
Как отвечать
GOMAXPROCS это число логических процессоров планировщика, то есть сколько горутин могут одновременно исполнять код Go. Это не предел числа горутин и не предел числа потоков операционной системы: потоки создаются сверх этого значения, когда горутина уходит в блокирующий системный вызов или в вызов C-кода.
По умолчанию значение равно числу доступных ядер. Читается и меняется одной функцией, ноль означает только чтение.
n := runtime.GOMAXPROCS(0)Менять на ходу дорого: вызов останавливает мир, поэтому значение задают на старте, обычно переменной окружения с тем же именем.
Главная практическая тема это контейнер. Рантайм видит ядра хоста, а планировщик ядра выдаёт процессу квоту по cgroup. При квоте в пару ядер на большой машине получаем лишние процессоры планировщика, лишние потоки, троттлинг в конце периода квоты и рост задержек, особенно на фазах сборки мусора. Лечится либо явной переменной окружения, либо библиотекой, которая выставляет значение по лимиту cgroup. В свежих версиях Go рантайм умеет учитывать лимит сам, но это стоит проверить именно на своей версии, а не принимать на веру.
Где спотыкаются
Говорят, что переменная ограничивает число потоков процесса. Она ограничивает только число потоков, одновременно исполняющих код Go; заблокированные в системных вызовах не считаются, а общий предел потоков задаётся отдельной настройкой рантайма.
Ставят единицу, чтобы избавиться от гонок. Гонки остаются: горутина вытесняется в произвольной точке, и одного процессора планировщика достаточно, чтобы получить порванное состояние. Синхронизацию это не заменяет, а только маскирует до первого изменения нагрузки.
Забывают про среду выполнения. Локально всё быстро, под квотой сервис тормозит, и причину ищут в коде. Обратная крайность тоже встречается: значение выкручивают выше числа доступных ядер в надежде на ускорение и получают лишние переключения и деградацию хвоста задержек.