FP-интенсивная производительность гиперпоточности на последних Xeon
Недавно мы приобрели двойную рабочую станцию Intel X5650 для запуска интенсивного моделирования с плавающей запятой под Ubuntu 10.04.
Каждый X5650 имеет 6 ядер, поэтому всего 12 ядер. Код тривиально параллелен, поэтому я выполняю его в основном с 12 потоками и наблюдаю примерно 1200% загрузки процессора через "top".
HyperThreading включен в BIOS, поэтому операционная система номинально видит 24 доступных ядра. Если я увеличу количество потоков до 24, лучшие сообщат об использовании процессора примерно на 2000% - однако, похоже, что фактическая производительность кода не увеличится на 20/12.
Мой вопрос - как HyperThreading на самом деле работает на Xeons последнего поколения? Выгодно ли интенсивному коду с плавающей точкой планировать более одного потока на ядро? Меняется ли ответ, если рабочий набор имеет порядок размера кэша по сравнению с несколькими разами больше или если выполняются существенные операции ввода-вывода (например, запись результатов моделирования на диск)?
Дополнительно - как мне интерпретировать процент использования процессора от "top", когда включена поддержка Hyperthreading?
2 ответа
С HT, ОС будет планировать 2 потока для каждого ядра одновременно. Степень использования, сообщаемая top, по сути, является просто средним числом потоков в "рабочем" состоянии за интервал выборки (обычно 1 секунда). Работающие потоки доступны для выполнения ЦП, но, возможно, они не выполняют много работы, например, если они в основном застопорены из-за промахов в кеше.
Когда поток заблокирован на реальном вводе / выводе - сети, диске и т. Д. - ОС отменит его от ядра и запланирует какой-либо другой готовый поток, поэтому HT не поможет.
HT пытается получить больше от использования математических исполнительных блоков, фактически не удваивая аппаратное обеспечение в ядре. Если один поток имеет достаточный параллелизм на уровне команд и не сильно пропускает кэш, то он в основном заполнит ресурсы ядра, а HT не поможет. Для тяжелых приложений FP с данными, которые не помещаются в кэш, HT все еще, вероятно, мало поможет, так как оба потока используют одни и те же блоки выполнения (математика SSE), и оба требуют больше, чем полный кэш - фактически это вероятно ранить, так как они будут бороться за кеш и побольше. Конечно, это зависит от того, что именно вы делаете и как выглядят ваши шаблоны доступа к данным.
HT в основном помогает в ветвящемся коде с нерегулярными и непредсказуемыми схемами доступа. Для FP-интенсивного кода вы часто можете добиться большего успеха с 1 потоком на ядро и тщательным проектированием ваших шаблонов доступа (например, хорошая блокировка данных).
Я разработал очень высокопроизводительный, смущающе параллельный код, который будет работать на максимально возможном количестве ядер. Первоначально он работал на 2-ядерном ноутбуке AMD, но когда я перешел на 2-ядерный ноутбук Intel +, улучшение производительности было незначительным: наличие процессора более позднего поколения, еще двух (HT) ядер и 670 МГц более высокая тактовая частота процессора могли просто не быть замеченным Когда я ограничил код двумя не-HT-потоками, неожиданное ускорение в случае двухъядерного процессора внезапно появилось, и я мог дышать легче.
Когда я изменил уровень оптимизации компилятора с 3 на 2 и, наконец, 1 гиперпоточность начала показывать свое обещание. Лучшие результаты были на уровне оптимизации 1 и были примерно на 50% лучше, чем в случае с 2 кодами без HT.
Я думаю, что случается так, что слишком хорошо написанный и оптимизированный код в максимальной степени использует ядро, поскольку в основном нет дополнительных доступных ресурсов для выполнения второго потока. Конечно, второй поток будет работать, но оба потока будут сталкиваться, когда им нужен один и тот же ресурс. Они будут делать это намного чаще из-за высокого уровня оптимизации.
Имея менее оптимизированный или плотный код, у потоков была возможность "чередовать" их доступ к ресурсам ядра в большей степени. Это привело к тому, что два потока работали со скоростью около 75% от скорости, при которой наиболее высокооптимизированный код работал бы на одном ядре. При суммировании менее оптимизированный код в двух потоках даст в 1,5 раза пропускную способность наиболее оптимизированного в одном.
Я развлекал идею написания кода, чтобы увидеть, какого уровня "чередования" ресурсов ядра можно достичь, и я предполагаю, что такой поток будет тратить половину своего выполнения внутреннего цикла в одном канале выполнения ЦП и половину в другом. Ожидаемый результат "будет" состоять в том, что один будет выполнять половину внутреннего цикла за другим для достижения наилучшего "чередования" ресурсов.