Обзор планировщика Go
Планировщик Go реализует M:N планирование, где M — это потоки ОС, а N — это горутины. Это позволяет множеству лёгких горутин работать эффективно на меньшем количестве потоков ОС.
Ключевые компоненты
Планировщик управляет тремя основными сущностями:
- G (Goroutine): Лёгкие единицы выполнения, которые дешевле создавать и управлять ими, чем потоками ОС
- M (Machine): Потоки ОС, которые выполняют горутины
- P (Processor): Логические процессоры, которые работают как планировщики, обычно соответствуя количеству ядер процессора
Алгоритм похищения задач (Work-Stealing)
Планировщик использует алгоритм похищения задач для поддержки балансировки нагрузки:
- У каждого P есть локальная очередь горутин, готовых к выполнению
- Когда P исчерпывает свою очередь, он пытается украсть работу из очередей других P
- Такой подход минимизирует конкуренцию за блокировки и максимизирует утилизацию CPU
- Горутины распределяются равномерно по всем доступным процессорам
Процесс планирования
- Горутина помещается в локальную очередь P
- M, связанный с этим P, выполняет горутины из очереди
- Когда горутина блокируется (например, на I/O), M может взять другую горутину
- Если все очереди пусты, планировщик может создать новые потоки или перейти в режим ожидания
Преимущества производительности
Такой дизайн даёт несколько преимуществ:
- Масштабируемость: Тысячи горутин могут работать эффективно на небольшом количестве потоков
- Низкие накладные расходы: Переключение контекста между горутинами дешевле, чем между потоками ОС
- Равномерное распределение: Похищение задач гарантирует, что все процессоры остаются загруженными
- Автоматическая балансировка: Планировщик адаптируется к меняющейся нагрузке без ручной настройки