Этот вопрос продолжается по моему вопросу здесь (по совету Мистика):
Производительность кода кода C
Продолжая мой вопрос, когда я использую упакованные инструкции вместо скалярных инструкций, код, использующий intrinsics, будет выглядеть очень похоже:
for(int i=0; i<size; i+=16) {
y1 = _mm_load_ps(output[i]);
…
y4 = _mm_load_ps(output[i+12]);
for(k=0; k<ksize; k++){
for(l=0; l<ksize; l++){
w = _mm_set_ps1(weight[i+k+l]);
x1 = _mm_load_ps(input[i+k+l]);
y1 = _mm_add_ps(y1,_mm_mul_ps(w,x1));
…
x4 = _mm_load_ps(input[i+k+l+12]);
y4 = _mm_add_ps(y4,_mm_mul_ps(w,x4));
}
}
_mm_store_ps(&output[i],y1);
…
_mm_store_ps(&output[i+12],y4);
}
Измеренная производительность этого ядра составляет около 5,6 операций FP за цикл, хотя я бы ожидал, что это будет ровно 4 раза производительность скалярной версии, то есть 4.1,6 = 6,4 FP ops за цикл.
Принимая во внимание ход весового коэффициента (спасибо, что указали это), график выглядит следующим образом:
Похоже, что расписание не изменяется, хотя после операции movss
есть дополнительная инструкция, которая перемещает значение скалярного веса в регистр XMM, а затем использует shufps
для копирования этого скалярного значения во всем векторе, Кажется, что вектор веса готов к использованию для mulps
во времени, принимая во внимание задержку переключения от нагрузки до домена с плавающей запятой, поэтому это не должно вызывать каких-либо дополнительных латентностей.
Команды movaps
(выравнивание, упакованное перемещение), addps
и mulps
, которые используются в этом ядре (с кодом сборки), имеют такую же задержку и пропускную способность, что и их скалярные версии, поэтому это не должно нести дополнительную задержку.
Есть ли у кого-нибудь идея, где этот дополнительный цикл на 8 циклов расходуется, если предположить, что максимальная производительность, которую может получить это ядро, составляет 6.4 FP ops за цикл и работает на 5.6 FP ops за такт?
Кстати, вот как выглядит фактическая сборка:
…
Block x:
movapsx (%rax,%rcx,4), %xmm0
movapsx 0x10(%rax,%rcx,4), %xmm1
movapsx 0x20(%rax,%rcx,4), %xmm2
movapsx 0x30(%rax,%rcx,4), %xmm3
movssl (%rdx,%rcx,4), %xmm4
inc %rcx
shufps $0x0, %xmm4, %xmm4 {fill weight vector}
cmp $0x32, %rcx
mulps %xmm4, %xmm0
mulps %xmm4, %xmm1
mulps %xmm4, %xmm2
mulps %xmm3, %xmm4
addps %xmm0, %xmm5
addps %xmm1, %xmm6
addps %xmm2, %xmm7
addps %xmm4, %xmm8
jl 0x401ad6 <Block x>
…