Проверка влияния патча на производительность

В предыдущей части этого поста я упомянул способ исправления бинарных файлов, скомпилированных с помощью компиляторов Intel или связанных с Intel MKL, чтобы обойти недобросовестный диспетчер ЦП. Патч также должен разрешить использование SIMD-инструкций и других оптимизаций для процессоров AMD.

В этом посте я проведу тесты, чтобы увидеть, насколько производительность увеличилась благодаря патчу. Обратите внимание, что использование SIMD-инструкций повышает производительность только для числовых приложений. Если есть программное обеспечение, которое не выполняет тяжелых вычислений, то в исправлении нет смысла. Также обратите внимание, что установка бинарных исправлений всегда опасна, и всегда есть возможность сломать программное обеспечение путем установки исправлений.

Во всех тестах я использовал компилятор Intel C/C++, компилятор Intel Fortran и Intel MKL версии 2021.6.0. Скомпилированные исполняемые файлы запускались на процессоре AMD Ryzen 7 5800H в системе Windows.

Тестовый тест 1. Умножение матриц на Фортране

Фортран предоставляет встроенную функцию matmul(), которая позволяет перемножать две матрицы вместе. Для этого теста я использовал код Fortran, который умножает две квадратные матрицы разного размера, заполненные случайными вещественными числами (REAL*8, что эквивалентно C double на x64). Умножение матриц повторяется 100 раз, и было измерено общее время выполнения.

Обратите внимание, что в данном случае я не использую подпрограммы BLAS от Intel MKL. Я использую встроенную функцию, чтобы проверить, насколько компилятор может использовать векторизацию для тяжелых числовых вычислений. Исходный код можно найти здесь. Я скомпилировал его с QaxCORE-AVX2, так как мой ноутбук поддерживает AVX2. Обратите внимание, что путь кода по умолчанию в Windows — SSE2.

Результаты между оригинальной и исправленной версией программного обеспечения сильно различаются. В исходной версии (т. е. без исправлений) умножение матриц использует инструкции SSE2. Когда диспетчер ЦП исправлен, чтобы не различать процессоры AMD, он запускает более быстрый кодовый путь AVX2.

Регистры XMM SSE2 имеют длину 128 бит, поэтому они могут работать с двумя числами REAL*8 (каждое по 64 бита) в одной инструкции. В то время как регистры YMM AVX2 имеют длину 256 бит, поэтому они могут работать с 4 числами REAL*8. Таким образом, AVX2 должен быть примерно в два раза эффективнее SSE2, и это то, что вы видите на графике. Разница между SSE2 и AVX2 особенно важна для больших размеров матриц (например, 3000x3000). В Linux вы увидите еще большую разницу, потому что путь к коду по умолчанию в компиляторе - x86, то есть без SIMD-инструкций.

Тест № 2: матричное умножение с Intel MKL

Intel MKL предоставляет подпрограммы BLAS, среди которых очень часто используется DGEMM. DGEMM также можно использовать для умножения двух матриц. В этом тесте я использовал код Fortran, который умножает две квадратные матрицы фиксированного размера, заполненные случайными числами. Умножение матриц повторяется 100 раз, и измеряется время выполнения.

На этот раз я использовал Intel MKL, чтобы увидеть, использует ли MKL диспетчер для запуска медленного кода на AMD. Intel заявила, что решила проблемы с производительностью процессоров сторонних производителей в версии MKL 2020.3. Исходный код можно найти здесь.

В этом случае между исправленной и неисправленной версиями программного обеспечения нет большой разницы. Исправление средства проверки CPUID, по-видимому, немного улучшает производительность. Таким образом, кажется, что Intel действительно исправила MKL для использования правильных векторных инструкций для процессоров сторонних производителей.

Еще одна вещь, на которую стоит обратить внимание, это то, что подпрограмма DGEMM в Intel MKL работает быстрее, чем встроенная в Fortran matmul(). Например, DGEMM на матрице 3000x3000 примерно на 25% быстрее, чем matmul() с AVX2. Разработчики математических библиотек обычно в значительной степени оптимизируют свой код, чтобы максимально повысить эффективность.

Тест № 3: скалярное произведение C++

Скалярное произведение между двумя массивами — это операция, при которой вы умножаете соответствующие элементы массива, а затем суммируете результаты. Я использую код C++ для вычисления скалярного произведения двух массивов. Из-за умножения и сложения чисел здесь очень полезны инструкции FMA.

В данном случае я скомпилировал его с помощью -arch:pentium -QaxCORE-AVX2 . Я установил базовый кодовый путь на x86 (старые процессоры Pentium использовали только x86), чтобы разница между кодовым путем по умолчанию и векторизованным кодовым путем SIMD была более очевидной. Обратите внимание, что объем вычислений, которые необходимо выполнить в этом случае, довольно мал (по сравнению, например, с умножением массива), поэтому разница в скорости векторизации FMA будет небольшой (при выражении в секундах). Исходный код можно найти здесь.

Как видите, инструкция FMA дает небольшой прирост. Но количество выполняемых вычислений недостаточно велико, чтобы показать большую разницу во времени, как я упоминал выше. В реальных научных приложениях потребуются тяжелые вычисления, и SIMD станет важным. (Кроме того, современное научное программное обеспечение все чаще использует ускорение GPU, которое также использует SIMD, за исключением того, что «регистры» SIMD находятся в GPU, а не в CPU. А также количество «ядер» и размер «регистров». ” намного больше, чем то, что доступно в ЦП.)

Другие варианты использования исправлений

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

Вы увидите такое сообщение:

В неудачном случае, если вы получите бинарный файл, скомпилированный таким образом, исправление средства проверки CPUID также удалит это сообщение и позволит запустить ваш код.

Как видите, исправление удаляет сообщение об ошибке, и программа работает отлично. (Обратите внимание, что если ваш процессор не может запускать AVX2, и вы компилируете для AVX2, вы все равно увидите сообщение об ошибке.)

Заключение

Таким образом, результатом всего этого является то, что вы можете очень легко воспользоваться преимуществами автоматической векторизации и множественной диспетчеризации компиляторов Intel C++/Fortran на AMD и других процессорах, отличных от Intel. Все, что вам нужно, это установка Python.

Одна из проблем с этим подходом заключается в том, что вам все равно нужно вносить разные модификации для разных процессоров, отличных от Intel. «Исправление» не отключает диспетчеризацию ЦП Intel, оно просто изменяет строку поставщика CPUID, с которой сравнивается. Итак, если пропатчить программу и заменить GenuineIntel на AuthenticAMD, то теперь программа работает корректно только на процессорах AMD. Диспетчер честно работать не будет на VIA nano или еще на чем-то.

Кроме того, существует риск исправления двоичного файла таким образом. У некоторых программ есть законные причины для проверки строки поставщика CPUID, например, для правильного управления потоками. (Многие игры делают это, потому что для наилучшей работы разным ЦП требуется разное расписание потоков.) И, как я показал выше, Intel MKL хорошо работает в процессорах AMD без каких-либо исправлений, поэтому исправление не требуется.

Конечно, Intel может в будущем запутать этот код проверки ЦП, чтобы предотвратить установку исправлений. Intel также может в будущем полностью отказаться от проверки производителя ЦП и использовать только честный диспетчер, который работает с ЦП всех марок, но это маловероятно, учитывая, что Intel хочет обеспечить наилучшую производительность на своих ЦП.

Спасибо за прочтение! Пожалуйста, не стесняйтесь оставлять комментарии или вопросы в ответ.

Скрипт Python и данные тестов можно найти здесь: https://github.com/shoubhikraj/intel-cpu-patch