Это перевод статьи Frank Denneman AMD EPYC и vSphere vNUMA.
Публикуется с разрешения автора.

Да, я знаю, что уже доступно второе поколение процессоров EPYC, но нужно начинать с начала. Фрэнк сказал, что уже работает над статьей про Rome, будем ждать от него новостей. А пока разберемся с особенностями Naples.

AMD набирает популярность на серверном рынке с EPYC CPU платформой. EPYC CPU платформа предоставляет высокое количество ядер и большой объем памяти. Если вы знакомы с предыдущим поколением AMD, то знаете, что AMD придерживается других принципов работы, нежели Intel. Для справки посмотрите статью, которую я писал в 2011 о 12-core 6100 Opteron под кодовым именем Magny-Cours. EPYC предоставляет больший масштаб, используя ранее представленные принципы. Давайте рассмотрим EPYC архитектуру и посмотрим, как она влияет на сайзинг ВМ и настройки ESXi. (Целью этой статьи не является сравнение AMD и Intel и оценка, что лучше, я хочу описать архитектурные различия).

EPYC архитектура

AMD EPYC — image courtesy of wccftech.com

Compute Complex

Zeppelin CCX Layout of 32 Core EPYC

Каждое ядро имеет свой собственный L1 (инструкции (64KB) и данные (32KB)) и L2 кэш (всего 4MB L2 кэша). Zeppelin имеет 16MB L3 кэша. Интересно, что каждый CCX имеет собственный L3 кэш объемом 8MB, который, в свою очередь разделен на 4 части по 2MB. Два CCX в кристалле Zeppelin соединены друг с другом через интерконнект (Infinity Fabric). Добавление хопов при доступе к памяти невыгодно с точки зрения пропускной способности и задержек. Несколько технических порталов произвели серьезное тестирование производительности кэша и по словам Anandtech.com:

“Локальный, находящийся внутри CCX 8MB L3 кэш доступен с очень низкими задержками. Но как только ядру необходимо получить доступ к другой части L3 кэша, находящемся даже на том же кристалле, задержка становится достаточно высока, она лишь немного лучше, чем задержка доступа к DRAM.”

По сути, это означает, что вы не можете считать все 64MB L3 кэша единым пулом ресурсов кэш памяти. Лучше считать, что вы имеете восемь 8MB пула кэш памяти. Это важно понимать, если несколько рабочих нагрузок совместно используют одни и те же данные, а планировщик ESXi пытается разместить обе рабочие нагрузки в одном и том же NUMA узле, чтобы оптимизировать производительность кэша и памяти для этих рабочих нагрузок. Может так случиться, что размер L3 может быть недостаточен. Опция, влияющая на это поведение, называется Action Affinity, более подробную информацию об этом параметре можно найти в последней части статьи.

Количество ядер Zeppelin

Zeppelin design of 24 Core EPYC

В таблице ниже показано количество ядер на Zeppelin для трех самых больших EPYC процессоров. Общее количество ядер на каждом Zeppelin кристалле можно использовать в качестве ориентира для настройки vNUMA, описанной далее в этой статье.

┌───────┬───────────────┬────────────────────┬────────────────┐
│ Cores │ Cores per CCX │ Cores per Zeppelin │ Zeppelin Count │
├───────┼───────────────┼────────────────────┼────────────────┤
│ 32 │ 4 │ 8 │ 4 │
│ 24 │ 3 │ 6 │ 4 │
│ 16 │ 2 │ 4 │ 4 │
└───────┴───────────────┴────────────────────┴────────────────┘

Infinity Fabric

Каждый CCX подключен к SDF через Cache-Coherent Master (CCM), который отвечает за пересылку когерентного (согласованного) трафика между CCX. SDF использует Unified Memory Controller (UMC) для подключения к DRAM модулям памяти. Каждый UMC предоставляет канал памяти для двух модулей DIMM. Обеспечивая работу четырех DIMM модулей памяти.

Zeppelin CCX and SDF Architecture

Как это влияет на сайзинг виртуальной машины? Zeppelin кристалл — это NUMA узел, который содержит максимум 8 ядер (16 потоков) и суммарный объем памяти, сформированный 4 модулями DIMM. Таким образом, один EPYC процессор предоставляет четыре NUMA узла операционной системе.

Объем оперативной памяти и NUMA

Scalable архитектурой это либо 96GB или 192GB памяти. Это означает, что следуя best practice для высокой производительности, необходимо заполнение всех каналов для обеспечения максимальной пропускной способности. Однако при нынешней цене DIMM многие клиенты не могут позволить себе такую конфигурацию.

В EPYC, каждый Zeppelin имеет два канала памяти. Каждый канал поддерживает до двух модулей. Для хорошей производительности каждый канал должен быть укомплектован хотя бы одним модулем DIMM. Это означает, что хорошо спроектированная (с точки зрения производительности) система должна иметь 16 модулей памяти. Такая конфигурация обеспечивает теоретическую пропускную способность 42,6GB/s, обеспечивая при этом (небольшую) емкость памяти только двух модулей DIMM. В результате один EPYC процессор презентует четыре NUMA узла операционной системе. Если используется минимальное количество модулей памяти на канал — один (1DPC), размер NUMA узла может быть слишком мал и, следовательно, мала общая производительность, если размер виртуальной машины превышает объем памяти, доступной каждому Zeppelin. Servethehome опубликовали benchmark тесты о различиях производительности между разными конфигурациями памяти для EPYC.

1 EPYC CPU Package = 4 NUMA Nodes

Для NUMA очень важно понимать границы локальной памяти и удаленной памяти. Традиционно это легко разграничивалось по количеству ядер процессора и объему подключенной памяти. При использовании EPYC необходимо также понимать различие между типами удаленного доступа к памяти. Это может быть удаленный доступ к памяти внутри одного процессора или удаленный доступ к памяти другого процессора (сокета). Причина, по которой необходимо понимать эту разницу, заключается во влиянии на скорость доступа к памяти приложений. Наличие виртуальной машины и приложения, занимающих несколько NUMA узлов, может привести к очень нестабильному времени отклика.

Доступ к локальной памяти

Local Memory Access

Презентация AMD “Zeppelin an SOC for Multi-Chip Architectures” сообщает, что задержка доступа к локальной памяти составляет 90 наносекунд.

Удаленный доступ к памяти внутри одного процессора

В общей сложности каждый Zeppelin имеет 4 IFOP, но нужны на самом деле только три, поскольку в каждом процессоре есть только еще три Zeppelin.

Если быть точнее, IO проходит через еще один компонент, прежде чем попасть в IFOP. Этот компонент называется Coherent AMD socKet Extender (CAKE). Это облегчает кристалл-кристалл (die-to-die) или сокет-сокет (socket-to-socket) доступ к памяти. Этот модуль переводит запросы и ответы в формат, используемый SDF транспортным уровнем, в последовательный формат, используемый IFOP. Это означает, что при доступе к данным, хранящимся в модулях DIMM, подключенным к другим Zeppelin внутри одного процессора, требуется дополнительные хопы и процессорные циклы. AMD говорит, что задержка в таком случае составит ~145ns.

Remote Memory Access within EPYC CPU

Межпроцессорный удаленный доступ

Поскольку каждый Zeppelin имеет два IFIS контроллера, не каждый Zeppelin в двухсокетной системе подключен друг к другу напрямую. В худшем варианте будет два хопа. Первый хоп от одного процессора к другому и дополнительный хоп от одного Zeppelin к другому Zeppelin, к которому подключен модуль DIMM, содержащий данные. К сожалению, AMD не поделилась информацией о такой задержке.

Remote Access Inter-package, die-to-die communication

Сайзинг ВМ

Virtual NUMA

Когда была представлена vNUMA, наибольшее количество ядер процессора было равно 8, поэтому инженеры VMware установили пороговое значение vNUMA равное 9 (numa.vcpu.min=9). Это означает, что ВМ должна иметь по крайней мере 9 vCPU для создания virtual NUMA топологии. Учитывая, что наибольшее число ядер в системе EPYC составляет восемь ядер на Zeppelin, вы можете настроить стандартное пороговое значение vNUMA, чтобы оно было похоже на физическую топологию используемой модели EPYC.

Например, EPYC 7401 содержит 24 ядра, 6 ядер на Zeppelin и, таким образом, 6 ядер на NUMA узел. Когда вы используете стандартные настройки numa.vcpu.min=9, ВМ с 8 vCPU автоматически настраивается вот так.

Screenshot by @AartKenens

VPD — это virtual NUMA клиент, который воздействует на гостевую ОС, в то время как PPD — это NUMA клиент, который использует планировщик CPU VMkernel. В этой ситуации, планировщик ESXi использует два физических NUMA узла для удовлетворения запросов ВМ к процессору и памяти, в то время как гостевая ОС воспринимает топологию как Uniform Memory Access (UMA) систему. В UMA системе время доступа к памяти не зависит от того какой процессор делает этот запрос или какой чип содержит запрашиваемые данные, т.е. во всей системе примерно одинаковые задержки и пропускная способность. Тем не менее, это не наш кейс. Чтение и запись из удаленного CCX кэша и удаленной памяти (даже внутри одного процессора) медленнее, чем из локальной памяти внутри одного Zeppelin. Если задать параметр numa.vcpu.min=6, то создадутся два VPD и гостевая ОС получит информацию о физической топологии хоста. Гостевая ОС и приложения смогут оптимизировать работу с памятью для достижения лучшей производительности.

Action Affinity

Что если настроенный объем памяти ВМ превышает объем памяти физического узла NUMA?

Например, EPYC 7601 (32 ядра), NUMA узел содержит 8 ядер, и сервер укомплектован 256ПИ (16 x 16GB DIMM). NUMA узел имеет 4 модуля DIMM. Таким образом, NUMA узел содержит 8 ядер и 64GB. Что произойдет, если ВМ будет настроена с 6 vCPU и 96GB? По умолчанию планировщик ESXi попытается разместить 64GB памяти ВМ внутри NUMA узла, оставшиеся 32GB в удаленном NUMA узле. Включив advanced настройку numa.consolidate = FALSE заставит планировщик распределить ВМ между оптимальным количеством NUMA узлов, большим чем 1. В этом случае будут созданы два NUMA узла, и планировщик распределит по 3 vCPU в каждый NUMA узел.

Теперь производительность и поведение приложения зависит от его архитектуры. Если приложение однопоточное, этот параметр может оказаться бесполезным. Однако, если это многопоточное приложение, вы можете заметить некоторые преимущества. Единственное, что нужно сделать, это установить параметр numa.vcpu.min в значение равное количеству vCPU в каждом virtual NUMA клиенте, чтобы определить топологию vNUMA для гостевой ОС и для приложения. Следующая команда поможет вам получить NUMA конфигурацию виртуальной машины:

vmdumper -l | cut -d \/ -f 2-5 | while read path; do egrep -oi “DICT.(displayname.|numa.|cores.|vcpu.|memsize.|affinity.)= .|numa:.|numaHost:.” “/$path/vmware.log”; echo -e; done

Помните, что планировщик CPU и NUMA в ESXi не использует SRAT (System Resource Allocation Table) для определения дистанции между отдельными NUMA узлами. ESXi использует свой собственный метод для определения задержки между различными узлами NUMA в системе. ESXi использует значения этих задержек для первоначального размещения и пытается разместить NUMA клиентов ВМ максимально близко друг к другу. Однако планировщик ESXi не использует эту информацию во время балансировки нагрузки. Эта работа ведётся. Добавление нового первоклассного показателя в эвристику — непростая задача, и, зная инженеров CPU, они стремятся создать систему, которая будет полностью улучшена за счет дополнения нового кода.

Увеличение NUMA Node Compute Sizing

Если все рабочие нагрузки соответствуют одному и тому же паттерну нагрузки, вы можете изменить настройку целиком ESXi хоста, добавив advanced параметр numa.PreferHT=1.

Channel-Pair Interleaving (1 NUMA узел на сокет)

Исследуйте ваши требования к рабочим нагрузкам

В Twitter спрашивают, что я думаю об архитектуре процессора EPYC? Для каждой технической проблемы есть решение. Если посмотреть на архитектуру, я думаю, что EPYC — отличное решение для небольших и средних рабочих нагрузок. Я думаю, что для более крупных монолитных приложений, требующих постоянной производительности, будет лучше посмотреть на другие архитектуры. (Это мое мнение, а не мнение VMware!)

Все, что здесь публикуется является моим персональным мнением. Не является мнением или позицией моих прошлых, текущих или будущих работодателей.

Все, что здесь публикуется является моим персональным мнением. Не является мнением или позицией моих прошлых, текущих или будущих работодателей.