Исполнительное резюме: Как можно указать в своем коде, что OpenMP должен использовать только потоки для REAL-ядер, т.е. Не считать гиперпотоки?
Подробный анализ. На протяжении многих лет я в свое свободное время кодировал SW-only, рендеринг с открытым исходным кодом (растеризатор/raytracer). Код GPL и двоичные файлы Windows доступны здесь: https://www.thanassis.space/renderer.html Он компилируется и работает под Windows, Linux, OS/X и BSD.
В прошлом месяце я представил режим raytracing - и качество сгенерированных снимков было ракетами. К сожалению, raytracing на порядок медленнее, чем растрирование. Чтобы увеличить скорость, так же, как и для растеризаторов, я добавил поддержку OpenMP (и TBB) в raytracer - чтобы легко использовать дополнительные ядра процессора. Растрирование и растрирование легко поддаются потоковой обработке (работа на треугольник - работа на пиксель).
В домашних условиях, с моим Core2Duo, второе ядро помогало всем режимам - как режимы растрирования, так и raytracing получили ускорение, которое находится между 1,85x и 1,9x.
Проблема: Естественно, мне было любопытно видеть верхнюю производительность процессора (я также "играю" с графическими процессорами, предварительный Порт CUDA), поэтому я хотел получить прочную базу для сравнения. Я дал код моему хорошему другу, у которого есть доступ к машине "зверя" с 16-ядерным супер процессором Intel стоимостью 1500 $.
Он запускает его в "самом тяжелом" режиме, режиме raytracer...
... и он получает одну пятую скорость моего Core2Duo (!)
Гасп - ужас. Что сейчас произошло?
Мы начали пробовать разные модификации, патчи... и в итоге мы это поняли.
Используя переменную окружения OMP_NUM_THREADS, можно контролировать количество порожденных потоков OpenMP. По мере увеличения количества потоков от 1 до 8 скорость возрастала (близка к линейному увеличению). В тот момент, когда мы пересекли 8, скорость начала уменьшаться, пока она не опустилась до пятой скорости моего Core2Duo, когда использовались все 16 ядер!
Почему 8?
Потому что 8 было числом реальных ядер. Остальные 8 были... гиперпотоками!
Теория: Теперь это было для меня новостью - я видел, что гиперпоточность помогла много (до 25%) в других алгоритмах, так что это было неожиданно. По-видимому, несмотря на то, что у каждого гиперпотокового ядра есть свои собственные регистры (и SSE-модуль?), Raytracer не мог использовать дополнительную вычислительную мощность. Что заставило меня думать...
Вероятно, это не перерабатывающая мощность, которая голодает - это пропускная способность памяти.
В raytracer используется структура данных иерархии ограниченных томов, чтобы ускорить пересечения лучей треугольника. Если используются гиперпотоковые ядра, то каждый из "логических ядер" в паре пытается читать из разных мест в этой структуре данных (т.е. В памяти) - и кэширование ЦП (локально для каждой пары) полностью разрушено. По крайней мере, моя теория - любые предложения приветствуются.
Итак, вопрос: OpenMP обнаруживает количество "ядер" и порождает потоки, чтобы соответствовать им, то есть включает в себя гиперпотоки "ядра" в расчете. В моем случае это, по-видимому, приводит к катастрофическим результатам, по скорости. Кто-нибудь знает, как использовать OpenMP API (если возможно, переносимо) только для создания потоков для REAL-ядер, а не для гиперпотоков?
P.S. Код открыт (GPL) и доступен по ссылке выше, не стесняйтесь воспроизводить на вашей собственной машине - я предполагаю, что это произойдет во всех гиперпотоковых процессорах.
P.P.S. Извините за длину сообщения, я думал, что это был образовательный опыт и хотел поделиться.