AVX(2) data load
Граждане, что-то вот отчаялся понять, отчего может быть так что
Вот так - хорошо, профайлер в этом месте особых тормозов не показывает:
__m128 p0 = _mm_loadu_ps(source);
__m128 p1 = _mm_loadu_ps(source + 4);
__m128 p2 = _mm_loadu_ps(source + 8);
А вот так - нехорошо:
__m256 i0008 = _mm256_loadu_ps(fsrcstart3);
__m256 i0915 = _mm256_loadu_ps(fsrcstart3 + 8);
__m256 i1623 = _mm256_loadu_ps(fsrcstart3 + 16);
То есть профайлер кажет в этом месте ~3 лишние секунды на загрузку (тех же самых) данных, общим объемом в ~1.2 гигапикселя (28 файлов по 40 Mpix). Это, конечно, 0.1 sec CPU time на файл, исполняется впараллель во все ядра, т.е. wall clock - ну может 20 миллисекунд лишних на файл, но обидно ж.
Пробовал совать префетч. И перед фетчем (для текущих данных) и после него (для данных следующего цикла) - толку мало. Попробую, конечно, еще размазать загрузку другими командами.
Или VTune Amplifier в таких делах доверять не стоит?
P.S. А то же самое (ну по сути), но с целыми данными, где целая пачка _mm_loadu_si128 - работает вроде неплохо. Ну с той поправкой, правда, что данных вдвое меньше (int16 вместо float32) и в тестовом наборе у меня таких файлов немного.
P.P.S. И с еще одним типом данных, где один _mm256_loadu_ps на цикл - AVX заметно быстрее SSE.
Comments
А ты учитываешь все
А ты учитываешь все спецэффекты от AVX2?
А какие именно спецэффекты то
А какие именно спецэффекты то?
_mm256_zeroall() по выходу из AVX-кода - делаю.
Блок включается какие-то
Блок включается какие-то сотни тысяч тактов. потом довольно быстро (но не рсазу) выключается. Плюс после включения блока процессор снижает частоту если был в турбобусте. Причём, как я понимаю, существенно (но зависит от кокнретной модели).
Не, ну вот смотри, есть два
Не, ну вот смотри, есть два набора файлов, примерно одинакового размера, 24 linear DNG в плавучке, 3 компонента на пиксель и еще 24 - таких же, но 4 компонента на пиксель. Отличаются деталями (fp16/fp24/fp32, черный вычтен/не вычтен, есть scale в 0-1 или нету), это я из двух исходников наклонировал.
И вот значит float4 во внутреннее представление (тоже float4) - AVX быстрее. float3->float4 - AVX медленнее. Ну ладно, я бы даже и согласился бы (там float3->float4 не такой простой код), но
а) Amplifier показывает тормоза на чтении данных (и вроде как не должен провираться)
б) int3 -> float4 - работает нормально, хотя там код еще сложнее и регистров надо еще больше (но, понятно входных данных самих меньше - и может быть потому оно не проявляется)
P.S. Оно там все слезы на
P.S. Оно там все слезы на фоне декодирования float-данных, но разобраться и понять очень бы хотелось конечно
А cache line splits amplifier
А cache line splits amplifier не показывает во втором случае? (плюс выше уже написали про включение AVX и частоту.)
Я не очень понимаю где/как
Я не очень понимаю где/как это там смотреть.
Вот зато там есть memory access профайлинг - и таки да, интересные мне инструкции 'memory bound' (еще, конечно, гистограмма сильно тормозит - но этим не удивить)
А SSE-случай - не memory
А SSE-случай - не memory bound.
Это на Haswell исполняется?
Это на Haswell исполняется? split loads - в backend/cache/L1 колонке.
Kaby Lake.
Kaby Lake.
В колонке - при каком анализе? Я все стандартные попробовал - нет колонки, надо вероятно настраивать.
Амплифаейр - свежий. 2018-й
general exploration analysis
general exploration analysis/bottom-up view/general exploration viewpoint: backend bound/memory bound/L1 bound.
Еще advisor/roofline может показать L1/L2/L3/DRAM throughput для обоих циклов.
Ага, я до этого места дошел.
Ага, я до этого места дошел.
L1 bound и DRAM bound (красным и большие проценты)
Почему то же самое не bound для примерно такого же 128-битного кода - ну хрен пойми.
L1 bound тоже разворачивается
L1 bound тоже разворачивается на 6 показателей.
Ага.
Ага.
FB full (и немного 4k aliasing, но тут блин вообще ничего не сделаешь, какие дали данные снаружи, такие и есть)
Что неудивительно, фигачим то гигабайтами подряд.
Увы, ничего тут не поможет, кажется, MOVNTDQA для невыровненых данных нету, выровнять входные я не могу, значит вот тут такая неустроенность имеется: читать мимо кэша не выйдет.
Кстати, знакомый, который
Кстати, знакомый, который много профилирует с помощью PMC, недоумевает как Амплифаер показывает тормоза на загрузке. Он говорит, что PMC не позволяют такой результат получить достоверно.
Может статистически смотрит
Может статистически смотрит (а куды мы попали то)?
Это называется sampling, и он
Это называется sampling, и он biased довольно здорово, причём biased по-разному в зависимости от команд и их последовательностей и как они поделились на пачки при сабмите в ядро процессора. Это большое исскуство читать вывод семплинг-профайлера.
Ну я вижу тотальное
Ну я вижу тотальное замедление (в 2 секунды CPU time на 24 примера, т.е. ~10msec wall time на файл) - и оставлю SSE вариант пока.
Но вообще вот результаты профайлинга amplifier у меня особых вопросов не вызывают как правило, похоже на правду ж все
Ну оно конечно при просмотре
Ну оно конечно при просмотре ассемблера - часто показывает тормоза на следующей инструкции, а не на той что тормозит.
Но мы же тоже понимаем что на паре
movups xmm0, [addr]
add rdx, 0x10
Если и показывает время на сложении - это оно на инструкцию промахнулось.
Да, и давай я всё же уточню:
Да, и давай я всё же уточню: AVX-команд вдвое меньше чем SSE? Т.е. объём данных прокачивается строго одинаковый и разница в скорости между 2*N командами SSE (по 128 бит) и N командами AVX (по 256 бит)?
load/store - вдвое меньше,
load/store - вдвое меньше, естественно.
Арифметики плавучей - ну тоже (4 операции на юнит из 1/2 пикселей соответственно)
reordering: SSE 7 команд на 4 выходных пикселя, AVX: 12 команд на 8 пикселей. Потому что float3->float4 не вполне тривиальная операция.