Что такое GPM модель планировщика в Go и как она работает?
Что проверяют
Понимаете ли вы, почему горутин можно плодить десятками тысяч, и где планировщик перестаёт вас спасать. Для Go-разработчика это базовый вопрос, по ответу на который видно, работал ли человек с нагруженным сервисом или читал про горутины как про дешёвые потоки.
Как отвечать
Три сущности. G это горутина: стек в пару килобайт, который растёт по мере надобности, поэтому создание стоит дёшево. M это поток операционной системы. P это контекст выполнения с локальной очередью готовых горутин, и число P задаётся переменной GOMAXPROCS. Чтобы выполнять код, M обязан захватить P и брать горутины из его очереди, отсюда и ограничение на количество одновременно исполняемого кода.
Дальше про распределение работы. Очередей несколько: локальная у каждого P и общая. Когда локальная опустела, P забирает половину очереди у соседа, и только потом идёт в общую, это и называется кражей работы. Сетевые операции поток не блокируют: горутина паркуется, дескриптор уходит в опрашивающий механизм ввода-вывода, а при готовности горутина возвращается в очередь. На блокирующем системном вызове M блокируется вместе с горутиной, но P от него отцепляется и передаётся другому потоку, поэтому остальные горутины продолжают работать.
Хорошо упомянуть фоновый монитор: он отбирает P у затянувшихся системных вызовов и вытесняет горутину, занявшую процессор слишком долго. Вытеснение асинхронное, поэтому счётный цикл без вызовов функций планировщик уже не вешает.
Где спотыкаются
Называют горутину потоком и путают конкурентность с параллелизмом. Горутин может быть сто тысяч, одновременно исполняется столько, сколько есть P.
Из дешевизны горутин делают вывод, что их можно не считать. Каждая держит стек и, как правило, соединение или буфер, а все вместе они упираются в общий ресурс. Ограничитель в виде пула воркеров или семафора нужен там, где задачи порождаются входящим потоком.
Забывают про квоту процессора в контейнере. Долгое время число P бралось по ядрам хоста, и в поде с жёстким лимитом это давало лишние переключения и рваные задержки. В свежих версиях рантайм учитывает ограничение, но для старых его задают явно.