У меня есть многократно добавленное ядро внутри моего приложения, и я хочу увеличить его производительность.
Я использую Intel Core i7-960 (тактовые частоты 3,2 ГГц) и уже вручную реализовал ядро с использованием встроенных функций SSE следующим образом:
for(int i=0; i<iterations; i+=4) {
y1 = _mm_set_ss(output[i]);
y2 = _mm_set_ss(output[i+1]);
y3 = _mm_set_ss(output[i+2]);
y4 = _mm_set_ss(output[i+3]);
for(k=0; k<ksize; k++){
for(l=0; l<ksize; l++){
w = _mm_set_ss(weight[i+k+l]);
x1 = _mm_set_ss(input[i+k+l]);
y1 = _mm_add_ss(y1,_mm_mul_ss(w,x1));
…
x4 = _mm_set_ss(input[i+k+l+3]);
y4 = _mm_add_ss(y4,_mm_mul_ss(w,x4));
}
}
_mm_store_ss(&output[i],y1);
_mm_store_ss(&output[i+1],y2);
_mm_store_ss(&output[i+2],y3);
_mm_store_ss(&output[i+3],y4);
}
Я знаю, что могу использовать упакованные векторы fp для повышения производительности, и я уже сделал это успешно, но я хочу знать, почему единственный скалярный код не способен удовлетворить максимальную производительность процессора.
Производительность этого ядра на моей машине составляет ~ 1.6 FP операций за цикл, в то время как максимум будет 2 FP-операций за цикл (так как FP add + FP mul может выполняться параллельно).
Если я правильно изучил сгенерированный ассемблерный код, идеальное расписание будет выглядеть следующим образом, где инструкция mov
занимает 3 цикла, латентность переключения из домена загрузки в домен FP для зависимых инструкций принимает 2 циклов, умножение FP занимает 4 цикла, а добавление FP занимает 3 цикла. (Заметим, что зависимость от multiply → add не несет никакой задержки на переключение, поскольку операции принадлежат одному домену).
В соответствии с измеренной производительностью (~ 80% от максимальной теоретической производительности) есть накладные расходы на ~ 3 команды за 8 циклов.
Я пытаюсь либо:
- избавиться от этих накладных расходов или
- объясните, откуда оно взялось
Конечно, есть проблема с недостатками кэша и несогласованностью данных, которые могут увеличить латентность команд перемещения, но есть ли другие факторы, которые могли бы сыграть здесь роль? Как зарегистрироваться в киосках или что-то в этом роде?
Я надеюсь, что моя проблема понятна, заблаговременно за ваши ответы!
Обновление: сборка внутреннего цикла выглядит следующим образом:
...
Block 21:
movssl (%rsi,%rdi,4), %xmm4
movssl (%rcx,%rdi,4), %xmm0
movssl 0x4(%rcx,%rdi,4), %xmm1
movssl 0x8(%rcx,%rdi,4), %xmm2
movssl 0xc(%rcx,%rdi,4), %xmm3
inc %rdi
mulss %xmm4, %xmm0
cmp $0x32, %rdi
mulss %xmm4, %xmm1
mulss %xmm4, %xmm2
mulss %xmm3, %xmm4
addss %xmm0, %xmm5
addss %xmm1, %xmm6
addss %xmm2, %xmm7
addss %xmm4, %xmm8
jl 0x401b52 <Block 21>
...