Написание конвейерного оптимизированного кода C AVX в VS 2017 - PullRequest
0 голосов
/ 27 августа 2018

Я пытаюсь написать код C, который маскирует задержку процессора при использовании конвейерной обработки. Вот выдержка:

__m256  v256f_rslt_0 = _mm256_loadu_ps(&ch_results_8[pos + (0 * FLOATS_IN_M256)]);
__m256  v256f_rslt_1 = _mm256_loadu_ps(&ch_results_8[pos + (1 * FLOATS_IN_M256)]);
__m256  v256f_rslt_2 = _mm256_loadu_ps(&ch_results_8[pos + (2 * FLOATS_IN_M256)]);
__m256  v256f_rslt_3 = _mm256_loadu_ps(&ch_results_8[pos + (3 * FLOATS_IN_M256)]);

__m256  v256f_scale_0 = _mm256_loadu_ps(&cl_8[pos + (0 * FLOATS_IN_M256)]);
__m256  v256f_scale_1 = _mm256_loadu_ps(&cl_8[pos + (1 * FLOATS_IN_M256)]);
__m256  v256f_scale_2 = _mm256_loadu_ps(&cl_8[pos + (2 * FLOATS_IN_M256)]);
__m256  v256f_scale_3 = _mm256_loadu_ps(&cl_8[pos + (3 * FLOATS_IN_M256)]);

v256f_rslt_0 = _mm256_max_ps(v256f_rslt_0, v256f_c_zero);
v256f_rslt_1 = _mm256_max_ps(v256f_rslt_1, v256f_c_zero);
v256f_rslt_2 = _mm256_max_ps(v256f_rslt_2, v256f_c_zero);
v256f_rslt_3 = _mm256_max_ps(v256f_rslt_3, v256f_c_zero);

v256f_rslt_0 = _mm256_mul_ps(v256f_rslt_0, v256f_scale_0);
v256f_rslt_1 = _mm256_mul_ps(v256f_rslt_1, v256f_scale_1);
v256f_rslt_2 = _mm256_mul_ps(v256f_rslt_2, v256f_scale_2);
v256f_rslt_3 = _mm256_mul_ps(v256f_rslt_3, v256f_scale_3);

Есть 5 математических операций * 4; 2 показаны.

Однако компилятор уничтожает конвейерную обработку. Вот часть ASM:

vmaxps  ymm2, ymm0, ymm10
vmulps  ymm0, ymm2, YMMWORD PTR [r9+rax-96]
vminps  ymm2, ymm0, ymm7
vmovups ymm0, YMMWORD PTR [rax-64]
vmulps  ymm6, ymm3, ymm8
vsubps  ymm3, ymm7, ymm2

vmaxps  ymm2, ymm0, ymm10
vmulps  ymm0, ymm2, YMMWORD PTR [r9+rax-64]
vminps  ymm2, ymm0, ymm7
vmovups ymm0, YMMWORD PTR [rax-160]
vmulps  ymm5, ymm3, ymm8
vsubps  ymm3, ymm7, ymm2

Компилятор четко сгруппировал код в 4 блока, что означает максимальную задержку.

Оптимизация компилятора: /O2 /Oi /Ot /GL Оптимизация компоновщика: /OPT:REF /OPT:ICF /LTCG:incremental

Есть ли способ предотвратить изменение порядка команд компилятором и, таким образом, сохранить конвейерный исходный код?

1 Ответ

0 голосов
/ 27 августа 2018

Программная конвейеризация в таком небольшом масштабе, как правило, не требуется для процессоров с неупорядоченным выполнением, если вы используете несколько аккумуляторов, поэтому для процессора есть некоторая ILP.

Современные x86-процессоры на удивление устойчивы в отношении планирования небольших команд, поскольку теперь кэши uop в основном устраняют проблемы внешнего декодирования / выравнивания. (Но положение инструкции по отношению к 32-байтовым границам по-прежнему влияет на кэш UOP, что может иметь значение, если у вас узкое место в интерфейсе.)

Внутренние узкие места из-за планирования команд встречаются редко, пока вы не доберетесь до гораздо более длинных цепочек dep, больших, чем размер RS: См. Понимание влияния lfence на цикл с двумя длинными цепочками зависимостей для увеличения длины для получения подробной информации о том, как современные процессоры обрабатывают несколько длинных цепочек развертывания, и каковы ограничения для нахождения ILP.

Единственный центральный процессор, который может выполнять этот код AVX, это Xeon Phi первого поколения (Knight's Corner), и вы обычно захотите использовать его вариант AVX512 вместо AVX2.


Согласился, что это планирование команд, вероятно, хуже, чем порядок, который вы использовали в источнике.

В больших масштабах или если вы обнаружите, что даже в этом масштабе ручное планирование инструкций (например, путем редактирования asm, сгенерированного компилятором) помогает повысить производительность, попробуйте использовать лучший компилятор.

gcc, clang и ICC могут компилировать встроенные функции, поэтому вы не застряли с MSVC.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...