Библиотека Intel OpenMP значительно снижает пропускную способность памяти на платформах AMD, установив KMP_AFFINITY=scatter

#openmp #amd #hpc #icc #affinity

#openmp #hpc #icc #amd-процессор #сходство

Вопрос:

Для программ с ограниченным объемом памяти не всегда быстрее использовать много потоков, скажем, столько же, сколько ядер, поскольку потоки могут конкурировать за каналы памяти. Обычно на машине с двумя сокетами чем меньше потоков, тем лучше, но нам нужно установить политику привязки, которая распределяет потоки по сокетам, чтобы максимально увеличить пропускную способность памяти.

Intel OpenMP утверждает, что KMP_AFFINITY=scatter — для достижения этой цели противоположное значение «компактный» — это размещение потоков как можно ближе. Я использовал ICC для создания программы Stream для сравнительного анализа, и это утверждение легко проверяется на компьютерах Intel. И если установлен параметр OMP_PROC_BIND, собственные переменные OpenMP env, такие как OMP_PLACES и OMP_PROC_BIND, игнорируются. Вы получите такое предупреждение:

         OMP: Warning #181: OMP_PROC_BIND: ignored because KMP_AFFINITY has been defined
  

Однако тест на новейшей машине AMD EPYC, который я получил, показывает действительно странные результаты. KMP_AFFINITY=scatter обеспечивает максимально низкую пропускную способность памяти. Похоже, что этот параметр делает прямо противоположное на машинах AMD: размещение потоков как можно ближе друг к другу, так что даже кэш L3 на каждом узле NUMA используется не полностью. И если я явно установлю OMP_PROC_BIND=spread, Intel OpenMP игнорирует его, как указано в приведенном выше предупреждении.

Машина AMD имеет два сокета, 64 физических ядра на сокет. Я тестировал с использованием 128, 64 и 32 потоков, и я хочу, чтобы они были распределены по всей системе. Используя OMP_PROC_BIND=spread, Stream дает мне скорость триады 225, 290 и 300 ГБ / с соответственно. Но как только я устанавливаю KMP_AFFINITY=scatter, даже когда OMP_PROC_BIND=spread все еще присутствует, потоки дают 264, 144 и 72 ГБ / с.

Обратите внимание, что для 128 потоков на 128 ядрах установка KMP_AFFINITY=scatter обеспечивает лучшую производительность, это еще больше говорит о том, что на самом деле все потоки расположены как можно ближе, но не рассеиваются вообще.

Таким образом, KMP_AFFINITY=scatter отображает совершенно противоположное (в плохом смысле) поведение на компьютерах AMD и даже перезаписывает собственную среду OpenMP независимо от марки процессора. Вся ситуация звучит немного подозрительно, поскольку хорошо известно, что ICC определяет марку процессора и использует диспетчер процессора в MKL для запуска более медленного кода на компьютерах, отличных от Intel. Так почему ICC не может просто отключить KMP_AFFINITY и восстановить OMP_PROC_BIND, если он обнаруживает процессор, отличный от Intel?

Кому-нибудь известна эта проблема? Или кто-нибудь может подтвердить мои выводы?

Чтобы дать больше контекста, я являюсь разработчиком коммерческой программы для вычислительной гидродинамики, и, к сожалению, мы связываем нашу программу с библиотекой ICC OpenMP, а значение KMP_AFFINITY=scatter установлено по умолчанию, потому что в CFD мы должны решать крупномасштабные разреженные линейные системы, и эта часть чрезвычайно ограничена памятью. Я обнаружил, что при установке KMP_AFFINITY=scatter наша программа становится в 4 раза медленнее (при использовании 32 потоков), чем фактическая скорость, которую программа может достичь на компьютере AMD.

Обновить:

Теперь, используя hwloc-ps, я могу подтвердить, что KMP_AFFINITY=scatter на самом деле выполняет «compact» на моей машине AMD threadripper 3. Я прикрепил результат lstopo. Я запускаю свою программу CFD (созданную ICC2017) с 16 потоками. OPM_PROC_BIND=spread позволяет разместить один поток в каждом CCX, чтобы полностью использовать кэш L3. Hwloc-ps -l -t дает:

введите описание изображения здесь

При установке KMP_AFFINITY = scatter я получил

введите описание изображения здесь

Я попробую последнюю версию среды выполнения ICC / Clang OpenMP и посмотрю, как это работает.

введите описание изображения здесь

Комментарии:

1. Для дальнейшего использования используйте KMP_AFFINITY=verbose,scatter для печати карты привязки и сравнения логических процессоров на карте с выводом hwloc .

2. @HristoIliev Спасибо! Я только что попробовал использовать KMP_AFFINITY=verbose, scatter,granularity= core на двух машинах с одним сокетом, одна из которых — AWS c5d.12xlarge с 24-ядерным Xeon, а другая — 64-ядерный threadripper 3. Теперь я могу подтвердить, что алгоритм «scatter» разработан неправильно. Он попытается разместить потоки через сокеты, но на каждом сокете потоки расположены как можно ближе. Затем для машины с одним сокетом потоки просто размещаются последовательно от core: 0.

3. Это не влияет на чип Intel, поскольку все ядра используют кэш L3. Но для чипа AMD каждые 4 ядра на одном CCX имеют свой собственный кэш L3. Такое размещение приводит к минимальному использованию кэша L3 на этом чипе. Я продолжу свое исследование на двухсокетной машине AMD EPYC позже.

Ответ №1:

TL; DR: не использовать KMP_AFFINITY . Она не переносима. Предпочтительнее OMP_PROC_BIND (его нельзя использовать KMP_AFFINITY одновременно). Вы можете смешать его с OMP_PLACES для привязки потоков к ядрам вручную. Кроме того, numactl следует использовать для управления привязкой канала памяти или, в более общем смысле, эффектами NUMA.

Длинный ответ:

Привязка потоков: OMP_PLACES может использоваться для привязки каждого потока к определенному ядру (уменьшая переключение контекста и проблемы с NUMA). OMP_PROC_BIND и KMP_AFFINITY теоретически должны делать это правильно, но на практике они не могут этого сделать в некоторых системах. Обратите внимание, что OMP_PROC_BIND и KMP_AFFINITY являются исключительной опцией: их не следует использовать вместе ( OMP_PROC_BIND это новая переносимая замена старой KMP_AFFINITY переменной среды). Поскольку топология ядра меняется от одной машины к другой, вы можете использовать hwloc инструмент для получения списка идентификаторов PU, требуемых OMP_PLACES . Особенно hwloc-calc для получения списка и hwloc-ls проверки топологии процессора. Все потоки должны быть привязаны отдельно, чтобы перемещение было невозможно. Вы можете проверить привязку потоков с hwloc-ps помощью .

Эффекты NUMA: процессоры AMD создаются путем сборки нескольких CCX, соединенных вместе с помощью соединения с высокой пропускной способностью (AMD Infinity Fabric). Из-за этого процессоры AMD являются системами NUMA. Если не учитывать эффекты NUMA, это может привести к значительному снижению производительности. numactl Инструмент предназначен для контроля / смягчения эффектов NUMA: процессы могут быть привязаны к каналам памяти с помощью --membind опции, а политика распределения памяти может быть установлена на --interleave (или --localalloc если процесс поддерживает NUMA). В идеале процессы / потоки должны работать только с выделенными данными и первыми подключаться к локальным каналам памяти. Если вы хотите протестировать конфигурацию на данном CCX, вы можете поиграть с --physcpubind и --cpunodebind .

Я предполагаю, что среда выполнения Intel / Clang не выполняет хорошую привязку потоков, когда KMP_AFFINITY=scatter она установлена, из-за плохого сопоставления PU (что может быть вызвано ошибкой ОС, ошибкой во время выполнения или неправильными настройками пользователя / администратора). Вероятно, из-за CCX (поскольку обычные процессоры, содержащие несколько узлов NUMA, были довольно редкими).
На процессорах AMD потоки, обращающиеся к памяти другого CCX, обычно требуют дополнительных значительных затрат из-за перемещения данных через (довольно медленное) межсоединение Infinity Fabric и, возможно, из-за его насыщенности, а также одного из каналов памяти. Я советую вам не доверять автоматической привязке потоков среды выполнения OpenMP (use OMP_PROC_BIND=TRUE ), а выполнять привязки потоков / памяти вручную, а затем сообщать об ошибках, если это необходимо.

Вот пример результирующей командной строки для запуска вашего приложения: numactl --localalloc OMP_PROC_BIND=TRUE OMP_PLACES="{0},{1},{2},{3},{4},{5},{6},{7}" ./app

PS: будьте осторожны с идентификаторами PU / core и логическими / физическими идентификаторами.

Комментарии:

1. Большое спасибо! Очень информативно. Я последую вашим предложениям и проведу тщательное исследование. Я также получил руководство по настройке HPC для компьютера AMD EPYC, его тоже нужно прочитать. Очень ценю ваш ответ.

2. Обратите внимание, что код среды выполнения OpenMP Intel / LLVM доступен в дистрибутиве LLVM и что он использует hwloc для понимания аппаратной компоновки машины. Поэтому он должен быть в состоянии сделать это правильно 🙂

3. Еще раз спасибо! Далее я продолжу играть с эффектом numa. Многопоточность в нашей программе CFD следует оригинальной парадигме распараллеливания MPI, то есть разбивает сетку CFD на множество частей, и каждый поток работает над одной частью. Таким образом, связь между этими потоками сводится к минимуму с помощью алгоритма разделения сетки (metis, chaco, …). Я предполагаю, что поток, естественно, будет выделять память той части сетки, с которой он работает, как можно более локально, поэтому эффект NUMA может быть незначительным.

4. Уже не так просто реализовать код, поддерживающий NUMA, который использует преимущества политики NUMA первого касания, поскольку автоматический балансировщик NUMA в Linux 3.x и новее, если он включен (что в случае RHEL и производных), постоянно перемещает страницы памяти между доменами на основе пары эвристик. Необходимо использовать специальные потоковые распределители памяти или функции в libnuma (или вообще отключить балансировщик).