2016-09-21 1 views
1

Я пытаюсь использовать встроенные функции Intel SIMD для ускорения программы запроса-ответа. Предположим, что query_cnt зависит от ввода, но всегда меньше, чем число регистров SIMD (т. Е. Для их хранения достаточно регистров SIMD). Поскольку запросы - это горячие данные в моем приложении, вместо того, чтобы загружать их каждый раз, когда это необходимо, я могу сначала загрузить их и всегда держать их в регистре?Как сохранить зависящие от входа горячие данные в регистрах при использовании SIMD-функций

Предположим, что запрошены float и AVX256 поддерживается. Теперь я должен использовать что-то вроде:

std::vector<__m256> vec_queries(query_cnt/8); 
for (int i = 0; i < query_cnt/8; ++i) { 
    vec_queries[i] = _mm256_loadu_ps((float const *)(curr_query_ptr)); 
    curr_query_ptr += 8; 
} 

Я знаю, что это не очень хорошая практика, так как есть потенциал накладных расходов загрузки/хранения, но, по крайней мере, есть небольшая вероятность, что vec_queries[i] могут быть оптимизированы таким образом, чтобы они могли быть хранится в регистрах, но я все еще думаю, что это не очень хорошо.

Любые лучшие идеи?

+0

Вы обрабатываете несколько запросов в цикле, который ничего не делает? Если нет, ваши данные не будут оставаться в регистрах, когда вы перейдете к следующему запросу. Или вы говорите, что думаете, что может стоить использовать глобальные переменные регистра для запросов? GNU C может это сделать: '__m256 vec_query0 asm (" ymm0 ");' должен быть правильным синтаксисом IIRC. –

+0

Вы посмотрели фактическое asm, чтобы убедиться, что ваши векторы * не * хранятся в регистрах? Если вам повезет, компилятор может оптимизировать большую часть служебных расходов динамического распределения std :: vector.Если нет, попробуйте использовать массив фиксированного размера (поскольку у вас низкая верхняя граница по его размеру). –

+0

@PeterCordes Спасибо за ваш совет. Я не рассматривал фактический asm, но ваше предположение, что использование массива фиксированного размера может быть хорошим вариантом, так что я могу использовать что-то вроде '__m256 vec_query0 asm (" ymm0 ")' для привязки каждого элемента массива к регистру , но если я это сделаю, будут ли некоторые регистры заняты фиксированными элементами, которые могут привести к штрафу за производительность? – MarZzz

ответ

0

Из примера кода, который вы опубликовали, похоже, что вы просто используете memcpy переменной длины. В зависимости от того, что делает компилятор, и окружающего кода, вы можете получить лучшие результаты только от звонка memcpy. например для выровненных копий с размером, кратным 16B, точка безубыточности между векторным контуром и rep movsb может быть как минимум 128 байтов на Intel Haswell. Ознакомьтесь с руководством по оптимизации Intel для некоторых заметок о внедрении на memcpy и диаграмме размера против циклов для пары разных стратегий. (Ссылки в теге ).

Вы не сказали, какой процессор, так что я просто предполагаю недавнюю Intel.

Я думаю, вы слишком беспокоитесь о регистрах. Нагрузки, попавшие в кеш L1, чрезвычайно дешевы. Haswell (и Skylake) может выполнять две __m256 нагрузки за такт (и магазин в том же цикле). До этого Sandybridge/IvyBridge может выполнять две операции памяти за такт, при этом максимум одного из них является хранилищем. Или в идеальных условиях (256 бит нагрузки/хранилища) они могут управлять 2x 16B, загруженными и 1x 16B, хранящимися за такт. Таким образом, загрузка/хранение векторов 256b дороже, чем у Haswell, но все же очень дешево, если они выровнены и горят в кеше L1.

Я упомянул в комментариях, что GNU C global register variables может быть возможностью, но в основном в смысле «это технически возможно в теории». Вероятно, вам не нужны несколько векторных регистров, предназначенных для этой цели на протяжении всего времени выполнения вашей программы (включая вызовы функций библиотеки, поэтому вам придется перекомпилировать их).

В действительности просто убедитесь, что компилятор может встроить (или хотя бы видеть при оптимизации) определения для каждой функции, которую вы используете внутри любых важных циклов. Таким образом, он может избежать необходимости проливать/перезаписывать векторные регистры через вызовы функций (так как ABI Windows и System V x86-64 не имеют регистров YMM (__m256), сохраненных в кодах).

См. Agner Fog's microarch pdf, чтобы узнать больше о микроархитектурных деталях современных процессоров, по крайней мере, о деталях, которые можно измерить экспериментально и настроиться.

+0

Я попробовал 'memcpy' между' __m256 a' и 'float b [8]' для подражания '_mm256_loadu_ps' и' _mm256_storeu_ps' и нашел, что это действительно сработало. Сначала я думал, что встроенный тип данных SIMD, такой как '__m256' или' __m256i', является специальным типом данных, который может иметь больше шансов храниться в реестрах. Теперь я передумал, что они, похоже, не являются особыми по сравнению с их коллегами, такими как 'float' или' int', но с большим количеством ограничений выравнивания, это правильно? Если это так, если я заменил все SIMD 'load/stores' на' memcpy', какова может быть потенциальная разница в производительности? – MarZzz

+0

@MarZzz: Да, они очень похожи на скалярные типы 'float' или' int'. Если вы собираетесь использовать '__m256' (или' __m256i') для большего количества вычислений, используйте встроенные функции загрузки. (или использовать простое назначение из другого '__m256'). Если вы хотите перемещать кучу данных, используйте memcpy. Вы не использовали бы memcpy для назначения 'array [i]' 'float tmp', поэтому не делайте этого для типов SIMD. Он * вероятно * оптимизирует прочь, но он менее читабельен и, конечно же, не помогает компилятору. –

+0

Если вам интересно, как это выглядит, посмотрите на выход компилятора. Гораздо проще отсортировать код, который сгенерирован компилятором, а не писать собственную сборку с нуля, поэтому вам не нужно действительно знать asm, чтобы попробовать это. Поместите некоторый код на http://gcc.godbolt.org/, чтобы получить [красиво отформатированное представление] (http://stackoverflow.com/questions/38552116/how-to-remove-noise-from-gcc-clang- assembly-output) asm (с дополнительной подсветкой цвета, чтобы показать, какая строка источника создала линию asm). –

Смежные вопросы