MAATRIX / Блог / Как виртуализация крадёт производительность: где именно теряются проценты

Как виртуализация крадёт производительность: где именно теряются проценты

MAATRIX

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

Два способа показать гостю железо: эмуляция и паравиртуализация

У гипервизора (KVM, Xen, Hyper-V, VMware) есть выбор: либо честно эмулировать реальное устройство, которое гостевая ОС «знает» из коробки, либо договориться с гостем через специальный интерфейс, заточенный под виртуализацию.

Эмуляция устройств. Классический пример — виртуальная сетевая карта Intel e1000 или диск, эмулирующий IDE-контроллер. Гостевая ОС видит знакомое железо и работает с ним через обычный драйвер, ничего не подозревая о виртуализации. Плюс — совместимость: такой диск или сетевую карту подхватит даже древняя ОС без дополнительных драйверов. Минус — цена. Каждая операция ввода-вывода (запись в регистр контроллера, чтение статуса) — это отдельная привилегированная инструкция, которая гостевая ОС не может выполнить напрямую и которую надо перехватывать и обрабатывать в гипервизоре. Для диска это означает лишний слой трансляции протокола на каждую операцию, для сети — лишнюю обработку каждого пакета.

Паравиртуализация через virtio. Это специально спроектированный интерфейс, который с самого начала знает, что работает поверх гипервизора. Вместо эмуляции регистров настоящего чипа гость и хост общаются через общую память (кольцевые буферы, virtqueue) и явные уведомления друг другу. Меньше переключений между гостем и хостом на единицу переданных данных, меньше накладных расходов на пакет или на операцию ввода-вывода. Практический вывод простой: если у вас Linux-гость и современный гипервизор — используйте virtio-net и virtio-blk (или virtio-scsi) вместо эмулируемых устройств. Это не экзотика, а стандарт для любого нормально настроенного KVM уже больше десяти лет.

Разница между эмулируемым и virtio-устройством не в разы теряется на голом железе — по сути, эмуляция всегда будет менее пропускной, потому что каждая транзакция должна пройти через дополнительный код гипервизора, интерпретирующий поведение виртуального чипа. Насколько именно это заметно на конкретной нагрузке — зависит от паттерна доступа (много мелких операций бьёт больнее, чем редкие большие), поэтому конкретные проценты приводить не буду — это надо мерить на своей нагрузке.

VM exit: момент, когда гость отдаёт управление хосту

Центральное понятие для понимания накладных расходов виртуализации — VM exit. Это принудительный выход из режима выполнения гостевой машины обратно в гипервизор, который происходит, когда гость пытается сделать что-то, что напрямую делать нельзя: обратиться к привилегированному регистру, выполнить операцию ввода-вывода, изменить настройки прерываний, выполнить некоторые системные инструкции.

Механика такая: аппаратная виртуализация (Intel VT-x, AMD-V) добавила процессору режим гостя (guest mode) внутри общей модели выполнения. Пока гостевой код не пытается сделать что-то «опасное» для системы в целом, он выполняется на реальном железе с реальной скоростью — никакой эмуляции инструкций нет. Но при определённых событиях процессор аппаратно переключается в режим хоста, сохраняет состояние гостя, передаёт управление гипервизору, тот разбирается, что произошло, эмулирует нужное поведение (например, записывает значение в виртуальный регистр устройства) и возвращает управление гостю.

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

Практическое следствие: если вы видите, что нагрузка чисто вычислительная (например, компиляция кода, обработка данных в памяти без интенсивного диска и сети), разница между VPS и выделенным сервером на том же процессоре обычно минимальна. Если нагрузка — это тысячи мелких пакетов в секунду или интенсивная работа с диском (СУБД под нагрузкой, очереди сообщений), количество VM exit растёт, и здесь конфигурация гипервизора и выбор драйверов начинают иметь значение.

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

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

Арендовать сервер

Вложенная трансляция адресов и TLB-промахи

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

В обычной, невиртуализированной системе процессор переводит виртуальный адрес процесса в физический адрес через таблицы страниц ОС, и результат этого перевода кэшируется в TLB (Translation Lookaside Buffer) — маленьком, но очень быстром кэше внутри процессора. Промах в TLB стоит дорого: надо пройти по таблицам страниц в памяти (это называется page table walk), а память в разы медленнее кэша.

В виртуализированной системе появляется дополнительный уровень: гостевая ОС переводит гостевой виртуальный адрес в гостевой «физический» адрес (который на самом деле не физический, а тоже виртуальный с точки зрения хоста), а затем этот гостевой физический адрес нужно перевести в реальный физический адрес хоста. Это называется nested paging (Intel EPT — Extended Page Tables, AMD NPT — Nested Page Tables). Без аппаратной поддержки это означало бы, что при каждом промахе TLB надо пройти по двум наборам таблиц страниц вложенно друг в друга — операция резко дороже обычного page table walk.

Современные процессоры умеют кэшировать промежуточные результаты этого вложенного перевода, и сам механизм EPT/NPT — это как раз то, что аппаратно ускоряет второй уровень трансляции, вместо того чтобы гипервизору эмулировать его программно. Тем не менее, TLB на виртуализированной машине в среднем "холоднее", чем на голом железе, особенно сразу после переключения контекста или после миграции виртуальной машины между физическими ядрами — процессы с большим количеством случайных обращений к памяти (базы данных с большими индексами, интерпретаторы с активной сборкой мусора) чувствуют это заметнее, чем последовательный проход по массиву данных, который укладывается в несколько больших страниц (huge pages) и почти не создаёт промахов TLB.

Отсюда практический совет: если у вас память-интенсивная нагрузка на VPS и вы упираетесь в производительность, посмотрите в сторону huge pages (2 МБ или 1 ГБ страницы вместо стандартных 4 КБ) — они снижают количество записей, которые вообще нужно транслировать, а значит и число промахов вложенной трансляции.

Почему 2026-й — это не 2006-й

Стоит явно проговорить контекст: разговоры про «виртуализация съедает 30-40% производительности» родом из эпохи программной виртуализации (VMware до появления аппаратной поддержки, ранние версии Xen в paravirtualization-режиме), когда гипервизору приходилось либо полностью эмулировать каждую инструкцию процессора программно, либо модифицировать ядро гостевой ОС, чтобы оно само не пыталось выполнять привилегированные операции.

Intel VT-x появился в 2005 году, AMD-V — тогда же под именем AMD-V (изначально Pacifica). С тех пор аппаратная поддержка виртуализации проделала долгий путь: второе поколение расширений (EPT/NPT для памяти, о которых шла речь выше) убрало необходимость программно эмулировать трансляцию адресов, поддержка виртуализации прерываний (APICv/AVIC) сократила количество VM exit при работе с сетевыми картами и таймерами, а IOMMU (Intel VT-d, AMD-Vi) сделала возможным честный проброс физических устройств гостю в обход гипервизора вообще.

Итог: современный KVM или Hyper-V на актуальном процессоре — это совсем другая история, чем гипервизоры двадцатилетней давности. Основная часть кода гостя выполняется на реальном железе без какой-либо эмуляции, и накладные расходы сосредоточены именно в тех точках, которые мы разобрали выше — VM exit при обращении к устройствам и вложенная трансляция адресов при промахах TLB. Это не значит, что накладных расходов нет вообще — значит, что они локализованы и предсказуемы, а не размазаны по каждой инструкции.

Проброс железа: когда виртуализации почти нет

Если задача требует минимальных накладных расходов на конкретное устройство — есть механизм PCI passthrough (проброс устройства), при котором физическое устройство (сетевая карта, GPU, NVMe-накопитель) отдаётся гостевой машине напрямую, в обход эмуляции и даже в обход virtio. IOMMU здесь отвечает за то, чтобы гость мог безопасно работать с физическими адресами устройства напрямую, а гипервизор в штатной работе устройства почти не участвует.

Это радикально устраняет накладные расходы виртуализации для конкретного устройства — но ценой гибкости: устройство становится привязанным к одной виртуальной машине, его нельзя разделить между несколькими гостями (кроме SR-IOV — технологии, которая позволяет одной физической сетевой карте представить несколько виртуальных функций, каждую из которых можно пробросить отдельному гостю). Для большинства задач на аренде сервера это избыточно — обычный VPS с virtio-драйверами справляется достаточно хорошо, а проброс железа оправдан там, где нужна предсказуемая задержка на уровне устройства: GPU для вычислений, сетевые карты для сетевых лабораторий с высокой пропускной способностью.

Если вам принципиально важно не иметь вообще никакого слоя виртуализации над железом — логичный выбор выделенный сервер вместо VPS, а не попытки выжать максимум из проброса устройств на арендованной виртуальной машине.

Как посмотреть на потери своими глазами

Теория теорией, но на конкретном сервере вопрос звучит практически: а моя VM сейчас страдает от виртуализации или нет? Несколько вещей, на которые стоит смотреть.

Steal time. Это метрика, которая отвечает на вопрос «сколько времени моя виртуальная машина хотела выполняться, но физическое ядро в этот момент отдали другому гостю». Она не про накладные расходы виртуализации как таковой, а про конкуренцию за физический процессор между несколькими VM на одном хосте — но именно её чаще всего путают с «тормозами от виртуализации». Посмотреть можно через top (колонка %st) или через vmstat 1:

$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 1  0      0 512340  81200 623400    0    0     2     8   45   72  3  1 96  0  0

Последняя колонка — st (steal). Если она стабильно больше нескольких процентов под вашей типичной нагрузкой — это сигнал, что хост перепродан (overcommit) по CPU сильнее, чем вам нужно, и стоит смотреть на более щедрый тариф или на выделенный сервер, а не разбираться с настройками драйверов.

Тип сетевого и дискового устройства. Внутри гостя это можно проверить командой lspci (если у вас есть доступ к гостевой ОС на уровне ядра, что обычно так и есть на VPS):

$ lspci | grep -iE 'ethernet|scsi|storage'
00:03.0 Ethernet controller: Red Hat, Inc. Virtio network device
00:05.0 SCSI storage controller: Red Hat, Inc. Virtio SCSI

Если вместо Virtio вы видите что-то вроде Intel Corporation 82540EM Gigabit Ethernet Controller (emulated) — это признак, что виртуальная машина использует эмулируемое устройство, а не паравиртуализированное. На арендованном VPS вы обычно не управляете этим напрямую (это настройка на стороне хостинга), но при заказе выделенного сервера с собственной виртуализацией — это первое, что стоит проверить и поправить в конфигурации libvirt/QEMU.

Huge pages. Проверить, включены ли huge pages и используются ли они, можно так:

$ cat /proc/meminfo | grep -i huge
AnonHugePages:         2048 kB
HugePages_Total:          0
HugePages_Free:           0
Hugepagesize:           2048 kB

AnonHugePages показывает прозрачные huge pages (THP), которые ядро Linux может выделять автоматически без явной настройки, — если там ненулевое значение, часть вашей памяти уже использует укрупнённые страницы. Если вы гоняете память-интенсивную нагрузку (СУБД, in-memory кэш) и хотите снизить накладные расходы на трансляцию адресов, изучите явную настройку huge pages для конкретного процесса — многие СУБД (например, PostgreSQL) поддерживают это через параметры конфигурации.

Что из этого следует на практике

Если сравнивать KVM с OpenVZ или другими типами виртуализации — стоит помнить, что источники накладных расходов разные: KVM — это полная аппаратная виртуализация с изоляцией на уровне ядра, а OpenVZ и подобные контейнерные технологии делят одно ядро между гостями и вообще не проходят через VM exit и вложенную трансляцию адресов — зато жертвуют изоляцией. Каждый подход — это компромисс, и выбор между VPS и выделенным сервером тоже, по сути, выбор между «немного накладных расходов ради гибкости» и «максимум предсказуемости ценой аренды целого физического сервера».

Для подавляющего большинства задач — веб-приложения, API, средние базы данных, очереди сообщений — накладные расходы современной аппаратной виртуализации на хорошо настроенном хосте (virtio-драйверы, актуальный процессор с поддержкой EPT/NPT, отсутствие переподписки по CPU) настолько малы, что тратить время на проброс железа или переезд на выделенный сервер не имеет смысла. Если же вы упираетесь именно в дисковый или сетевой ввод-вывод под высокой нагрузкой — сначала проверьте steal time и тип драйверов, и только потом делайте вывод, что дело в самой виртуализации, а не в переподписанном хосте или неоптимальной конфигурации.

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

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

Арендовать сервер

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

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

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

Как узнать, использует ли мой VPS virtio или эмуляцию устройств?

Выполните lspci внутри гостевой ОС и посмотрите на модель сетевой карты и дискового контроллера — Virtio в названии означает паравиртуализацию, конкретная модель чипа (например, e1000 или LSI SAS) — эмуляцию.

Steal time в 2-3% — это повод для беспокойства?

Обычно нет, небольшие пики steal time под кратковременной нагрузкой — это нормальная конкуренция за ресурсы на разделяемом хосте. Тревожный сигнал — стабильно высокий steal time (стабильно двузначные значения) именно в моменты, когда вашей VM нужен процессор.

Даёт ли выделенный сервер нулевые накладные расходы по сравнению с VPS?

Да, потому что там вообще нет гипервизора между вашей ОС и железом (если только вы сами не поднимаете виртуализацию поверх него). Но это не значит автоматически «быстрее для любой задачи» — если нагрузка не упирается в VM exit или TLB-промахи, разница может быть небольшой.

Стоит ли гнаться за huge pages на обычном веб-сервере?

Обычно нет смысла — эффект заметен на память-интенсивных нагрузках с большим количеством случайных обращений (СУБД, in-memory хранилища), а для типичного веб-приложения с умеренным потреблением памяти прирост, скорее всего, будет в пределах погрешности измерения.

Влияет ли количество виртуальных ядер (vCPU) на накладные расходы виртуализации?

Косвенно да: чем больше vCPU у гостя и чем больше гостей на одном физическом хосте, тем выше вероятность конкуренции за реальные ядра и, соответственно, steal time — это отдельный от VM exit и TLB источник потерь, но тоже реальный.

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

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

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