Вот очень простая реализация SSE3:
#include <emmintrin.h>
__m128d vsum = _mm_set1_pd(0.0);
for (int i = 0; i < n; i += 2)
{
__m128d v = _mm_load_pd(&a[i]);
vsum = _mm_add_pd(vsum, v);
}
vsum = _mm_hadd_pd(vsum, vsum);
double sum = _mm_cvtsd_f64(vsum0);
Вы можете развернуть цикл, чтобы повысить производительность, используя несколько аккумуляторов, чтобы скрыть задержку добавления FP (как предлагает @Mysticial). Разверните 3 или 4 раза с несколькими векторами «суммы» для узкого места при нагрузке и пропускной способности с добавлением FP (один или два за такт) вместо задержки с добавлением FP (один за 3 или 4 цикла):
__m128d vsum0 = _mm_setzero_pd();
__m128d vsum1 = _mm_setzero_pd();
for (int i = 0; i < n; i += 4)
{
__m128d v0 = _mm_load_pd(&a[i]);
__m128d v1 = _mm_load_pd(&a[i + 2]);
vsum0 = _mm_add_pd(vsum0, v0);
vsum1 = _mm_add_pd(vsum1, v1);
}
vsum0 = _mm_add_pd(vsum0, vsum1); // vertical ops down to one accumulator
vsum0 = _mm_hadd_pd(vsum0, vsum0); // horizontal add of the single register
double sum = _mm_cvtsd_f64(vsum0);
Обратите внимание, что массив a
предполагается выровненным по 16 байтам, а количество элементов n
предполагается кратным 2 (или 4, в случае развернутого цикла).
См. также Самый быстрый способ выполнить горизонтальную сумму векторов с плавающей запятой на x86 для альтернативных способов выполнения горизонтальной суммы вне цикла. Поддержка SSE3 не является полностью универсальной (особенно процессоры AMD стали поддерживать ее позже, чем Intel).
Кроме того, _mm_hadd_pd
обычно не самый быстрый способ даже на процессорах, которые его поддерживают, поэтому версия только для SSE2 не будет хуже на современных процессорах. Это вне цикла и в любом случае не имеет большого значения.
person
Paul R
schedule
01.10.2012