sse2 умножение с плавающей запятой

Я попытался перенести код из FANN Lib (нейронная сеть, написанная на C) в SSE2. Но производительность SSE2 ухудшилась по сравнению с обычным кодом. С моей реализацией SSE2 один запуск занимает 5,50 мин без 5,20 мин.

Как SSE2 может работать медленнее обычного? Может ли это быть из-за _mm_set_ps? Я использую компилятор Apple LLVM (XCode 4) для компиляции кода (все флаги расширения SSE включены, уровень оптимизации -Os).

Код без SSE2

                neuron_sum +=
                fann_mult(weights[i], neurons[i].value) +
                fann_mult(weights[i + 1], neurons[i + 1].value) +
                fann_mult(weights[i + 2], neurons[i + 2].value) +
                fann_mult(weights[i + 3], neurons[i + 3].value);

код SSE2

                __m128 a_line=_mm_loadu_ps(&weights[i]);
                __m128 b_line=_mm_set_ps(neurons[i+3].value,neurons[i+2].value,neurons[i+1].value,neurons[i].value);
                __m128 c_line=_mm_mul_ps(a_line, b_line);
                neuron_sum+=c_line[0]+c_line[1]+c_line[2]+c_line[3];

person martin s    schedule 26.03.2012    source источник
comment
Если вы посмотрите на сборку, должно быть довольно ясно, почему она работает медленнее. Вы, вероятно, будете поражены тем, во что компилируется _mm_set_ps. (Так что да, вы правы, подозревая _mm_set_ps.)   -  person Mysticial    schedule 27.03.2012
comment
извини. Это только определение. #define fann_mult(x,y) (x*y)   -  person martin s    schedule 27.03.2012
comment
Релевантно: stackoverflow.com/questions/4120681/   -  person kennytm    schedule 27.03.2012


Ответы (1)


Чтобы иметь шанс увидеть ускорение здесь, вам нужно сделать следующее:

  • убедитесь, что weights[i] выровнено по 16 байтам, а затем используйте _mm_load_ps вместо _mm_loadu_ps
  • реорганизуйте neurons[] так, чтобы это был SoA вместо AoS (а также с выравниванием по 16 байтам), а затем используйте _mm_load_ps для загрузки 4 значений за раз
  • переместите горизонтальную сумму из цикла (есть цикл, верно?) - просто сохраните 4 частичные суммы в векторе vneurom_sum, а затем выполните одну окончательную горизонтальную сумму на этом векторе после цикла

Даже в этом случае вы не увидите значительного ускорения, так как выполняете только одну арифметическую операцию для 2 загрузок и 1 сохранения. Поскольку большинство современных процессоров x86 имеют два скалярных FPU, в любом случае вы, вероятно, не приблизитесь к теоретическому 4-кратному ускорению для 128-битного SIMD с плавающей запятой, я бы не ожидал, скажем, 50% ускорения по сравнению со скалярным кодом.

person Paul R    schedule 26.03.2012
comment
спасибо за Ваш ответ. Да, есть петля, я думаю, будет сложно реорганизовать нейроны, потому что тогда мне придется переписывать всю структуру. Но идея с суммой горизонталь + вертикаль сработала хорошо и я смог улучшить показатели до 4.30м. - person martin s; 27.03.2012
comment
@user1293890 user1293890 - рад, что это помогло - постфактум оптимизация SIMD, такая как эта, может быть довольно сложной, если вы не готовы сделать серьезную операцию на соответствующих структурах данных. - person Paul R; 27.03.2012