Numa technology bios настройка
Введение
Видел упоминание об архитектуре NUMA (Non-Uniform Memory Architecture) на платформе AMD в статье
Четырёхъядерность от AMD: платформа Quad FX , а также в новостях Для работы с Quad FX понадобится самая дорогая версия Windows Vista., в которых сообщается, что архитектура NUMA может дать прирост быстродействия платформы AMD Quad FX в операционной системе Windows Vista.
Помимо Windows Vista архитектуру NUMA поддерживает и MS Server 2003.
Цель тестирования - проверить влияние на производительность подсистемы памяти с использованием архитектуры NUMA и в Node Interlive (попеременное.
Видел упоминание об архитектуре NUMA (Non-Uniform Memory Architecture) на платформе AMD в статье
Четырёхъядерность от AMD: платформа Quad FX , а также в новостях Для работы с Quad FX понадобится самая дорогая версия Windows Vista., в которых сообщается, что архитектура NUMA может дать прирост быстродействия платформы AMD Quad FX в операционной системе Windows Vista.
Помимо Windows Vista архитектуру NUMA поддерживает и MS Server 2003.
Цель тестирования - проверить влияние на производительность подсистемы памяти с использованием архитектуры NUMA и в Node Interlive (попеременное использование контроллеров памяти - эквивалентно дисковому массиву RAID0).
Тестируемое железо
(кликните по картинке для увеличения)
В этой системе используются два процессора, соответственно и два контроллера памяти, а так же в BIOS есть возможность переключать NUMA в Node Interlive. К сожалению, в системе нет PCIex слотов, поэтому нет возможности протестировать в графических приложениях.
Тестовые программы
В качестве операционной системы была выбрана MS Server 2003 SP1.
Для тестирования был использован следующий набор синтетических бенчмарков и программного обеспечения:
Everest 3.01.704
CrystalMark2004 0.9.115.278
Rmcpulite_2005_v13 - первая сцена с дефолтными настройками, автоматическим выбором MMX/SSE и максимальным количеством потоков использовался показатель Renderer FPS
WinRar 3.61 встроенный тест с включенной многопоточностью - результат KB/s
7zip 4.42 встроенный тест (настройки дефолтные) с включенной многопоточностью - результат общий MIPS
Для тестирования были взяты задания 1495 (Gromacs), которые используют около 120Мб оперативной памяти и достаточно сильно зависят от её пропускной способности.
Результаты тестирования
Everest 3.01.704
Скорость памяти архитектуры NUMA оказалась гораздо выше, но это, похоже, связано с тем, что тестовый участок памяти находится на одном из контроллеров памяти, что само собой уменьшает задержки при работе с памятью.
CrystalMark2004 0.9.115.278
Rmcpulite_2005_v13
Winrar показывает большие различия в быстродействии архитектур. При использовании Winrar в однопоточном режиме в Node Interlive нет влияния, на каких ядрах выполняется приложение, т.к. память используется попеременно с обоих контроллеров памяти. А вот в однопоточном режиме в NUMA данные, похоже, находятся на первом контроллере памяти, и при добавлении второго процессора снижает общую производительность. В многопоточном режиме в Node Interlive производительность даже увеличивается при использовании двух ядер от различных CPU, по сравнению с двумя ядрами одного CPU (при более ранних тестированиях различных систем - двуядерный вариант всегда лидировал над двухпроцессорным из-за более скоростной работы с кешем), вследствие того, что используются оба контроллера памяти. Также видно, что в режиме NUMA используется контроллер первого CPU, а из-за этого ухудшается масштабируемость системы при увеличении количества ядер.
7zip 4.42
В 7zip не произошло таких больших отличий между NUMA и Node Interlive, похоже, больше обращений в кеш память и влияние оперативной памяти оказалось незначительным.
В Folding@Home запускал от одной вычислительной задачи до четырех и замерял скорость вычисления в сутки (PPD). Для четырех выполняемых вычислительных задач дополнительно проверил PPD при ручном выставлении соответствия одной задаче одного ядра (Set Affinity) в диспетчере задач операционной системы.
При автоматическом выставлении соответствия операционной системой видно падение PPD на одну задачу при увеличении количества одновременно запущенных задач. Также видно, что Node Interlive предпочтительней при автоматическом выставлении соответствия. И только при ручном выставлении соответствия в NUMA производительность увеличивается практически линейно с увеличением задач и превосходит Node Interlive.
Хотя в некоторых статьях и были прогнозы насчет увеличения производительности системы при поддержке операционной системой NUMA - на практике картина неоднозначна. Для полного увеличения быстродействия памяти требуется поддержка NUMA не только со стороны операционной системы, но и со стороны приложений (например, MS SQL поддерживает NUMA), либо необходимо запускать приложения с установкой соответствия CPU.
На мой взгляд, это очередные маркетинговые трюки с заимствованием серверных технологий в настольный сегмент и вряд ли разработчики будут писать приложения с поддержкой NUMA для этого сегмента, но возможно кому-то покажется удобным вариант увеличения быстродействия путем ручной установки соответствия CPU в диспетчере задач.
Numa BIOS – российская БСВВ, разработана в полном соответствии со стандартом UEFI и заменяет стандартный BIOS.
Уникальное доверенное отечественное микропрограммное обеспечение, единственная полноценная альтернатива иностранным BIOS для средств вычислительной техники построенных на платформах Intel и AMD под архитектуру x86/x64.
В реестре российского ПО
Numa BIOS
Основные преимущества Numa BIOS
Заменяет собой оригинальный BIOS материнской платы без потери базовой функциональности, обеспечивает полноценное функционирование аппаратных компонентов и периферии.
Использование Numa BIOS в составе аппаратных платформ позволяет соответствовать критериям Минпромторга, определенным в ПП РФ №719 от 17.07.2015, по отнесению ИТ-продукции к произведенной в России.
Полностью собственная разработка. Все исходные коды Numa BIOS доступны для контроля на отсутствие в нем недекларированных возможностей (НДВ-2) в соответствии с требованиями Регуляторов (ФСТЭК России, ФСБ России).
Возможность встраивания в Numa BIOS дополнительных модулей, обеспечивающих дополнительные функции на уровне БСВВ, например: гипервизор, ядро ОС, криптопровайдер, система централизованного управления, тонкий клиент, антивирусный сканер и т.п.
Возможность установки модуля доверенной загрузки Numa Arce (ИТ.СДЗ.УБ4.ПЗ), разработанного НумаТех специально для функционирования в среде Numa BIOS.
Возможность отключения определенных аппаратных компонентов (контроллеров и устройств), смонтированных на аппаратной платформе ее зарубежным производителем, в целях обеспечения безопасности.
Контроль целостности модулей БСВВ, возможность защиты на доступ в меню управления. Поддержка установки пароля на HDD/SSD, поддержка OPAM, SecureBoot.
Реализует поддержку стандартов UEFI, ACPI, SMBios, CSM (legacy), PM, PXEboot. Корректное функционирование HBA, Raid, SAS, АПМДЗ (OptionROMs). Обеспечивает загрузку и исполнение ОС Windows (XP и новее), ОС Linux, Unix, DOS.
НумаТех может выполнить заказную разработку или адаптацию Numa BIOS для использования в составе практически любой аппаратной платформы, реализованной на базе х86/х64 архитектуры Intel или AMD.
Выполнение требований документа «Требования к межсетевым экранам» (ФСТЭК России, 2016) и возможность использования для создания любых типов программно-технических межсетевых экранов с их последующей сертификацией в системе сертификации ФСТЭК России.
Повсеместно распространена практика сокрытия различных функций, параметров, используемых на этапах разработки и тестирования. Вместе с тем производится и сегрегация плат на уровне БИОС. Как результат, пользователь лишается доступа к фактически прописанным и реализованным возможностям.
реклама
Естественно, пытливого юзера не может устраивать такое злодейское ограничение пользовательской свободы. В связи с чем предлагаю рассмотреть способы обхода этих искусственных препон. Рассматривать будем на примере плат Intel 100-300 чипсетов. Аспекты работы с AMIBCP общеприменимы.
Способ 1. Простой
требуется: программа AMIBCP и навык прошивки МП
Качаем подходящую версию AMIBCP. В нашем случае v5.02.
реклама
File → Open… (файл_биос)
Слева представлено структурное меню БИОС. Сопоставляя названия с фактически наблюдаемыми при входе в БИОС, определяем главный блок.
Именно в этом блоке в соответствующих меню и подменю находятся все доступные настройки. Иерархия та же, что и в БИОС. Наряду со знакомыми наблюдаем и совершенно новые отдельные параметры и даже целые разделы!
реклама
Для открытия элемента достаточно поменять значение в колонке Access/Use на USER:
Обратите внимание, на разделы, описанные как бы наравне с главным, есть ссылки в теле главного блока. Т.е. доступ к ним получить можно. Например, по пути Setup → Advanced → System Agent (SA) Configuration → Memory Configuration большой раздел с настройками памяти.
реклама
Помимо открытия доступа к настройкам, AMIBCP позволяет изменить значения настроек по умолчанию, переименовать параметры и разделы.
Этим способом можно открыть только те настройки, которые непосредственно прописаны в главном блоке.
Способ 2. Сложный (для разделов)
то же + UEFITool, IFR-Extractor и HEX-редактор
Попытаемся разблокировать тот же раздел с настройками памяти на Gigabyte. Организация БИОС здесь другая, упоминаний нужного раздела в главном блоке нет.
Подменим доступный раздел скрытым. «Пожертвуем», к примеру, подменю USB Configuration (Setup → Peripherals).
Определились с тем, что хотим открыть, определились, что хотим закрыть. К делу!
UEFITool
Ищем модуль по сигнатуре $SPF: *Ctrl+F* → Text
Извлекаем и сохраняем модуль: *ПКМ* → Extract Body (to SPF.bin)
GUID модуля может отличаться, в этом случае вести поиск по сигнатуре Setup.
Модуль → PE32 Image Section → *ПКМ* → Extract Body (to Setup.bin)
IFR-Extractor
*открыть* → Setup.bin → Extract (to Setup IFR.txt)
Открываем Setup IFR.txt в блокноте.
Нас интересует FormId.
Теперь определим FormId раздела «Memory Configuration»:
0x3B628 Form: Memory Configuration, FormId: 0x2738
Открываем SPF.bin в HEX-редакторе.
Ищем HEX-последовательность, соответствующую FormID раздела Memory Configuration:
*Ctrl+F* → Hex Values (3827)
Нас интересует page_id через 4 позиции – 0060 (последовательность 6000)
Аналогично ищем page_id для Usb Configuration. Сразу за page_id 005D следует parent_id 0019.
Parent_id – это page_id родительского раздела, в нашем случае раздела Peripherals.
Ищем обратную последовательность parent_id+page_id (19005D00). Это форма вызова раздела USB Configuration.
Заменяем page_id USB Configuration на page_id Memory Configuration. Вместо 19005D00 получаем соответственно 19006000:
Остается только заменить исходный SPF модуль на модифицированный в UEFITool:
*ПКМ* → Replace Body…
Сохраняем: File → Save image file. Прошиваем.
Теперь вместо раздела USB Configuration будет вызываться раздел с настройками памяти.
При желании можно изменить имя раздела в AMIBCP.
Если AMIBCP не работает
С новыми материнками все чаще AMIBCP работать отказывается.
В этом случае остается лишь использовать 2 способ.
О прошивке
Подпишитесь на наш канал в Яндекс.Дзен или telegram-канал @overclockers_news - это удобные способы следить за новыми материалами на сайте. С картинками, расширенными описаниями и без рекламы.
NUMA (Non-Uniform Memory Access — «Неравномерный доступ к памяти» или Non-Uniform Memory Architecture — «Архитектура с неравномерной памятью») — технология совсем не новая. Я бы даже сказала, что совсем старая. То есть, в терминах музыкальных инструментов, это уже даже не баян, а, скорее, варган.
Но, несмотря на это, толковых статей, объясняющих, что это, а главное, как с этим эффективно работать, нет. Данный пост, исправляющий эту ситуацию, предназначен прежде всего для тех, кто ничего не знает про NUMA, но также содержит кое-что интересное и для знатоков-NUMизматов, а главное, он облегчает жизнь мне, инженеру Intel, так как отныне всех интересующихся NUMA русскоязычных разработчиков буду отсылать к нему.
Три Богатыря
И начнем с отрицания отрицания. То есть, посмотрим на Uniform Memory Access (Однородный доступ к памяти), известный также так SMP (Symmetric Multi Processing – Симметричная Многопроцессорная Обработка).
SMP – это архитектура, в которой процессоры соединены с общей системной памятью при помощи шины или подобного соединения)симметрично, и имеют к ней равный однородный доступ. Именно так, как показано на схеме ниже (на примере двух CPU), были устроены все многопроцессорные машины Intel, когда контроллер памяти (MCH/MGCH), больше известный как «Северный Мост» (“NorthBridge”) находился в чипсете.
Недостаток SMP очевиден — при росте числа CPU, шина становится узким местом, значительно ограничивая производительность приложений, интенсивно использующих память. Именно поэтому SMP системы почти не масштабируются, два-три десятка процессоров для них – это уже теоретический предел.
Альтернатива SMP для производительных вычислений – это MPP (Massive Parallel Processing).
MPP — архитектура, разделяющая систему на многочисленные узлы, процессоры в которых имеют доступ исключительно к локальным ресурсам. MPP прекрасно масштабируется, но не столь прекрасно программируется. А именно — не обеспечивает встроенного механизма обмена данными между узлами. То есть, реализовывать коммуникации, распределение и планировку задач на узлах должен выполняемый на MPP софт, что подходит далеко не для всех задач и их программистов.
И, наконец, NUMA (Non-Uniform Memory Access). Эта архитектура объединяет положительные черты SMP и MPP. NUMA система разделяется на множественные узлы, имеющие доступ как к своей локальной памяти, так и к памяти других узлов (логично называемой «удаленной»). Естественно, доступ к удаленной памяти оказывается гораздо медленнее, чем к локальной. Оттуда и название – «неоднородный доступ к памяти». Это – не только название, но и недостаток архитектуры NUMA, для смягчения которого может потребоваться специальная оптимизация софта, о которой — дальше.
Вот как выглядит двухсокетная NUMA система Intel Xeon (а именно там дебютировала Intel NUMA) с контроллерами памяти, интегрированными в CPU.
Процессоры здесь соединены QPI — Intel QuickPath соединением «точка-точка» с высокой пропускной способностью и низкой задержкой передачи.
На рисунке не показан кеш процессоров, но все три уровня кеш памяти, конечно же, там есть. А значит, есть и особенность NUMA, о которой необходимо сказать: NUMA, используемая в системах Intel, поддерживает когерентность кешей и разделяемой памяти (то есть, соответствие данных между кешами разных CPU), поэтому ее иногда называют ccNUMA — cache coherent NUMA. Это означает наличие специального аппаратного решения для согласования содержимого кешей, а также и памяти, когда более чем один кеш хранит одну и ту же ее часть. Конечно, такое общение кешей ухудшает общую производительность системы, но без него программировать систему с непредсказуемым текущим состоянием данных было бы крайне интересно затруднительно. Для уменьшения влияния этого эффекта, следует избегать ситуаций, когда несколько процессоров сразу работают с одним блоком памяти (не обязательно с одной переменной!). Именно так и пытаются поступить продукты, поддерживающие NUMA.
Таким образом, от железа мы плавно перешли к программному обеспечению и производительности NUMA систем.
Итак, NUMA поддерживается следующими OS:
Windows Server 2003, Windows XP 64-bit и Windows Vista – до 64 логических процессоров,
Windows 7, Windows Server 2008 R2 – полная поддержка.
Linux OS kernel: 2.6 и выше, UNIX OS — Solaris и HP-Unix.
Если говорить о базах данных, то NUMA поддерживается Oracle8i, Oracle9i, Oracle10g и Oracle11g, а также SQL Server 2005 и SQL Server 2008.
Именно поэтому в BIOS мультисокетных серверов с NUMA есть специальный пункт «Разрешить\запретить NUMA». Конечно же, от запрета NUMA в BIOS топология системы никак не изменится — удаленная память не приблизится. Произойдет только следующее – система не сообщит ОС и ПО о том, что она NUMA, а значит, распределение памяти и планировка потоков будут «обычными», такими как на симметричных многопроцессорных системах.
Если BIOS разрешает NUMA, то операционная система сможет узнать о конфигурации NUMA узлов из System Resource Affinity Table (SRAT) в Advanced Configuration and Power Interface (ACPI). Приложения могут получить такую информацию, используя библиотеку libnuma в Linux, а сами понимаете, на каких системах — Windows NUMA interface.
Эта информация – начало поддержки NUMA вашим приложением. За ним должна следовать непосредственно попытка максимально эффективно использовать NUMA. Общие слова на эту тему уже сказаны, для дальнейших пояснений перейду к частному примеру.
Допустим, вы выделяете память при помощи malloc. Если дело происходит в Linux, то malloc только резервирует память, а ее физическое выделение происходит только при фактическом обращении к данной памяти. В этом случае память автоматически выделится на том узле, который ее и использует, что очень хорошо для NUMA. В Windows же malloc работает по-другому, он выделяет физическую память непосредственно при аллоцировании, то есть, на узле выделяющего память потока. Поэтому она вполне может оказаться удаленной для других потоков, ее использующих. Но есть в Windows и дружественное к NUMA выделение памяти. Это VirtualAlloc, который может работать точно также, как malloc в Linux. Еще более продвинутый вариант — VirtualAllocExNuma из Windows NUMA API.
Следующий простой пример, использующий OpenMP,
можно подружить с NUMA, обеспечив инициализацию данных каждым потоком, вызывающую соответствующую привязку физической памяти к использующему ее узлу:
Отдельным пунктом здесь надо упомянуть Affinity — принудительную привязку потоков к конкретным процессорам, предотвращающую возможную переброску операционной системой потоков между процессорами и могущую вызвать потенциальный «отрыв» потоков от своей используемой локальной памяти.
Для установки Affinity имеются соответствующие API как в Linux, так и в Windows ( стандартный Windows API, и NUMA WinAPI). Также функциональность для установки привязки присутствуют во многих параллельных библиотеках (например, в показанном выше примере OpenMP за это отвечает переменная окружения KMP_AFFINITY ).
Но надо понимать, что во-первых, affinity срабатывает не всегда (для системы это, скорее, намек, чем приказ), а во-вторых, положительный эффект от установки Affinity будет только в том случае, когда вы полностью контролируете систему, то есть, на ней работает исключительно ваше приложение, а сама ОС не сильно нагружает систему. Если же, как это чаще всего бывает, приложений несколько, причем, они интенсивно используют CPU и память, пытаясь при этом привязаться к одному процессору, ничего не зная друг о друге, да и ОС конкурирует за те же ресурсы, то от использования Affinity может быть больше вреда, чем пользы
Производительность.
А теперь самое интересное. Попробуем узнать, насколько же в реальности доступ к памяти в NUMA неоднороден, а производительность реальных приложений, соответственно, зависит от этой неоднородности.
Прежде всего, посмотрим теоретические данные. Согласно презентациям Intel, «задержка доступа к удаленной памяти ~ 1.7x доступа к локальной памяти, а пропускная способность локальной памяти может быть до двух раз больше, чем удаленной»
Данные реального сервера на Xeon 5500 приводятся в техническом описании Dell -“задержка доступа к локальной памяти составляет 70 наносекунд, к удаленной – 100 наносекунд (т.е. ~1.4 раза), пропускная способность локальной памяти превосходит удаленную на 40% ”.
На вашей реальной системе эти приблизительные данные могут быть получены при помощи бесплатной утилиты Microsoft Sysinternals — CoreInfo, оценивающей относительную «стоимость» доступа к памяти разных узлов NUMA. Результат, конечно, сильно приблизительный, но некоторые выводы сделать позовляет.
Пример результата Coreinfo:
- Использование исключительно локальной памяти (все запросы обрабатываются на первом NUMA узле, в памяти которого лежит тестовая таблица)
- Использование исключительно удаленной памяти (все запросы обрабатываются на втором узле NUMA, с использованием той же таблицы в памяти первого узла.
Как видите, разница составляет всего чуть более 5%! Результат приятно удивительный. И это – случай максимальной разницы, достигаемый при 32 одновременно работающих потоках с запросами (при другом количестве потоков разница еще меньше).
Так нужно ли оптимизировать для NUMA? Зайду издалека. Хотя у меня нет времени убираться дома, зато есть время читать советы по уборке :). И один из полезных, виденных мной советов такой — чтобы меньше убираться, надо избежать потенциального беспорядка, для чего старайтесь хранить все вещи как можно ближе к месту их использования.
Теперь замените «вещи» на «данные», а «квартиру» на «программу» и увидите один из способов достичь порядка в ваших программах. Но это как раз и будет NUMA-оптимизация, о которой вы сейчас и прочли.
Недавно в нашем блоге появилась статья о NUMA-системах, и я хотел бы продолжить тему, поделившись своим опытом работы в Linux. Сегодня я расскажу о том, что бывает, если неправильно использовать память в NUMA и как диагностировать такую проблему с помощью счётчиков производительности.
Итак, начнем с простого примера:
Это простой тест, который в цикле суммирует элементы массива. Запустим его в несколько потоков на двухсокетном сервере, у которого в каждый сокет установлен четырехядерный процессор. Ниже приведен график, на котором мы видим времена выполнения программы в зависимости от числа потоков:
Мы видим, что время выполнения на восьми потоках всего в 1.16 раза короче чем на четырех потоках, хотя при переходе с двух на четыре потока прирост производительности заметно выше. Теперь сделаем простую трансформацию кода: добавим директиву распараллеливания перед инициализацией массива:
И соберем еще раз времена выполнения:
И вот, на восьми потоках произошло улучшение производительности почти в 2 раза. Таким образом, наше приложение практически линейно масштабируется на всём диапазоне потоков.
Итак, давайте разберемся, что же произошло? Каким образом простое распараллеливание цикла инициализации привело к почти двукратному приросту? Рассмотрим устройство двухпроцессорного сервера с поддержкой NUMA:
За каждым четырёхядерным процессором закреплен определенный объем физической памяти, с которым он общается через интегрированный контроллер памяти и шину данных. Такая связка процессор + память называется узел или нода (node). В NUMA-системах (Non Uniform Memory Access) доступ в память чужой ноды занимает намного больше времени, чем доступ в память своей ноды. Когда приложение в первый раз обращается к памяти, то происходит закрепление виртуальных страниц памяти за физическими. Но в NUMA-системах под управлением ОС Linux у этого процесса есть своя специфика: физические страницы, за которыми будут закреплены виртуальные, выделяются на той ноде, с которой произошло первое обращение. Это так называемый “first-touch policy”. Т.е. если с первой ноды произошло обращение к какой либо памяти, то виртуальные страницы этой памяти будут отображаться на физические, которые тоже будут выделены на первой ноде. Поэтому здесь важно правильно инициализировать данные, ведь от того как данные закрепятся за нодами будет зависеть производительность приложения. Если говорить о первом примере, то весь массив был проинициализирован на одной ноде, что привело к закреплению всех данных за первой нодой, после чего половина этого массива была считана другой нодой, а это и привело к ухудшению производительности.
Внимательный читатель должен был уже задаться вопросом «А разве выделение памяти через malloc не является первым доступом?». Конкретно в этом случае – нет. Дело вот в чем: при выделении больших блоков памяти в Linux, функция glibc malloc (а также calloc и realloc) по умолчанию вызывает сервисную функцию ядра mmap. Эта сервисная функция делает лишь отметки о количестве выделенной памяти, но физическое выделение происходит только при первом доступе к ним. Этот механизм реализуется через прерывания (exceptions) Page-Fault и Copy-On-Write, а также через маппирование на «нулевую» страницу (“zero” page). Кому интересны детали, могут почитать книгу «Understanding the Linux Kernel». А вообще, возможна ситуация, когда функция glibc calloc выполнит первый доступ к памяти для того, чтобы её «занулить». Но опять же, такое произойдет, если calloc решит вернуть пользователю ранее освобожденную память на куче (heap), а такая память уже будет существовать на физических страницах. Поэтому во избежании лишних головоломок рекомендуется использовать так называемые NUMA-aware менеджеры памяти (например TCMalloc), но это уже другая тема.
А теперь давайте ответим на главный вопрос этой статьи: «Как узнать правильно ли приложение работает с памятью в NUMA-системе?». Этот вопрос будет для нас всегда самым первым и главным при адаптации приложений для серверов с поддержкой NUMA, независимо от операционной системы.
Для ответа на этот вопрос нам понадобится VTune Amplifier, который умеет считать события для двух счётчиков производительности (performance counters): OFFCORE_RESPONSE_0.ANY_REQUEST.LOCAL_DRAM и OFFCORE_RESPONSE_0.ANY_REQUEST.REMOTE_DRAM. Первый счётчик считает количество всех запросов, данные для которых были найдены в оперативной памяти своей ноды, а второй – в памяти чужой ноды. Можно на всякий случай еще собрать счётчики для КЭШ’а: OFFCORE_RESPONSE_0.ANY_REQUEST.LOCAL_CACHE и OFFCORE_RESPONSE_0.ANY_REQUEST.REMOTE_CACHE. Вдруг окажется что данные находятся не в памяти, а в КЭШ’е процессора на чужой ноде?
Итак, запустим наше приложение без распараллеливания инициализации на восемь потоков под VTune и посчитаем количество событий для указанных выше счетчиков:
Мы видим, что поток, выполнявшийся на cpu 0, работал в основном со своей нодой. Хотя время от времени модуль vmlinux на этом ядре зачем-то заглядывал в чужие ноды. А вот поток на cpu 1, делал всё наоборот: только для 0.13% всех запросов данные нашлись в его собственной ноде. Здесь я должен пояснить, каким образом ядра закреплены за нодами. Ядра 0,2,4,6 принадлежат первой ноде, а ядра 1,3,5,7 – второй. Топологию можно узнать с помощью утилиты numactl:
numactl --hardware
available: 2 nodes (0-1)
node 0 cpus: 0 2 4 6
node 0 size: 12277 MB
node 0 free: 10853 MB
node 1 cpus: 1 3 5 7
node 1 size: 12287 MB
node 1 free: 11386 MB
node distances:
node 0 1
0: 10 20
1: 20 10
Обратите внимание, что здесь перечислены логические номера, в реальности же ядра 0,2,4,6 принадлежат одному четырехядерному процессору, а ядра 1,3,5,7 – другому.
Теперь посмотрим на значение счетчиков для примера с параллельной инициализации:
Картина почти идеальная, мы видим, что все ядра работают в основном со своими нодами. Обращения в чужие ноды, составляют не больше полпроцента от всех запросов, за исключением cpu 6. Это ядро примерно 4.5% всех запросов отправляет в чужую ноду. Т.к. обращение в чужую ноду занимает в 2 раза дольше времени чем в свою, то 4.5% таких запросов не сильно ухудшают производительность. Поэтому, можно сказать, что теперь приложение правильно работает с памятью.
Таким образом, используя эти счётчики вы всегда можете определить есть ли возможность ускорить приложение для NUMA-системы. На практике у меня были случаи когда правильная инициализация данных ускоряла приложения в 2 раза, причем в некоторых приложениях приходилось параллелить все циклы, немного ухудшая производительность для обычной SMP-системы.
Вот так формируется счётчик OFFCORE_RESPONSE_0 в процессоре Nehalem:
Для того чтобы понять кто именно отправляет свои запросы в чужую ноду нужно разложить ANY_REQUEST на составные запросы: DEMAND_DATA_RD, DEMAND_RFO, DEMAND_IFETCH, COREWB, PF_DATA_RD, PF_RFO, PF_IFETCH, OTHER и собрать для них события по отдельности. Таким образом «виновник» был найден:
OFFCORE_RESPONSE_0.PREFETCH.REMOTE_DRAM
cpu 0: 6405
cpu 1: 597190
cpu 2: 2503
cpu 3: 229271
cpu 4: 2035
cpu 5: 190549
cpu 6: 19364266
cpu 7: 228027
Но почему prefetcher именно на 6 ядре заглядывал в чужую ноду, в то время как prefetcher’ы остальных ядер работали со своими нодами? Дело в том, что перед запуском примера с параллельной инициализацией, я дополнительно установил жесткую привязку потоков к ядрам следующим образом:
Согласно этой привязке первые четыре потока работают на первой ноде, а вторые четыре потока – на второй. Отсюда видно, что 6-ое ядро – это последнее ядро принадлежащее первой ноде (0,2,4,6). Обычно prefetcher всегда пытается закачать память с упреждением, которая находится далеко впереди (или позади, зависит от направления, в котором программа обращается к памяти). В нашем случае prefetcher шестого ядра закачивал память, которая находилась впереди той, с которой в тот момент работал поток Internal thread 3. Вот здесь то и произошло обращение в чужую ноду, так как впереди стоящая память частично принадлежала первому ядру чужой ноды (1,3,5,7). А это и привело к появлению 4.5% обращений в чужую ноду.
Замечание: тестовая программа была собрана компилятором Intel с опцией –no-vec, чтобы получить скалярный код вместо векторного. Это было сделано с целью получения «красивых данных» для облегчения понимания теории.
Читайте также: