Как оптимизировать приложения для AMD?

Как оптимизировать приложения для AMD?

Рациональное использование кэш-памяти яв­ля­ет­ся важ­ным фак­то­ром быст­ро­дей­ст­вия при­ло­же­ний и улуч­ше­ния точ­нос­ти бенч­ма­рок. Од­на из его осо­бен­нос­тей со­сто­ит в су­щест­во­ва­нии так на­зы­ва­е­мых non-tem­po­ral дан­ных: это ин­фор­ма­ция, кэ­ши­ро­вать ко­то­рую не сто­ит — до­ступ к ней про­ис­хо­дит од­но­крат­но.

Мало того, что это связано с до­пол­ни­тель­ной пе­ре­сыл­кой дан­ных, так еще из кэш-па­мя­ти вы­тес­ня­ет­ся вос­тре­бо­ван­ная про­цес­сором ин­фор­ма­ция. Про­из­во­ди­тель­ность при этом сни­зит­ся — для раз­ме­ще­ния не­нуж­ных дан­ных из кэш-па­мя­ти уда­ля­ю­тся нуж­ные.

Бенч­мар­ки оперативной памяти — типовой пример обработки non-tem­po­ral дан­ных, так как в них име­ет место непрерывная передача блоков, размер которых в разы пре­вы­ша­ет размер кэш-па­мя­ти. В этом слу­чае мы намеренно нивелируем эффект кэ­ши­ро­ва­ния данных (но не инструкций!) для создания мак­си­маль­ной нагрузки на шину DRAM. Иначе, вместо ОЗУ будет протестирована кэш-память. Позитивный эф­фект не­кэ­ши­ру­е­мой записи (non-temporal store) уже был исследован тестовой лабораторией «Компостера».

Практические результаты

В ходе оптимизации бенч­ма­рок оперативной памяти для процессоров AMD, возникла необходимость поддержки не­кэ­ши­ру­е­мо­го чтения (non-temporal load). Замена обычной загрузки (инструкция MOVAPD) не­кэ­ши­ру­е­мой загрузкой (ин­ст­рук­ция MOVNTDQA) дает устойчивый выигрыш около 400 мегабайт в секунду. Рас­смот­рим результаты от­лад­ки тес­то­во­го при­ме­ра в среде FASM Editor. Размер читаемого блока 1 гигабайт, за­пус­ка­ет­ся 4 потока, в соответствии с ко­ли­че­ст­вом ядер исследуемого процессора.

Обычное чтение, инструкция MOVAPD, пропускная способность DRAM около 10.5 Гигабайт в секунду
Рис.1 Обычное чтение, инструкция MOVAPD, пропускная способность DRAM около 10.5 Гигабайт в секунду

Некэшируемое чтение, инструкция MOVNTDQA, пропускная способность DRAM около 10.9 Гигабайт в секунду
Рис.2 Некэшируемое чтение, инструкция MOVNTDQA, пропускная способность DRAM около 10.9 Гигабайт в секунду

Маленькие хитрости

Помимо применения non-temporal load для операции чтения оперативной памяти, акцентируем внимание на двух дополнительных особенностях дизайна высокопроизводительного и компактного кода, особенно значимых для процессоров AMD.

  1. В ситуациях, когда достаточно восьми регистров XMM, следует использовать младшие 8 регистров XMM0-XMM7, поскольку для ряда инструкций, адресация 8 старших регистров XMM8-XMM15 приводит к увеличению размера инструкции на 1 префиксный байт.
  2. Вместо применения только положительных смещений относительно базы, указывающей на начало некоторого блока данных, мы задаем положительные и отрицательные смещения относительно базы, указывающей на середину блока. Поскольку для кодирования адресных смещений в инструкциях применяется знаковое расширение, то все смещения, находящиеся в диапазоне -128...+127 представимы одним байтом, в отличие от набора смещений в диапазоне 0...255, часть которых не представима одним байтом. В режимах Protected Mode 32/64, смещение, не поместившееся в один байт, кодируется 32-битным операндом, размер инструкции возрастет сразу на 4 байта.

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

Исследование кодирования инструкций, цикл чтения памяти в отладчике FDBG: перед исследуемым фрагментом установлена точка останова, инструкция программного прерывания INT3
Рис.3 Исследование кодирования инструкций, цикл чтения памяти в отладчике FDBG: перед исследуемым фрагментом установлена точка останова, инструкция программного прерывания INT3

Для процессоров AMD, у которых кэш память инструкций общая для двух ядер, размер тела цикла имеет важ­ное зна­че­ние, поскольку оказывает влияние на трафик выборки инструкций во время выполнения фра­г­мен­та, кри­ти­че­ско­го с точки зрения производительности.

Конфигурация стенда

Процессор AMD Athlon(tm) X4 880K Quad Core Processor. Установлен один модуль памяти PC3-12800, одноканальный режим, теоретический максимум пропускной способности около 12.8 Гигабайт в секунду. Моделируем бюджетную сис­те­му.

Особенности совместимости

Как известно, 256-битные инструкции и регистры YMM0-YMM15 впервые появились в процессорах, декларирующих поддержку AVX. При этом поддерживается 256-битная форма инструкции передачи данных VMOVAPD.

Инструкции некэшируемого чтения MOVNTDQA/VMOVNTDQA вносят очередную не­ре­гу­ляр­ность в ар­хи­тек­ту­ру век­тор­ных рас­ши­ре­ний системы команд процессора. 256-битная форма поддерживается только в рамках AVX2, 128-битные формы поддерживаются в рамках SSE4.1 и AVX.

Описание инструкции MOVNTDQA/VMOVNTDQA в документации AMD
Рис.4 Описание инструкции MOVNTDQA/VMOVNTDQA в документации AMD

Описание инструкции MOVNTDQA/VMOVNTDQA в документации Intel
Рис.5 Описание инструкции MOVNTDQA/VMOVNTDQA в документации Intel

Примечательно, что для протестированного процессора, в ряде случаев, 128-битные операции SSE пред­по­чти­тель­нее 256-битных операций AVX, поскольку используется 128-битный вычислительный тракт, вы­пол­ня­ю­щий 256-бит­ный код за два 128-битных цикла. Напомним и о том, что кэш-память инструкций первого уровня (L1 instruction cache) и блок вы­чис­ле­ний с плавающей точкой (FPU) являются ресурсами, общими в пре­де­лах пары ядер.

Выводы

Грамотный учет особенностей архитектуры и внутренней топологии взаимодействия ресурсов позволит «выжать мак­си­мум» даже из бюджетных конфигураций аппаратного обеспечения. Вместе с тем, тщательная оптимизация кода на уровне ассемблера, не является трендом в современной софт-индустрии. Присутствующие в документа­ции обо­их про­цес­сор­ных ги­ган­тов предупреждения относительно implementation-dependent реализации некэши­ру­емой загрузки, многое объясняют в сложившемся положении дел…