MAATRIX / Блог / Что такое context switch и сколько он стоит вашему серверу

Что такое context switch и сколько он стоит вашему серверу

MAATRIX

Сервер вроде бы не загружен — CPU показывает 40%, а сайт всё равно подтормаживает и запросы выполняются дольше, чем должны. Часто причина не в недостатке вычислительной мощности, а в том, что ядро тратит время не на полезную работу, а на переключение между процессами и потоками. Разберёмся, что такое context switch, почему он не бесплатен даже на самом быстром CPU и как понять, что именно он — ваша проблема.

Что происходит при переключении контекста

Context switch (переключение контекста) — это момент, когда CPU перестаёт выполнять один поток или процесс и начинает выполнять другой. С точки зрения пользователя это происходит незаметно и постоянно: на одном ядре физически может выполняться только один поток в любой момент времени, а процессов и потоков в системе обычно намного больше, чем ядер. Планировщик ядра (scheduler) создаёт иллюзию параллельности, быстро переключаясь между ними.

Само переключение — это не бесплатная операция, а последовательность конкретных шагов, которые ядру приходится выполнять каждый раз:

  • Сохранение состояния регистров. У выполняющегося потока есть набор регистров CPU — program counter, stack pointer, регистры общего назначения, флаги. Всё это нужно сохранить в структуре данных потока (в Linux это часть task_struct), иначе поток нельзя будет корректно возобновить.
  • Восстановление состояния следующего потока. Симметричная операция — регистры нового потока загружаются обратно в CPU из сохранённого состояния.
  • Переключение состояния MMU и контекста памяти. Между потоками одного процесса адресное пространство общее, и этот шаг дешевле. Но при переключении между разными процессами меняется таблица страниц (page table) — MMU (Memory Management Unit) начинает работать с другим отображением виртуальных адресов в физические.
  • Сброс TLB-кеша (или его частичная инвалидация). TLB (Translation Lookaside Buffer) хранит недавно использованные пары «виртуальный адрес → физический адрес», чтобы не лазить в таблицу страниц на каждое обращение. При смене адресного пространства старые записи становятся невалидными. Ядро либо сбрасывает TLB целиком, либо (на CPU с поддержкой PCID/ASID) сбрасывает выборочно — дешевле, но всё равно не бесплатно.
  • Планирование. Scheduler решает, какой поток запускать следующим — проходит по очередям готовности, учитывает приоритеты и время ожидания. Это тоже расходует CPU-время.

Отдельно стоит syscall — переключение между user space и kernel space (например, read() или write()). Формально это смена режима работы CPU, а не процесса, но механизм похож: сохранить контекст, переключиться, вернуться обратно. Поэтому частые syscall тоже нагружают CPU сильнее, чем кажется по загрузке в процентах.

Почему это не бесплатно даже на быстром CPU

Ключевая проблема в том, что после восстановления регистров CPU ещё не работает на полной скорости. Современные процессоры активно используют кеши L1/L2/L3 — при исполнении кода они постепенно «прогреваются»: в кеш попадают нужные данные и инструкции текущего потока. При переключении на другой поток его данных в кеше, скорее всего, нет — предыдущий поток их вытеснил. Начинается период холодного кеша (cache cold start), когда CPU чаще промахивается и вынужден лезть в оперативную память, которая на порядки медленнее кеша. То же самое происходит с TLB: после сброса первые обращения к памяти нового потока идут с промахами, и MMU каждый раз заново проходит по таблице страниц.

Поэтому реальная стоимость одного context switch — это не только сохранение/восстановление регистров (относительно быстрая операция), а ещё и косвенные потери из-за холодного кеша после переключения. Обычно вторая часть больше первой, и именно она делает частые переключения ощутимо дорогими даже на очень быстром CPU: чем быстрее ядро, тем больше полезной работы оно теряет за то же время простоя из-за промахов кеша.

Точные цифры (сколько микросекунд или циклов CPU уходит на конкретное переключение) сильно зависят от архитектуры процессора, наличия PCID/ASID, размера рабочего набора потока и ядра ОС — псевдоточные бенчмарки здесь приводить не буду, на разных серверах цифры будут заметно отличаться. Общий принцип важнее числа: переключение стоит тем дороже, чем больше данных нужно «прогреть» заново в кеше после него.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Откуда берутся частые context switch

Есть несколько типичных источников, из-за которых число переключений контекста в системе резко растёт.

Слишком много потоков или процессов относительно числа ядер. Если на сервере с 4 ядрами крутится приложение, которое создаёт 200 рабочих потоков (неудачно сконфигурированный пул воркеров), планировщику приходится постоянно ротировать их между этими 4 ядрами: каждый поток получает свой квант времени, потом переключение — и по кругу. Классический случай — веб-сервер или приложение, где размер thread pool выставлен «с запасом» без учёта реального числа CPU.

Частые системные вызовы (syscall). Программы, которые постоянно дёргают ядро — читают/пишут маленькими порциями вместо буферизации, злоупотребляют блокирующим I/O — провоцируют частые переходы user space ↔ kernel space. Каждый такой переход недёшев сам по себе, а если поток при этом блокируется в ожидании ответа от ядра, происходит ещё и полноценное переключение на другой поток.

Interrupt storm от сетевой карты. Сетевая карта генерирует аппаратное прерывание (IRQ) на каждый входящий пакет (или пачку пакетов при interrupt coalescing). Обработчик прерывания выполняется с высоким приоритетом и вытесняет текущий поток. При высокой сетевой нагрузке — много мелких пакетов в секунду, флуд, атака или просто активный трафик на VPN-сервере — число прерываний резко растёт, и CPU тратит заметную долю времени на обработку IRQ вместо полезной работы приложений. На серверах, где через один процессор проходит много сетевого трафика (VPN-шлюзы, балансировщики), это одна из частых причин странно высокой загрузки CPU при скромном трафике — похожая тема разобрана в статье про высокую нагрузку на процессор.

Избыточная многопоточность в языках и фреймворках. Некоторые рантаймы (Java с дефолтными настройками thread pool, неправильно сконфигурированный Node.js с кластеризацией) создают больше потоков/воркеров, чем физических ядер, «на всякий случай» — это усугубляет первую проблему из списка.

Виртуализация и oversubscription на хосте. На VPS отдельная тонкость: если гипервизор выделяет больше виртуальных vCPU суммарно по всем гостям, чем физических ядер на хосте, переключение контекста происходит на двух уровнях — внутри гостевой ОС и на уровне гипервизора между виртуальными машинами. Поэтому на выделенном сервере при прочих равных переключений меньше и они предсказуемее, чем на переподписанном VPS.

Как посмотреть число переключений: vmstat

Самый быстрый способ увидеть общую картину по системе — vmstat:

vmstat 1

Вывод обновляется каждую секунду, интересующая нас колонка — cs (context switches) в секцию system:

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in    cs us sy id wa st
 2  0      0 512340  81200 923410    0    0     2     8  120   850 12  3 84  1  0

Здесь in — число прерываний в секунду, cs — число переключений контекста в секунду. Смотреть нужно не на абсолютное число (оно естественно растёт с числом ядер и активности системы), а на динамику: если cs резко подскочил одновременно с ростом sy (system time) и падением us (user time), это сигнал, что система тратит непропорционально много ресурсов на переключения и обслуживание ядра, а не на прикладной код.

Точного «нормального» порога cs для всех случаев не существует — он зависит от приложения, профиля нагрузки и числа ядер. Поэтому ориентируйтесь на изменение показателя во времени относительно вашей же обычной нагрузки, а не на абсолютную цифру из чужой статьи.

Как посмотреть, кто именно переключается: pidstat -w

vmstat показывает общую картину по системе, но не говорит, какой именно процесс генерирует переключения. Для этого нужен pidstat из пакета sysstat:

# Debian/Ubuntu
sudo apt install sysstat

# AlmaLinux/RHEL
sudo dnf install sysstat

Дальше смотрим переключения контекста по процессам:

pidstat -w 1

Вывод выглядит примерно так:

Linux 6.1.0 (server)   09/01/2026   _x86_64_  (4 CPU)

12:00:01      UID       PID   cswch/s nvcswch/s  Command
12:00:02        0       842      45.00      12.00  nginx
12:00:02        0      1210     310.00     180.00  node
12:00:02      1000      3301      2.00       0.00  sshd

Две ключевые колонки:

  • cswch/s — добровольные переключения (voluntary context switches) в секунду. Поток сам отдаёт CPU, потому что ждёт чего-то — обычно ввода-вывода, блокировки мьютекса, ответа от сети или диска. Высокое значение здесь часто говорит о том, что процесс много и часто ждёт I/O, а не о проблеме с CPU как таковым.
  • nvcswch/s — принудительные переключения (non-voluntary context switches). Поток был готов работать дальше, но планировщик вытеснил его — например, у него закончился квант времени или подоспел поток с более высоким приоритетом. Высокое значение здесь — куда более тревожный сигнал: он означает, что процессу не хватает CPU-времени и он конкурирует за ядра с другими потоками/процессами.

Если нужно посмотреть по конкретному процессу или найти самый «шумный» PID, удобно сузить вывод:

pidstat -w -p ALL 1 5

и отсортировать по колонке nvcswch/s — процессы с наибольшим числом принудительных переключений и есть основные кандидаты на разбор: возможно, у них слишком много потоков, неправильно настроен affinity, или они конкурируют с чем-то ещё на этом же ядре. Заодно стоит проверить, не крутится ли на сервере что-то лишнее — этому посвящена статья про то, как узнать, кто нагружает сервер.

Когда это реальная проблема производительности

Не любое количество context switch — проблема: переключения происходят постоянно, это нормальная часть работы планировщика. Стоит начинать разбираться, когда выполняется несколько условий одновременно:

  • Растёт sy (system time), а не us (user time). CPU занят, но именно ядром, а не вашим приложением.
  • cs/in растут непропорционально нагрузке. Например, число запросов выросло на 20%, а число переключений — в 3 раза. Это обычно указывает на архитектурную проблему (слишком много потоков на запрос, неэффективный I/O), а не на естественный рост.
  • nvcswch/s высок у конкретного процесса, при этом ему явно не хватает CPU — задержки растут, хотя суммарная загрузка CPU по top не выглядит критичной.
  • Задержки растут при стабильной загрузке CPU в процентах. Признак, что CPU тратит время на переключения и прерывания, а не только на вычисления — «полезная» загрузка ниже, чем показывает общий процент.

Если ни одного из этих признаков нет, а cs просто «выглядит большим» в абсолютных цифрах — поводов для беспокойства обычно нет. У активного веб-сервера с десятками воркеров счётчик переключений в принципе будет высоким, и это нормально.

Если причина — избыточное число потоков относительно ядер, вариантов решения два: уменьшить thread pool до значения, близкого к числу ядер, либо увеличить число ядер под нагрузку — на VPS апгрейд тарифа снимает конкуренцию за CPU без изменения кода. Для interrupt storm может помочь распределение обработки прерываний по нескольким ядрам (RSS/RPS) — отдельная тема за рамками этой статьи.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Context switch — это всегда плохо?

Нет, это нормальная часть работы многозадачной ОС. Проблема не в факте переключений, а в их избыточном количестве относительно полезной работы.

Почему на многоядерном сервере переключений меньше не становится?

Потому что число потоков обычно растёт вместе с числом ядер — рантаймы часто настраивают пул воркеров по формуле «число ядер × N». Если N выбрано с запасом, переключений может быть даже больше, чем на менее мощном, но аккуратно настроенном сервере.

Разница между cswch/s и nvcswch/s — что важнее?

Для диагностики нехватки CPU важнее nvcswch/s: она напрямую говорит, что процессу не хватает времени CPU. Высокий cswch/s чаще указывает на ожидание I/O — тоже стоит внимания, но природа проблемы другая.

Можно ли полностью избавиться от context switch?

Нет и не нужно — это фундаментальный механизм многозадачности. Речь об уменьшении избыточных переключений: разумный thread pool, эффективный I/O, достаточное число ядер под реальную нагрузку.

Помогает ли быстрый диск уменьшить число context switch?

Косвенно да, если узкое место было в ожидании диска (I/O wait). Но если причина в другом (слишком много потоков, interrupt storm), апгрейд диска не поможет — стоит сначала проверить, не упирается ли сервер именно в диск.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →