Optimization 计算大点积的最快方法是什么?

Optimization 计算大点积的最快方法是什么?,optimization,assembly,sse,avx,dot-product,Optimization,Assembly,Sse,Avx,Dot Product,考虑一下这个片段: double dot(double* a, double* b, int n) { double sum = 0; for (int i = 0; i < n; ++i) sum += a[i] * b[i]; return sum; } 如何使用intrinsic或汇编程序加速它 注: 您可以采用最新的体系结构,包括AVX扩展。 n是几百。 dot本身将在紧密循环中使用 下面是一个简单的SSE实现: #include "pmmintrin.h" __m

考虑一下这个片段:

double dot(double* a, double* b, int n) {
  double sum = 0;
  for (int i = 0; i < n; ++i) sum += a[i] * b[i];
  return sum;
}
如何使用intrinsic或汇编程序加速它

注:

您可以采用最新的体系结构,包括AVX扩展。 n是几百。 dot本身将在紧密循环中使用
下面是一个简单的SSE实现:

#include "pmmintrin.h"

__m128d vsum = _mm_set1_pd(0.0);
double sum = 0.0;
int k;

// process 2 elements per iteration
for (k = 0; k < n - 1; k += 2)
{
    __m128d va = _mm_loadu_pd(&a[k]);
    __m128d vb = _mm_loadu_pd(&b[k]);
    __m128d vs = _mm_mul_pd(va, vb);
    vsum = _mm_add_pd(vsum, vs);
}

// horizontal sum of 2 partial dot products
vsum = _mm_hadd_pd(vsum, vsum);
_mm_store_sd(&sum, vsum);

// clean up any remaining elements
for ( ; k < n; ++k)
{
    sum += a[k] * b[k];
}

下面是一个简单的SSE实现:

#include "pmmintrin.h"

__m128d vsum = _mm_set1_pd(0.0);
double sum = 0.0;
int k;

// process 2 elements per iteration
for (k = 0; k < n - 1; k += 2)
{
    __m128d va = _mm_loadu_pd(&a[k]);
    __m128d vb = _mm_loadu_pd(&b[k]);
    __m128d vs = _mm_mul_pd(va, vb);
    vsum = _mm_add_pd(vsum, vs);
}

// horizontal sum of 2 partial dot products
vsum = _mm_hadd_pd(vsum, vsum);
_mm_store_sd(&sum, vsum);

// clean up any remaining elements
for ( ; k < n; ++k)
{
    sum += a[k] * b[k];
}


我很惊讶你的编译器在这么小的函数上做得不好。你能展示你当前的输出程序集吗?这样我们就有了一个起点?如果你在一个超线程内核上,你能在两个线程之间分配工作吗?我不知道这是否能给你带来很多好处。你现在使用的是什么编译器和什么选项?你是在重复使用两个向量的点积吗?当你调用点时,向量在内存层次结构中的什么位置?我很惊讶你的编译器在这么小的函数上做得不好。你能展示你当前的输出程序集吗?这样我们就有了一个起点?如果你在一个超线程内核上,你能在两个线程之间分配工作吗?我不知道这是否能给你带来很多好处。你现在使用的是什么编译器和什么选项?你是在重复使用两个向量的点积吗?当你调用点时,向量在内存层次结构中的什么位置?很好的答案。如果您可以保证a和b是16字节对齐的,那么您可以使用_mm_load_pd而不是_mm_loadu_pd,这可能有助于提高性能,尤其是在旧的Nehalem之前的CPU上。我认为,即使在现代CPU上,至少在常春藤桥上,对齐也是重要的。自Nehalem以来的唯一区别是,在对齐内存上的加载速度几乎与loadu一样快,但在未对齐内存上的加载速度仍然慢得多。@raxman:没错,即使在Nehalem和更高版本的CPU上,也存在可测量的差异,但与在较旧的CPU上未对齐的加载/存储通常命中2倍相比,差异相对较小,对于像上面这样的琐碎操作,内存带宽可能是限制因素,它可能无关紧要。但是,是的,只要有可能,总是尝试使用16字节对齐的方式。hadd_pd不单独添加128位部分吗?必须有一个permute2f128vsum,vsum,1在两次添加之间切换和高部分吗?为什么工程师们不制作任何能够通过所有元素添加的avx指令?例如,_mm256_dp_ps制作单独的两个点积,因为它适用于r、g、b、a或x、y、z、0乘法?@huseyin:的确-看起来AVX实际上只是两个SSE执行单元栓接在一起-这对于大多数基本的SIMD操作来说都很好,但当您有数据加宽/缩小操作包时,请解压缩,或者需要在向量水平添加、对齐器等上水平操作,然后遇到问题,必须开始排列向量以使其工作,这会降低总体效率。答案很好。如果您可以保证a和b是16字节对齐的,那么您可以使用_mm_load_pd而不是_mm_loadu_pd,这可能有助于提高性能,尤其是在旧的Nehalem之前的CPU上。我认为,即使在现代CPU上,至少在常春藤桥上,对齐也是重要的。自Nehalem以来的唯一区别是,在对齐内存上的加载速度几乎与loadu一样快,但在未对齐内存上的加载速度仍然慢得多。@raxman:没错,即使在Nehalem和更高版本的CPU上,也存在可测量的差异,但与在较旧的CPU上未对齐的加载/存储通常命中2倍相比,差异相对较小,对于像上面这样的琐碎操作,内存带宽可能是限制因素,它可能无关紧要。但是,是的,只要有可能,总是尝试使用16字节对齐的方式。hadd_pd不单独添加128位部分吗?必须有一个permute2f128vsum,vsum,1在两次添加之间切换和高部分吗?为什么工程师们不制作任何能够通过所有元素添加的avx指令?例如,_mm256_dp_ps制作单独的两个点积,因为它适用于r、g、b、a或x、y、z、0乘法?@huseyin:的确-看起来AVX实际上只是两个SSE执行单元栓接在一起-这对于大多数基本的SIMD操作来说都很好,但当您有数据加宽/缩小操作包时,请解压缩,etc或需要在向量水平添加、对齐器等上水平操作,然后遇到问题,必须开始排列向量以使其工作,这会降低总体效率。