4

A 和 B 是向量或长度 N,其中 N 可以在 20 到 200 的范围内。我想计算这些向量之间距离的平方,即d^2 = ||AB||^2。

到目前为止,我有:

float* a = ...;
float* b = ...;
float d2 = 0;

for(int k = 0; k < N; ++k)
{
    float d = a[k] - b[k];
    d2 += d * d;
}

这似乎工作正常,除了我已经分析了我的代码并且这是瓶颈(超过 50% 的时间都花在了这样做上)。我在 Win 7 上使用 Visual Studio 2012,并带有以下优化选项:/O2 /Oi /Ot /Oy-. 我的理解是 VS2012 应该自动矢量化该循环(使用 SSE2)。但是,如果我插入#pragma loop(no_vector)代码,我不会明显减速,所以我猜循环没有被矢量化。编译器通过以下消息确认:

  info C5002: loop not vectorized due to reason '1105'

我的问题是:

  1. 是否可以修复此代码以便 VS2012 可以对其进行矢量化?
  2. 如果没有,尝试自己对代码进行矢量化是否有意义?
  3. 你能推荐一个网站让我了解 SSE2 编码吗?
  4. 是否存在某个 N 值,低于该值的矢量化会适得其反?
  5. 是什么reason '1105'
4

2 回答 2

6

使用 SSE 内在函数实现这一点非常简单:

#include "pmmintrin.h"

__m128 vd2 = _mm_set1_ps(0.0f);
float d2 = 0.0f;
int k;

// process 4 elements per iteration
for (k = 0; k < N - 3; k += 4)
{
    __m128 va = _mm_loadu_ps(&a[k]);
    __m128 vb = _mm_loadu_ps(&b[k]);
    __m128 vd = _mm_sub_ps(va, vb);
    vd = _mm_mul_ps(vd, vd);
    vd2 = _mm_add_ps(vd2, vd);
}

// horizontal sum of 4 partial dot products
vd2 = _mm_hadd_ps(vd2, vd2);
vd2 = _mm_hadd_ps(vd2, vd2);
_mm_store_ss(&d2, vd2);

// clean up any remaining elements
for ( ; k < N; ++k)
{
    float d = a[k] - b[k];
    d2 += d * d;
}

请注意,如果您可以保证aandb是 16 字节对齐的,那么您可以使用_mm_load_ps而不是_mm_loadu_ps可能有助于提高性能,尤其是在较旧的(前 Nehalem)CPU 上。

另请注意,对于这样的循环,相对于加载数量而言,算术指令非常少,那么性能可能会受到内存带宽的限制,并且在实践中可能无法实现矢量化的预期加速。

于 2013-06-08T16:07:03.860 回答
4

MSDN 文档中,1105 错误代码意味着编译器无法弄清楚如何将代码简化为矢量化指令。对于浮点运算,表明您需要指定 /fp:fast 选项以启用任何浮点缩减。

于 2013-06-09T01:16:58.120 回答