Представьте: две виртуальные машины на одном хосте, настроены одинаково, крутят одну нагрузку. Одна стабильно на 20% медленнее. Не в нагрузке дело, не в хосте, не в соседях. Просто память медленной VM оказалась на другой стороне интерконнекта от процессоров, а внутри гостя нет ни одной ручки, чтоб это исправить.
Вот о чём эта история. Компания Edera выпустила стек изменений, который делает виртуализацию на Xen полностью NUMA-осведомлённой — от гостя через паравиртуальные драйверы до dom0 и гипервизора. Некоторые куски там вообще первые в своём роде.
NUMA — это Non-Uniform Memory Access. Доступ к памяти стоит по-разному: важно, какой CPU обращается и в каком банке лежат данные. Раньше была UMA (Uniform Memory Access) — один контроллер, равные цены для всех. Но когда ядер стало много, шина упёрлась в потолок: длинные дорожки замедляют сигнал, один контроллер не тянет всех, начинаются конфликты. Решение — раздать каждому сокету свой контроллер памяти и соединить их интерконнектом. Локальный доступ быстрый, удалённый — через мост.
В конце 2010-х картинка усложнилась. Например, AMD EPYC с чинлетами: внутри одного сокета сидят несколько кристаллов (CCD), у каждого свой кусок кэша и памяти, общаются через Infinity Fabric. То есть NUMA-узлов может быть несколько даже на одном сокете. У Intel то же самое — Sub-NUMA Clustering (SNC). Четыре-восемь узлов на двухсокетнике сегодня — рутина.
Насколько это медленно? Удалённый доступ в 1.5–3 раза дороже локального на микробенчмарке. На реальной нагрузке разрыв легко уходит в 4–5 раз, потому что все дерутся за общий интерконнект. И главное — производительность становится непредсказуемой: один запрос может занять 80 наносекунд, другой — 250, в зависимости от загрузки моста.
Есть известное решение — интерливинг: разбрасывать страницы по всем узлам по кругу, чтоб было предсказуемо, но «одинаково плохо». Половина обращений всё равно удалённые. Это работает, если софт ничего не знает про NUMA. Но если приложение понимает, куда оно ходит, оно должно иметь доступ к топологии.
И вот тут начинается боль Xen. Обычный Linux даёт процессам и память привязать, и топологию узнать. Но Xen — два стека: гипервизор и гость. KVM проще: там VM — просто процесс в Linux, и вся NUMA-магия хост-ядра работает на неё почти бесплатно. А Xen управляет железом напрямую, и его привилегированный гость dom0 вообще не видит NUMA-топологию. numactl -H внутри dom0 показывает один ровный узел, сколько бы их ни было на хосте.
Гипервизор может разместить память и vCPU гостя идеально. Но гость, доверяя плоской карте, запросто захочет оптимизироваться и сам себе положит данные на удалённый узел — и будет молча ходить через интерконнект. Вот она, головная боль операторов. Edera чинит это тем, что даёт гостю настоящую vNUMA-топологию и синхронизирует все слои: куда положили vCPU, туда же помещается память, и гость это видит. Никакой магии, просто инженерная работа, которую давно никто не делал.