Измерение производительности для простых операций с векторизованными массивами

Я оптимизирую простые операции с массивами, такие как

for (int i=0; i<cnt; i++) dst[i] = src1[i] * src2[i];

где cnt обычно составляет от 32 до 1024, что является типичным случаем в моем приложении. Я сравниваю Intel IPP, собственный векторизатор MSVC 2017, а также использую встроенные функции для AVX и AVX512 в MSVC. Я установил приоритет потока как критический, а маску сходства потоков — «1». Затем я использую QueryPerformanceCounter для расчета времени в начале и в конце «операции», которая состоит из запуска цикла миллионы раз. Работает на новом i9 с 10 ядрами.

Проблема в том, что результаты сильно различаются. Иногда оп таксе 3 секунды, иногда 4. Это делает результаты практически непригодными для использования. Есть идеи, что может быть не так? Значения в обрабатываемых массивах всегда одинаковы, массивы выровнены по границам 32 байта.


person Vojtěch Melda Meluzín    schedule 21.09.2018    source источник
comment
Что-то работает в фоновом режиме? Нравится антивирус? Также попробуйте установить привязку к другому ядру. Простой код, подобный этому, может иметь некоторую разницу во времени, но небольшую, не около 25%   -  person Anty    schedule 21.09.2018
comment
Спасибо, Энти, но я так не думаю, тем более, что это очень быстрый процессор с большим количеством ядер.   -  person Vojtěch Melda Meluzín    schedule 21.09.2018
comment
Являются ли ваши массивы FP или целочисленными? Тестирование Agner Fog показывает, что vpmulld zmm имеет пропускную способность только 1 на 2 такта на SKX (т. е. он не использует FMA-устройство порта 5 для запуска своих 2 uops в соответствии с agner.org/optimize), поэтому при развертывании похоже, что AVX512 будет узким местом при той же пропускной способности, что и развернутый цикл AVX2 для int32_t. Однако AVX512 лучше подходит для любого другого типа.   -  person Peter Cordes    schedule 22.09.2018
comment
Теперь это все 32-битные числа с плавающей запятой, мне не нужна интегральная арифметика. Проблема здесь в том, что результаты слишком неточны. Я даже пытался __rdtsc получить временную метку процессора, но она каждый раз разная... Даже один и тот же алгоритм дает абсолютно разные результаты...   -  person Vojtěch Melda Meluzín    schedule 22.09.2018


Ответы (1)


Ваш ЦП или ОС, вероятно, ограничивают частоту ядра. Один раз он может работать на частоте 3,6 ГГц, а другой — на 2,8 ГГц. Есть способы предотвратить это с помощью Windows и/или вашего BIOS, но я не помню подробностей.

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

person ChipK    schedule 21.09.2018
comment
Дело в том, что алгоритмы на самом деле используют одни и те же буферы, поэтому ситуация для всех должна быть абсолютно одинаковой. Тем не менее, иногда в 2 раза быстрее, иногда наоборот... Я не думаю, что можно исправить частоту ядра, в прошлый раз, когда я проверял, турбобуст был полностью автоматическим, и только ОС могла им управлять. . - person Vojtěch Melda Meluzín; 21.09.2018
comment
Неважно, если условия одинаковы. Если процессор работает слишком сильно, он может автоматически блокировать различные ядра. То же самое и с ОС, если она считает, что ее фоновые задачи на других ядрах более необходимы, она может регулировать текущее ядро, на котором вы работаете, в один раз, а не в следующий. - person ChipK; 21.09.2018
comment
Это не будет привязано к памяти. OP повторяет (миллионы раз) крошечный цикл от 32 до 1024 элементов (неопределенного размера, но даже 8-байтовый double будет составлять всего 8 КБ * 3 = 24 КБ, что помещается в кэш L1d.) С достаточным развертыванием или чем-то еще, чтобы избегайте узких мест на интерфейсе, он должен работать с 1 хранилищем векторов за такт. @VojtěchMeldaMeluzín: Как именно вы рассчитываете время? Вы даете ему достаточно времени, чтобы разогнаться до максимального турбо? Вы можете отключить турбо и установить регулятор производительности. (Хотя ИДК, как в Windows.) Вы измеряете только время настенных часов, а не тактовые циклы ядра. - person Peter Cordes; 22.09.2018
comment
@VojtěchMeldaMeluzín: Возможно ли, что ваш цикл иногда охватывает 32-байтовую границу кода и работает на половинной скорости из-за эффектов uop-кэша? (Skylake-X имеет отключенный буфер циклов, поэтому крошечные неразвернутые циклы могут пострадать в зависимости от выравнивания кода.) Иногда один и тот же двоичный файл работает с разной скоростью или разница только между разными сборками? - person Peter Cordes; 22.09.2018
comment
Я использую числа с плавающей запятой, поэтому он определенно должен соответствовать кешу L1. Я также пытался поместить все в непрерывный блок в стеке, без разницы. Я использую QueryPerformanceTimer, и теперь я попытался __rdtsc получить статистику одной (или нескольких) итераций. Я запускаю весь OP 4 раза подряд, печатая статистику, такую ​​​​как среднее количество тактовых циклов и общее время, и 4 раза всегда почти идентичны, но в следующий раз, когда я запускаю приложение, время иногда даже отличается на 30%. . - person Vojtěch Melda Meluzín; 22.09.2018
comment
Я не уверен насчет границы кода в 32 байта (на самом деле я не знал об этой проблеме), но я предполагаю, что приложение всегда должно размещаться по одному и тому же адресу памяти, верно? Так что это не должно иметь значения, имхо. - person Vojtěch Melda Meluzín; 22.09.2018
comment
Питер прав. Я упустил из виду размер массива. Возможно, вам следует подумать о загрузке ознакомительной копии Vtune Amplifier. Это очень мощный инструмент для решения подобных проблем. - person ChipK; 24.09.2018
comment
Ваша проблема звучит так, будто ОС переносит данные с одного ядра на другое. Я видел проблему раньше. Можете ли вы попробовать тест, в котором вы размещаете данные на том же ядре, что и код? - person ChipK; 24.09.2018