Так уж сложилось, далеко не каждый разработчик программного обеспечения располагает достаточными ресурсами для полной оптимизации своих продуктов с учетом многообразия существующих систем. В результате многолетней конкуренции производителей CPU образовалось деление платформ на «правильные» и «альтернативные», и потенциал весьма достойных аппаратных решений остается не раскрытым.
Попробуем разобраться в различиях мультипроцессорных топологий от Intel и AMD, чтобы в реальном тестировании выяснить, чем нам грозит игнорирование этих различий.
Intel Hyper Threading
При использовании технологии Intel Hyper-Threading, каждое физическое ядро представляется в виде двух логических процессоров. Вычислительные ресурсы процессоров одного ядра присутствуют в единственном экземпляре. Немного упрощая, можно сказать, что каждый из логических процессоров, содержит собственные архитектурные регистры, определяющие текущий контекст выполняемой программы, а также контроллер прерываний Local APIC (Advanced Programmable Interrupt Controller), необходимый для взаимодействия выполняемых потоков, остальные ресурсы являются общими.
AMD Modules
Если у Intel одно вычислительное ядро способно выполнять два потока, то AMD вводит понятие модуля, при этом каждый модуль содержит два ядра. Каждое из ядер содержит собственный L1 кэш данных, в то время как L1 кэш инструкций, унифицированный кэш L2, а также блок FPU являются общими для пары ядер одного модуля. Такой модуль производительнее одного ядра, выполняющего два потока, но уступает паре независимых ядер.
Рис 1. Топология Single Node процессора для простых систем
Согласно рис.1, у каждого ядра свой собственный кэш данных первого уровня (DC=Data Cache). В то время как кэш инструкций IC=Instruction Cache, и унифицированный кэш L2 это общие ресурсы для двух ядер одной пары. Кэш L3 общий для всего процессора.
Рис 2. Топология Dual Node процессора для серверов и рабочих станций
Это процессор с кластерной архитектурой для высокоуровневых систем. Обращаем внимание, L3 и пара каналов памяти, отдельные и собственные для каждого из кластеров. С точки зрения использования памяти, получаем NUMA-топологию в пределах одного процессора, как и в случае Intel Cluster-on-Die. Рассмотрим мультипроцессорную топологию, принятую в системах AMD на примере высокоуровневой платформы.
Рис 3. Иерархическая схема мультипроцессорной топологии AMD
Верхний уровень иерархии это два процессора, установленные в разъемы Socket P#0, Socket P#1.
Каждый процессор содержит два кластера (NUMA-узла), поэтому в системе 4 NUMA-узла: NUMA Node P#0...P#3. Каждый из таких узлов содержит кэш L3 (общий для инструкций и данных), размер которого в этом примере 8MB. Каждый NUMA-узел содержит 4 модуля. Модуль содержит, в данном примере, кэш L2(общий) размером 2MB, и кэш L1(инструкций) размером 64KB. Каждый модуль содержит два ядра (Cores). Кэш L1(данных) размером 16KB, отдельный для каждого ядра, в отличие от кэш L1(инструкций), который общий для пары ядер.
Практика
С помощью информационно-диагностических утилит собственной разработки (CPUIDx64 и Java CPUID), рассмотрим результат детектирования процессора операционной системой Microsoft Windows 10 а также информацию, выдаваемую процессором при выполнении инструкции CPUID. Для проведения данного эксперимента нам пришлось купить процессор для ПК, производства компании AMD.
Рис 4. Гибридный процессор AMD A8-7670K со встроенной графикой Radeon R7
Процессор содержит 4 вычислительных ядра x86 (CPU) и 6 графических ядер (GPU). Сегодня в центре нашего внимания вычислительные ресурсы общего назначения, а именно x86 CPU.
Рис 5. Так выглядит строка CPU name string, получаемая из процессора в текстовом виде
Интегрированные графические ядра (GPU) а также расширения спецификации ACPI, необходимые для их регулярного декларирования, станут темой отдельной статьи.
Рис 6. Дамп информации, возвращенной WinAPI GetLogicalProcessorInformation
Рис 7. Список ядер с перечислением логических процессоров каждого ядра
Как и при Hyper-Threading, имеет место декларация физических ядер, представляемых в виде двух логических процессоров, несмотря на различия MP топологии AMD и Intel.
Рис 8. Список доменов NUMA nodes
Рассматриваемый процессор не поддерживает кластеризацию, поэтому в системе только один NUMA-домен.
Рис 9. Список процессорных разъемов
Платформа поддерживает один процессорный сокет.
Рис 10. Список всех видов кэш-памяти, перечисление для всех ядер
Процессор содержит 4 ядра. Каждое ядро поддерживает собственный L1 кэш данных, размером 16 килобайт. В то время как L1 кэш инструкций, размером 96 килобайт, а также унифицированный (данные+инструкции) кэш L2, реализованы как общие для пары ядер (модуля).
Чем грозит обманчивое сходство
Как видим информация, возвращаемая функциями Win API на платформе AMD, определяет логические и физические процессоры, также, как это имеет место для платформы с поддержкой Intel Hyper-Threading. Вместе с тем, игнорирование различий двух архитектур, в некоторых случаях приводит к неуместной имплементации принципа отключения нечетных потоков. Поясним сказанное.
Так как для некоторых применений, например бенчмарок памяти и кэш, Hyper-Threading может дать отрицательный эффект, иногда имеет место необходимость отключения этой технологии применительно к приложению. Активность Hyper-Threading может обнаруживаться на основании топологической информации, по факту использования кэш памяти уровня L1 двумя потоками.
Рис 11. Процессор декларирует разделение кэш-памяти инструкций L1 code и унифицированной кэш-памяти инструкций и данных L2 unified двумя потоками, согласно параметру Max. logical CPU sharing this cache (кэш-память данных первого уровня L1 data у каждого потока собственная): несмотря на топологические различия, многоядерность, основанная на модулях, декларируется по тем же принципам, что и Hyper-Threading
Конечно, использовать CPUID было бы проще и правильнее, но к описанному методу детектирования HT, многих разработчиков принуждает тот факт, что ряд процессоров Intel, при некоторых условиях декларируют наличие Hyper-Threading (и количество логических процессоров равное двум) в ситуациях, когда Hyper-Threading выключен в CMOS Setup.
Обнаружив использование кэш-памяти уровня L1 двумя потоками, программа задает так называемую Affinity маску, содержащую единицы в четных битах и нули в нечетных, запрещая использование нечетных логических процессоров, таким образом, отказываясь от применения HT. Аналогичная операция в системе AMD, приведет к исключению нечетных ядер. Вряд ли это тот результат, которого ожидал программист.
Рис 12. Еще одна интересная особенность: нулевым значением бита FP256, процессор «сознается» в том,
что 256-битные операции выполняются как две 128-битных в режиме аппаратной эмуляции
Резюме
Итак, компания AMD создала собственную модель, определяющую «парность» логических процессоров. Если производительность важна для вашего приложения, рекомендуем воздержаться от соблазна сэкономить ресурсы и усилия, применяя одинаковый подход к оптимизации многопотоковой обработки для Intel и AMD. При формальной совместимости, различия слишком существенны…