← Назад к вопросам

Что будет работать быстрее, поток или горутина на 8 или 16-ядерном сервере?

2.0 Middle🔥 112 комментариев
#Конкурентность и горутины#Производительность и оптимизация

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Введение в вопрос о производительности потоков и горутин

Этот вопрос затрагивает самую суть философии параллелизма в Go. Простой ответ: горутина почти всегда будет "восприниматься" как более быстрая и эффективная для типичных сценариев разработки на Go, особенно при высокой нагрузке с множеством одновременных операций. Однако, чтобы понять почему, нужно разобрать несколько слоев: модель выполнения, стоимость создания, переключение контекста и взаимодействие с железом.

Разбор ключевых понятий

Поток ОС (OS Thread)

  • Что это: Наименьшая единица выполнения, управляемая ядром операционной системы. Создается и управляется вызовами ОС (например, pthread_create в Unix-системах).
  • Стек: Имеет фиксированный размер стека (обычно от 1 до 8 МБ), выделяемый заранее.
  • Планировщик: Планируется вытесняющим планировщиком ядра ОС. Переключение контекста между потоками (context switch) — относительно дорогая операция, требующая сохранения/восстановления множества регистров CPU и работы с таблицами ядра.
  • Параллелизм на многоядерных CPU: Каждый поток может быть назначен (scheduled) на отдельное физическое ядро процессора (8 или 16 в вашем случае), обеспечивая истинный параллелизм.

Горутина (Goroutine)

  • Что это: Легковесная абстракция выполнения, управляемая рантаймом Go, а не ядром ОС.
  • Стек: Имеет маленький, динамически растущий/сжимающийся стек (начинается с ~2 КБ). Это позволяет создавать миллионы горутин без истощения памяти.
  • Планировщик Go: Использует кооперативный, невытесняющий (с некоторыми точками вытеснения) планировщик, работающий в пользовательском пространстве. Горутина добровольно отдает управление в определенных точках (канал, системный вызов, runtime.Gosched()).
  • Мультиплексирование: Множество горутин мультиплексируется на пуле потоков ОС. По умолчанию количество потоков ОС равно количеству логических CPU (на 16-ядерном сервере — 16), но это настраивается (GOMAXPROCS).

Что будет работать "быстрее"? Анализ по аспектам

Однозначно сказать, что "быстрее", нельзя — это зависит от метрики. Но мы можем сравнить по ключевым параметрам:

1. Скорость создания и завершения

Горутина несомненно быстрее. Ее создание — это несколько аллокаций в куче Go, а не системный вызов. Это дешевле на порядки.

// Создание тысяч горутин — быстро и дешево
for i := 0; i < 100000; i++ {
    go func(id int) {
        // do some work
    }(i)
}

Создание же 100000 потоков ОС привело бы к катастрофическим затратам памяти и времени.

2. Переключение контекста (Context Switching)

Переключение между горутинами дешевле, чем между потоками ОС. Планировщик Go выполняет переключение в пользовательском пространстве, не уходя в ядро. Ему не нужно манипулировать тяжеловесными структурами ядра, таблицами памяти (Page Tables) и т.д.

3. Параллелизм на 8/16 ядрах

Здесь оба подхода могут быть равно эффективны в утилизации всех ядер, но механизм разный.

  • Потоки: ОС напрямую распределит 16 потоков по 16 ядрам.
  • Горутины: Планировщик Go запустит до GOMAXPROCS потоков ОС (по умолчанию = 16 на 16-ядерном сервере). Эти 16 потоков будут исполнять горутины, распределяя их по ядрам. Если ваша задача CPU-bound (вычислительная) и состоит из 16 долгих горутин без блокировок, они также равномерно загрузят все 16 ядер, как и нативные потоки.
// CPU-bound задача, использующая все ядра
func main() {
    runtime.GOMAXPROCS(16) // Явно указываем (хотя по умолчанию и так 16)
    var wg sync.WaitGroup
    for i := 0; i < 16; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            // Интенсивные вычисления, без блокировок на каналах или I/O
            for j := 0; j < 1e9; j++ {
                // some calculation
            }
        }()
    }
    wg.Wait()
}

4. Сценарии с блокирующими операциями (I/O, Сеть)

Здесь горутины демон стрируют подавляющее преимущество.

  • Если поток ОС выполняет блокирующий системный вызов (например, чтение из сети), весь поток блокируется. Ядро вынуждено переключать контекст на другой поток.
  • Горутина, выполняющая блокирующую операцию, не блокирует поток ОС целиком. Планировщик Go "отвязывает" эту горутину от потока, ставит ее в очередь ожидания, а освободившийся поток начинает выполнять другую, готовую к работе, горутину. Это позволяет нескольким десяткам потоков ОС обслуживать десятки тысяч одновременных сетевых соединений.

Именно в этом асинхронном I/O с синхронным API — главная сила модели Go.

Итоговый ответ: Что будет работать быстрее?

  • На 8-ядерном или 16-ядерном сервере для подавляющего большинства задач, характерных для экосистемы Go (микросервисы, API, обработка запросов, конвейеры данных), использование горутин приведет к более высокой общей пропускной способности (throughput) и эффективности использования ресурсов.
  • Горутины "быстрее" не в смысле raw speed одной операции на CPU, а в смысле скорости масштабирования, дешевизны создания и невероятно эффективной работы с I/O.
  • Чисто вычислительная (CPU-bound) задача, распараллеленная на количество ядер, будет выполняться примерно с одинаковой скоростью как на нативных потоках, так и на горутинах, потому что в конечном итоге горутины будут исполняться на тех же самых физических ядрах через пул потоков ОС. Однако накладные расходы на управление горутинами внутри рантайма все равно ниже.
  • Критически важным является принцип: не создавать горутин больше, чем логических CPU, для CPU-задач. В этом случае планировщик Go будет работать оптимально.

Вывод: Go был спроектирован так, чтобы абстрагировать разработчика от сложностей работы с потоками ОС. Используйте горутины как основную единицу параллелизма. Они позволят вам легко и безопасно использовать все ядра вашего 8- или 16-ядерного сервера, обеспечивая при этом лучшую производительность в типичных для облака и сети сценариях благодаря своей легковесности и эффективному планировщику.

Что будет работать быстрее, поток или горутина на 8 или 16-ядерном сервере? | PrepBro