...

Переключение контекста сервера и перегрузка процессора: Знать все

Процессор Context Switching решает, насколько эффективно серверные ядра переключаются между потоками и процессами, минимизируя задержки и Накладные генерировать. Я показываю, где именно возникают затраты, какие измеряемые величины имеют значение и как я снижаю накладные расходы на переключение в продуктивных средах.

Центральные пункты

  • Прямые затратыСохранение/загрузка регистров, TLB и изменение стека
  • Косвенные расходыПропуски кэша, миграция ядра, время работы планировщика
  • Пороговые значения>5 000 переключений/ядро/с в качестве предупреждающего сигнала
  • ОптимизацииСродство к процессору, асинхронный ввод-вывод, больше ядер
  • Мониторингvmstat, sar, perf для получения четких результатов.

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

Переключение контекста сохраняет текущее состояние потока или процесса и загружает следующий контекст выполнения, чтобы несколько рабочих нагрузок могли совместно использовать ядро при мультиплексировании по времени [7]. Этот механизм дает преимущества, но создает чистую нагрузку во время переключения. Накладные, потому что никакие приложения не работают [1]. Я имею в виду такие регистры, как IP, BP, SP и каталог страниц (CR3), которые система должна сохранять и восстанавливать в случае изменений [2]. Технически это кажется незаметным, но на практике это сильно определяет время отклика, особенно при большом количестве одновременных запросов. Тот, кто масштабирует серверы, должен следить за скоростью изменений, иначе работа по контролю будет заметно съедать ресурсы процессора.

Прямые накладные расходы в деталях

Прямые затраты возникают при сохранении и восстановлении аппаратного контекста, то есть стека ядра, таблиц страниц и регистров процессора [2]. На x86_64 переключение потока в одном и том же процессе часто занимает 0,3-1,0 микросекунды, а переключение процесса с другим адресным пространством - 1-5 микросекунд [1]. Если поток также переключается на другое ядро, влияние кэша добавляет 5-15 микросекунд, поскольку новое ядро сначала загружает свои данные обратно в кэш [1]. Это время кажется небольшим, но при тысячах переключений в секунду оно быстро увеличивается до ощутимых значений. Сервер-потери. Я учитываю это при планировании бюджета на задержку и устанавливаю жесткие ограничения для сервисов с жесткими требованиями к отклику.

Косвенные накладные расходы и кэши

Косвенные затраты часто доминируют, особенно когда рабочие нагрузки выполняются параллельно и мигрируют [1]. Если поток перемещается между ядрами, он теряет свои теплые данные L1/L2, что может стоить 50-200 наносекунд на доступ [1]. Промывка TLB при смене адресного пространства также приводит к задержкам конвейера, что снижает пропускную способность [3]. Кроме того, сама работа планировщика требует затрат времени, что означает несколько процентов расхода процессора при очень высоких частотах коммутаторов [1][3]. Я предотвращаю это Thrashing, путем установления взаимосвязей, минимизации изменений в ядре и выявления узких мест на ранних этапах.

Распознавание пороговых значений и их правильное считывание

Я анализирую vmstat и sar и смотрю на частоту переключений по ядру, а не только глобально [2]. Значения около 5 000 переключений на ядро и секунду определяют для меня четкий диапазон предупреждений, в котором я ищу конкретные причины [2]. При значениях свыше 14 000 на процессор и секунду я ожидаю значительных падений, например, в серверах баз данных или веб-серверах с высоким параллелизмом [6]. На виртуальных машинах я также ожидаю изменений в гипервизоре, которые могут привести к снижению чистых показателей гостевой системы [2]. Одно значение никогда не объясняет всего, поэтому я комбинирую Тариф, Задержка и использование в целостную картину.

Планировщик, прерывание и прерывания

Современный планировщик, такой как CFS, справедливо распределяет ядра и решает, когда вытеснить работающие потоки [4]. Слишком агрессивное вытеснение увеличивает усилия по переключению, слишком сдержанное вытеснение теряет время отклика для важных задач [3]. Я проверяю, отнимает ли нагрузка на прерывания время ядра, поскольку занятые прерывания приводят к дополнительным переключениям ядра. Для введения в тему рекомендую статью Обработка прерываний, потому что он очень четко объясняет влияние на задержку. Моей целью остается бережливое Преемственность-политика, защищающая жесткие пути и объединяющая вспомогательную работу.

Временные срезы, гранулярность и пробуждение

Длина временных срезов и гранулярность пробуждений напрямую определяют, как часто планировщик становится активным. Слишком маленькие временные срезы приводят к частым вытеснениям и, следовательно, к большему количеству переключений; слишком большие временные срезы увеличивают время отклика интерактивных или чувствительных к задержкам путей. Я обращаю внимание на эффективное min_granularity и wakeup_granularity планировщика, поскольку они определяют, когда бодрствующий поток может вытеснить работающий. В рабочих нагрузках с большим количеством кратковременных задач я предпочитаю немного более высокую толерантность к пробуждению, чтобы эвристика не поощряла „пробуждения“, которые в конечном итоге приводят только к трэшу. В очень критичных к задержкам системах стоит использовать „бестоковую“ работу, чтобы тиканье таймера не вызывало ненужных вытеснений. Это по-прежнему важно: Я оцениваю каждое изменение по сквозным задержкам, а не только по чистой скорости переключения.

Виртуализация, гиперпоточность и эффекты NUMA

При виртуализации гипервизор добавляет дополнительные уровни, которые также выполняют контекстные переключения [2]. Это смещает измеренные значения, и кажущаяся умеренной скорость в госте может быть выше на хосте. Гиперпоточность устраняет пробелы в конвейере ожидания, но не устраняет накладные расходы на переключение; неправильная расстановка потоков даже ухудшает ситуацию с кэшем [4]. В системах NUMA я также обращаю внимание на локальные доступы к памяти, поскольку удаленные доступы увеличивают задержки. Я планирую NUMA-зоны и протестировать поведение в условиях реальной производственной нагрузки.

Контейнеры, квоты на процессор и печать планировщика

В контейнерах я устанавливаю доли и квоты процессора так, чтобы контроллер пропускной способности CFS не дросселировал каждую миллисекунду. Если cgroup регулярно „рассинхронизируется“, это приводит к коротким запускам, частому преэмпшену и большему количеству переключений контекста - и в то же время к худшей чистой работе. Я планирую количество процессоров на контейнер консервативно, предпочитая использовать больше Акции как жесткие квоты и проверяю, укладываются ли пики „всплесков“ в свободную емкость хоста. На хостах с большим количеством небольших контейнеров я распределяю службы по узлам NUMA и объединяю связанные рабочие нагрузки в cgroup, чтобы планировщику приходилось меньше мигрировать. Если я вижу сильные различия между процессами в pidstat -w и sar, я специально увеличиваю сродство для cgroup и рассматриваю изолированные ядра для путей задержки.

Реализуйте напрямую: Снизить скорость переключения

Я начинаю с масштабирования ресурсов: больше ядер процессора и достаточно оперативной памяти снижают скорость переключения, поскольку больше работы выполняется параллельно [4]. Затем я использую привязку к процессору, чтобы держать потоки на фиксированных ядрах и использовать тепло кэша [4]. По возможности я использую асинхронный ввод-вывод, чтобы предотвратить блокировку процессов в ожидании и вызвать ненужные переключения [4]. В отношении путей задержки я отдаю предпочтение легким потокам пользовательского уровня, которые переключаются быстрее, чем чистые потоки ядра [4]. Этот прагматичный Последовательность быстро приносит ощутимый прогресс на практике.

Правильное использование сродства процессора и NUMA

С помощью привязки к процессору я привязываю сервисы к фиксированным ядрам и таким образом сохраняю рабочие наборы в кэше, что уменьшает межъядерные миграции [4]. В Linux я использую taskset или sched_setaffinity и включаю привязку к IRQ. В системах NUMA я распределяю сервисы по узлам и обеспечиваю локальное выделение памяти. За практическими подробностями обращайтесь к моему руководству по Сродство к процессору в хостинге, в котором компактно описаны все шаги. Очистить Булавки часто экономит несколько процентов процессора и значительно сглаживает пики задержки [1].

TLB, огромные страницы и последовательности KPTI

Изменения адресного пространства и очистка TLB являются ключевыми факторами, влияющими на косвенные накладные расходы. При необходимости я использую страницы большего размера (огромные страницы), чтобы снизить нагрузку на TLB и сделать срабатывания менее частыми. Это особенно эффективно для баз данных in-memory и кэшей с большими кучами. Миграции безопасности, такие как KPTI, исторически увеличивают стоимость переходов между пользователем и ядром; современные процессоры с PCID/ASID смягчают это, но большая доля системных вызовов остается видимой. Мое противоядие: объединение системных вызовов (batching), меньше мелких записей, меньше переключений контекста между пользовательской частью и ядром, а также асинхронный ввод/вывод в критических точках. Цель не в том, чтобы избежать каждого флеша, а в том, чтобы уменьшить их частоту, чтобы кэши могли работать.

Модели потоков: управляемые событиями и потоки по запросам

Архитектурная модель напрямую влияет на скорость переключения, поэтому я сознательно выбираю между событийным циклом и циклом "поток-запрос". Событийный цикл с асинхронным вводом/выводом генерирует меньше блокировок и, следовательно, меньше переключений при той же нагрузке. Классическая многопоточная обработка запросов предлагает простоту, но при высоком параллелизме производит массу контекстных переключений. Событийная модель обычно оправдывает себя для веб-серверов и прокси-серверов с большим числом одновременных соединений. Для более детального сравнения см. Модели с резьбой сфокусированный обзор с практическими соображениями; эти Выбор часто определяет кривую задержки.

Сохранение блокировки и время работы вне процессора

Помимо реальных изменений в процессоре, я наблюдаю Вне ЦПУ-время: Ожидание блокировок, ввода-вывода или доступа к планировщику. Высокие доли вне ЦП часто означают, что потоки „припаркованы“ из-за удержания блокировок, и планировщику постоянно приходится запускать новых кандидатов - генератор бесполезных переключений. Я измеряю это с помощью событий perf и точек отслеживания планировщика (sched_switch), чтобы понять, вызваны ли переключения предварительным вытеснением, блокировкой или миграцией. В приложениях я уменьшаю гранулярность критических секций, заменяю глобальные блокировки шардингом и использую безблокировочные структуры, где это необходимо. Это уменьшает поток пробуждения, и планировщик позволяет потокам дольше работать на ядре.

Руководство по мониторингу для получения четких выводов

Я начинаю с vmstat и sar, чтобы увидеть скорость переключения и использование с течением времени [2]. Затем я использую perf stat, чтобы проверить, куда уходит процессорное время и высок ли уровень ошибок предсказания ветвлений или событий TLB [4]. Netdata или аналогичные инструменты визуализируют значения по процессам и ядрам, что сводит к минимуму "слепые зоны" [4]. Важно проводить измерения во время реальных пиковых нагрузок, а не только во время простоя. Только в этих случаях Профили показать, изменяется ли планировщик из-за того, что я блокирую, перемещаю или создаю слишком много потоков.

Практический контрольный список: команды быстрого измерения

  • vmstat 1: процессы r/b, cs/s и контекст меняют тенденции каждую секунду
  • mpstat -P ALL 1: Использование и нагрузка прерываний на ядро
  • pidstat -w 1: добровольные/недобровольные переключения на процесс
  • perf stat -e context-switches,cpu-migrations,task-clock: сделать видимыми драйверы жестких затрат
  • perf sched timehist: отслеживание времени ожидания в очередях выполнения и поведения при пробуждении.
  • trace-cmd/perf record -e sched:sched_switch: Уточнение происхождения переключателей с помощью трассировки

Пороговые значения в виртуальных средах

На виртуальных машинах я с осторожностью читаю показатели переключения, поскольку планировщики хоста и совместное планирование вносят дополнительные переключения [2]. Я слежу за тем, чтобы количество vCPU и физических ядер совпадало, чтобы не было конкуренции за временные интервалы. Время кражи процессора дает мне представление о том, насколько сильно хост прерывает работу моих виртуальных процессоров. Если я вижу высокую скорость переключения и высокое время кражи одновременно, я отдаю предпочтение экземпляру с более выделенными ядрами. Таким образом я обеспечиваю Последовательность даже если гипервизор обслуживает множество гостевых систем параллельно.

Таблица ключевых показателей и быстрые победы

Я использую приведенный ниже обзор в качестве шпаргалки при заметном снижении накладных расходов на переключение и определении приоритетов конкретных шагов. Он охватывает сродство, масштабирование, облегчение потоков, планирование и асинхронный ввод-вывод, каждый из которых имеет ощутимые преимущества. Я расставляю приоритеты по этим пунктам и измеряю их до и после изменений, чтобы наглядно продемонстрировать успех. Небольшие вмешательства часто дают сильный эффект, например, если я только перераспределяю IRQ или ввожу epoll. Эти компактные Действия уменьшение пиков задержки и ощутимое увеличение чистой пропускной способности.

Мера оптимизации Преимущество Пример
Сродство к процессору Уменьшение количества пропусков кэша набор задач в Linux
Больше ядер Меньше переключателей Масштабирование до 16+ ядер
Светлые нити Ускоренная переналадка Потоки на уровне пользователя
Планировщик CFS Справедливое распределение Стандарт Linux
Асинхронный ввод/вывод Избегайте переключения ожидания epoll в Linux

Целевые показатели производительности и бюджеты на задержку

Я формулирую четкие цели: Сколько процентов процессора может стоить изменение и какая задержка остается для приложения. В хорошо настроенных системах я снижаю накладные расходы с нескольких процентов до менее чем одного процента, в зависимости от профиля [1]. Критические пути, такие как аутентификация, кэширование или структуры данных в памяти, приоритетны для сродства и асинхронного ввода-вывода. Я откладываю пакетную работу на спокойные фазы, чтобы сохранить пиковое время. Чистый Бюджет облегчает принятие решений, когда параметры планировщика должны быть взвешены между собой [3].

Сетевой ввод/вывод, IRQ и коалесценция

Сетевые маршруты часто генерируют изменения, а приложение этого не замечает: NAPI, SoftIRQs и ksoftirqd принимают на себя пики нагрузки, из-за которых планировщик оказывается дополнительно занят. Я проверяю, активна ли RSS (множественные очереди приема), и устанавливаю сродство IRQ так, чтобы сетевые прерывания были направлены на те же ядра, что и рабочие нагрузки, обрабатывающие пакеты. RPS/RFS помогают направить путь данных в локальные кэши, а не постоянно перепрыгивать через сокет. При умеренном объединении прерываний я сглаживаю поток пробуждений, не нарушая бюджеты на задержки. Эффект заметен сразу: меньше коротких „пробуждений“ процессора, больше продуктивных временных отрезков для каждого потока.

Контроль задержки хвоста и обратного давления

Высокая частота переключения контекста сильно коррелирует с дисперсией времени отклика. Поэтому я оптимизирую не только медиану, но и значения P95/P99: более короткие критические секции, чистые стратегии обратного давления (например, ограниченные очереди и отбрасываемые некритические запросы) и микропакет для путей с интенсивным вводом-выводом. Я намеренно сохраняю пулы потоков небольшими и эластичными, чтобы они не „засоряли“ планировщик тысячами ожидающих задач. Особенно в случае штормов соединений (например, волн переподключения) я дросселирую на границе, а не сворачиваю в ядре приложения - это уменьшает переключения, стабилизирует очереди и защищает бюджеты задержек в долгосрочной перспективе.

Избегайте критических антипаттернов

Я избегаю чрезмерного количества потоков, потому что это только стимулирует переключение и не приводит к автоматическому увеличению истинного параллелизма. Занятые циклы ожидания без резервного копирования сжигают процессор, заставляя планировщик часто упреждать. Частые миграции ядра без причины указывают на отсутствие сродства или на то, что IRQ подключены не к тому месту. Блокировка ввода-вывода на путях запросов создает постоянные переключения и увеличивает разброс времени отклика. Такие Образец Я распознаю их на ранней стадии и последовательно устраняю до того, как они попадут в полезную нагрузку.

Краткое резюме

Контекстное переключение процессора - один из самых больших скрытых факторов затрат в сильно загруженных серверах. Сначала я измеряю скорость переключения на ядро, классифицирую задержки и время кражи и нажимаю на тормоза при >5 000 переключений/ядро/с [2]. Затем я устанавливаю сродство, асинхронный ввод-вывод и, при необходимости, увеличиваю количество ядер, чтобы свести воедино прямые и косвенные эффекты [4]. Я оцениваю настройки планировщика, нагрузку на прерывания и виртуализацию в контексте, чтобы ни один из уровней не доминировал над другим [1][2][3]. При такой фокусировке Процедура Я снижаю накладные расходы менее чем на один процент и поддерживаю стабильное время отклика даже при высокой нагрузке.

Текущие статьи

Серверный процессор с визуализацией контекстного переключения
Серверы и виртуальные машины

Переключение контекста сервера и перегрузка процессора: Знать все

Контекстное переключение процессора приводит к перегрузке сервера - узнайте о причинах, стоимости и настройке производительности для достижения максимальной эффективности.

HTTP/2 Server Push в современном центре обработки данных хостинга
Веб-сервер Plesk

HTTP/2 Server Push: сценарии приложений в хостинге для максимальной производительности

Оптимизированный хостинг HTTP/2 server push: откройте для себя сценарии развертывания для предварительной загрузки ресурсов и веб-производительности - более быстрая загрузка с WordPress.

Сервер с функцией контроля длины очереди дисков в центре обработки данных
Серверы и виртуальные машины

Длина очереди дисков: оптимизация производительности сервера

Оптимизируйте длину очереди дисков: Измерьте задержку сервера хранения и выполните анализ ввода-вывода для обеспечения максимальной производительности сервера.