我把你的代码放到一个测试工具中,编译它clang -O3 并计时。我还实现了这两个例程的更快版本,将水平添加移出循环:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/time.h> // gettimeofday
#include <immintrin.h>
static void sse(const float *m_Array1, const float *m_Array2, const float *m_Array3, size_t n, float *Final1, float *Final2, float *Final3)
{
*Final1 = *Final2 = *Final3 = 0.0f;
for (int k = 0; k < n; k += 4)
{
__m128 g1 = _mm_load_ps( m_Array1 + k );
__m128 g2 = _mm_load_ps( m_Array2 + k );
__m128 g3 = _mm_load_ps( m_Array3 + k );
__m128 g1g3 = _mm_mul_ps( g1, g3 );
__m128 g2g3 = _mm_mul_ps( g2, g3 );
__m128 a1 = _mm_mul_ps( g1g3, g1g3 );
__m128 a2 = _mm_mul_ps( g2g3, g2g3 );
__m128 a3 = _mm_mul_ps( g1g3, g2g3 );
// horizontal add
{
a1 = _mm_hadd_ps( a1, a1 );
a1 = _mm_hadd_ps( a1, a1 );
*Final1 += _mm_cvtss_f32( a1 );
a2 = _mm_hadd_ps( a2, a2 );
a2 = _mm_hadd_ps( a2, a2 );
*Final2 += _mm_cvtss_f32( a2 );
a3 = _mm_hadd_ps( a3, a3 );
a3 = _mm_hadd_ps( a3, a3 );
*Final3 += _mm_cvtss_f32( a3 );
}
}
}
static void sse_fast(const float *m_Array1, const float *m_Array2, const float *m_Array3, size_t n, float *Final1, float *Final2, float *Final3)
{
*Final1 = *Final2 = *Final3 = 0.0f;
__m128 a1 = _mm_setzero_ps();
__m128 a2 = _mm_setzero_ps();
__m128 a3 = _mm_setzero_ps();
for (int k = 0; k < n; k += 4)
{
__m128 g1 = _mm_load_ps( m_Array1 + k );
__m128 g2 = _mm_load_ps( m_Array2 + k );
__m128 g3 = _mm_load_ps( m_Array3 + k );
__m128 g1g3 = _mm_mul_ps( g1, g3 );
__m128 g2g3 = _mm_mul_ps( g2, g3 );
a1 = _mm_add_ps(a1, _mm_mul_ps( g1g3, g1g3 ));
a2 = _mm_add_ps(a2, _mm_mul_ps( g2g3, g2g3 ));
a3 = _mm_add_ps(a3, _mm_mul_ps( g1g3, g2g3 ));
}
// horizontal add
a1 = _mm_hadd_ps( a1, a1 );
a1 = _mm_hadd_ps( a1, a1 );
*Final1 += _mm_cvtss_f32( a1 );
a2 = _mm_hadd_ps( a2, a2 );
a2 = _mm_hadd_ps( a2, a2 );
*Final2 += _mm_cvtss_f32( a2 );
a3 = _mm_hadd_ps( a3, a3 );
a3 = _mm_hadd_ps( a3, a3 );
*Final3 += _mm_cvtss_f32( a3 );
}
static void avx(const float *m_Array1, const float *m_Array2, const float *m_Array3, size_t n, float *Final1, float *Final2, float *Final3)
{
*Final1 = *Final2 = *Final3 = 0.0f;
for (int k = 0; k < n; k += 8 )
{
__m256 g1 = _mm256_load_ps( m_Array1 + k );
__m256 g2 = _mm256_load_ps( m_Array2 + k );
__m256 g3 = _mm256_load_ps( m_Array3 + k );
__m256 g1g3 = _mm256_mul_ps( g1, g3 );
__m256 g2g3 = _mm256_mul_ps( g2, g3 );
__m256 a1 = _mm256_mul_ps( g1g3, g1g3 );
__m256 a2 = _mm256_mul_ps( g2g3, g2g3 );
__m256 a3 = _mm256_mul_ps( g1g3, g2g3 );
// horizontal add1
{
__m256 t1 = _mm256_hadd_ps( a1, a1 );
__m256 t2 = _mm256_hadd_ps( t1, t1 );
__m128 t3 = _mm256_extractf128_ps( t2, 1 );
__m128 t4 = _mm_add_ss( _mm256_castps256_ps128( t2 ), t3 );
*Final1 += _mm_cvtss_f32( t4 );
}
// horizontal add2
{
__m256 t1 = _mm256_hadd_ps( a2, a2 );
__m256 t2 = _mm256_hadd_ps( t1, t1 );
__m128 t3 = _mm256_extractf128_ps( t2, 1 );
__m128 t4 = _mm_add_ss( _mm256_castps256_ps128( t2 ), t3 );
*Final2 += _mm_cvtss_f32( t4 );
}
// horizontal add3
{
__m256 t1 = _mm256_hadd_ps( a3, a3 );
__m256 t2 = _mm256_hadd_ps( t1, t1 );
__m128 t3 = _mm256_extractf128_ps( t2, 1 );
__m128 t4 = _mm_add_ss( _mm256_castps256_ps128( t2 ), t3 );
*Final3 += _mm_cvtss_f32( t4 );
}
}
}
static void avx_fast(const float *m_Array1, const float *m_Array2, const float *m_Array3, size_t n, float *Final1, float *Final2, float *Final3)
{
*Final1 = *Final2 = *Final3 = 0.0f;
__m256 a1 = _mm256_setzero_ps();
__m256 a2 = _mm256_setzero_ps();
__m256 a3 = _mm256_setzero_ps();
for (int k = 0; k < n; k += 8 )
{
__m256 g1 = _mm256_load_ps( m_Array1 + k );
__m256 g2 = _mm256_load_ps( m_Array2 + k );
__m256 g3 = _mm256_load_ps( m_Array3 + k );
__m256 g1g3 = _mm256_mul_ps( g1, g3 );
__m256 g2g3 = _mm256_mul_ps( g2, g3 );
a1 = _mm256_add_ps(a1, _mm256_mul_ps( g1g3, g1g3 ));
a2 = _mm256_add_ps(a2, _mm256_mul_ps( g2g3, g2g3 ));
a3 = _mm256_add_ps(a3, _mm256_mul_ps( g1g3, g2g3 ));
}
// horizontal add1
{
__m256 t1 = _mm256_hadd_ps( a1, a1 );
__m256 t2 = _mm256_hadd_ps( t1, t1 );
__m128 t3 = _mm256_extractf128_ps( t2, 1 );
__m128 t4 = _mm_add_ss( _mm256_castps256_ps128( t2 ), t3 );
*Final1 += _mm_cvtss_f32( t4 );
}
// horizontal add2
{
__m256 t1 = _mm256_hadd_ps( a2, a2 );
__m256 t2 = _mm256_hadd_ps( t1, t1 );
__m128 t3 = _mm256_extractf128_ps( t2, 1 );
__m128 t4 = _mm_add_ss( _mm256_castps256_ps128( t2 ), t3 );
*Final2 += _mm_cvtss_f32( t4 );
}
// horizontal add3
{
__m256 t1 = _mm256_hadd_ps( a3, a3 );
__m256 t2 = _mm256_hadd_ps( t1, t1 );
__m128 t3 = _mm256_extractf128_ps( t2, 1 );
__m128 t4 = _mm_add_ss( _mm256_castps256_ps128( t2 ), t3 );
*Final3 += _mm_cvtss_f32( t4 );
}
}
int main(int argc, char *argv[])
{
size_t n = 4096;
if (argc > 1) n = atoi(argv[1]);
float *in_1 = valloc(n * sizeof(in_1[0]));
float *in_2 = valloc(n * sizeof(in_2[0]));
float *in_3 = valloc(n * sizeof(in_3[0]));
float out_1, out_2, out_3;
struct timeval t0, t1;
double t_ms;
for (int i = 0; i < n; ++i)
{
in_1[i] = (float)rand() / (float)(RAND_MAX / 2);
in_2[i] = (float)rand() / (float)(RAND_MAX / 2);
in_3[i] = (float)rand() / (float)(RAND_MAX / 2);
}
sse(in_1, in_2, in_3, n, &out_1, &out_2, &out_3);
printf("sse : %g, %g, %g\n", out_1, out_2, out_3);
sse_fast(in_1, in_2, in_3, n, &out_1, &out_2, &out_3);
printf("sse_fast: %g, %g, %g\n", out_1, out_2, out_3);
avx(in_1, in_2, in_3, n, &out_1, &out_2, &out_3);
printf("avx : %g, %g, %g\n", out_1, out_2, out_3);
avx_fast(in_1, in_2, in_3, n, &out_1, &out_2, &out_3);
printf("avx_fast: %g, %g, %g\n", out_1, out_2, out_3);
gettimeofday(&t0, NULL);
for (int k = 0; k < 100; ++k) sse(in_1, in_2, in_3, n, &out_1, &out_2, &out_3);
gettimeofday(&t1, NULL);
t_ms = ((double)(t1.tv_sec - t0.tv_sec) + (double)(t1.tv_usec - t0.tv_usec) * 1.0e-6) * 1.0e3;
printf("sse : %g, %g, %g, %g ms\n", out_1, out_2, out_3, t_ms);
gettimeofday(&t0, NULL);
for (int k = 0; k < 100; ++k) sse_fast(in_1, in_2, in_3, n, &out_1, &out_2, &out_3);
gettimeofday(&t1, NULL);
t_ms = ((double)(t1.tv_sec - t0.tv_sec) + (double)(t1.tv_usec - t0.tv_usec) * 1.0e-6) * 1.0e3;
printf("sse_fast: %g, %g, %g, %g ms\n", out_1, out_2, out_3, t_ms);
gettimeofday(&t0, NULL);
for (int k = 0; k < 100; ++k) avx(in_1, in_2, in_3, n, &out_1, &out_2, &out_3);
gettimeofday(&t1, NULL);
t_ms = ((double)(t1.tv_sec - t0.tv_sec) + (double)(t1.tv_usec - t0.tv_usec) * 1.0e-6) * 1.0e3;
printf("avx : %g, %g, %g, %g ms\n", out_1, out_2, out_3, t_ms);
gettimeofday(&t0, NULL);
for (int k = 0; k < 100; ++k) avx_fast(in_1, in_2, in_3, n, &out_1, &out_2, &out_3);
gettimeofday(&t1, NULL);
t_ms = ((double)(t1.tv_sec - t0.tv_sec) + (double)(t1.tv_usec - t0.tv_usec) * 1.0e-6) * 1.0e3;
printf("avx_fast: %g, %g, %g, %g ms\n", out_1, out_2, out_3, t_ms);
return 0;
}
在我的 2.6 GHz Haswell (MacBook Pro) 上的结果是:
sse : 0.439 ms
sse_fast: 0.153 ms
avx : 0.309 ms
avx_fast: 0.085 ms
因此,对于原始实现和优化实现而言,AVX 版本确实似乎比 SSE 版本更快。优化后的实现速度明显快于原始版本,但幅度更大。
我只能猜测您的编译器没有为 AVX 生成非常好的代码(或者您可能忘记启用编译器优化?),或者您的基准测试方法可能存在问题。