将SSE代码转换为AVX - 成本为_mm256_and_ps

时间:2013-12-11 00:53:18

标签: c sse avx

我正在转换SSE2正弦和余弦函数(来自Julien Pommier的sse_mathfun.h;基于CEPHES sinf函数)来使用AVX以接受8个浮点矢量或4个双精度。

因此,Julien的函数sin_ps变为sin_ps8(对于8个浮点数)和sin_pd4为4个双精度。 (此处的“高级”编辑器无法接受我的代码,因此请访问http://arstechnica.com/civis/viewtopic.php?f=20&t=1227375进行查看。)

使用2011年Core2 i7 @ 2.7Ghz上的Mac OS X 10.6.8下的clang 3.3进行测试,基准测试结果如下所示:

  • sinf .. - > 27.6百万向量评估/秒超过5.56e + 07 iters(标准,标量sinf()函数)

    sin_ps .. - >每秒41.0百万的矢量评估 8.22e + 07 iters

    sin_pd4 .. - > 40.2百万向量评估/秒 8.06e + 07 iters

    sin_ps8 .. - > 250万次矢量评估/秒 5.1e + 06 iters

sin_ps8的成本非常可怕,似乎是由于使用了_mm256_castsi256_ps。事实上,注释掉“poly_mask = _mm256_castsi256_ps(emmm2);”这一行导致更正常的表现。 sin_pd4使用_mm_castsi128_pd,但似乎不是(只是)在sin_ps8中咬我的SSE和AVX指令的混合:当我通过2次调用_mm_castsi128_ps来模拟_mm256_castsi256_ps调用时,性能没有提高。 emm2和emm0是指向emmm2和emmm0的指针,两个v8si实例都因此(先验)正确对齐到32位边界。

有关可编辑的代码,请参阅sse_mathfun.hsse_mathfun_test.c

是否有一种(简单的)方法可以避免我所看到的惩罚?

2 个答案:

答案 0 :(得分:1)

将东西从寄存器中传输到内存中通常不是一个好主意。每次存入指针时都会这样做。

而不是:

{ ALIGN32_BEG v4sf *yy ALIGN32_END = (v4sf*) &y;
         emm2[0] = _mm_and_si128(_mm_add_epi32( _mm_cvttps_epi32( yy[0] ), _v4si_pi32_1), _v4si_pi32_inv1),
         emm2[1] = _mm_and_si128(_mm_add_epi32( _mm_cvttps_epi32( yy[1] ), _v4si_pi32_1), _v4si_pi32_inv1);
         yy[0] = _mm_cvtepi32_ps(emm2[0]),
         yy[1] = _mm_cvtepi32_ps(emm2[1]);
      }

/* get the swap sign flag */
emm0[0] = _mm_slli_epi32(_mm_and_si128(emm2[0], _v4si_pi32_4), 29),
emm0[1] = _mm_slli_epi32(_mm_and_si128(emm2[1], _v4si_pi32_4), 29);

/* get the polynom selection mask
there is one polynom for 0 <= x <= Pi/4
and another one for Pi/4<x<=Pi/2

Both branches will be computed.
*/
emm2[0] = _mm_cmpeq_epi32(_mm_and_si128(emm2[0], _v4si_pi32_2), _mm_setzero_si128()),
emm2[1] = _mm_cmpeq_epi32(_mm_and_si128(emm2[1], _v4si_pi32_2), _mm_setzero_si128());

((v4sf*)&poly_mask)[0] = _mm_castsi128_ps(emm2[0]);
((v4sf*)&poly_mask)[1] = _mm_castsi128_ps(emm2[1]);
swap_sign_bit = _mm256_castsi256_ps(emmm0);

尝试这样的事情:

__m128i emm2a = _mm_and_si128(_mm_add_epi32( _mm256_castps256_ps128(y), _v4si_pi32_1), _v4si_pi32_inv1);
__m128i emm2b = _mm_and_si128(_mm_add_epi32( _mm256_extractf128_ps(y, 1), _v4si_pi32_1), _v4si_pi32_inv1);

y = _mm256_insertf128_ps(_mm256_castps128_ps256(_mm_cvtepi32_ps(emm2a)), _mm_cvtepi32_ps(emm2b), 1);

/* get the swap sign flag */
__m128i emm0a = _mm_slli_epi32(_mm_and_si128(emm2a, _v4si_pi32_4), 29),
__m128i emm0b = _mm_slli_epi32(_mm_and_si128(emm2b, _v4si_pi32_4), 29);

swap_sign_bit = _mm256_castsi256_ps(_mm256_insertf128_si256(_mm256_castsi128_si256(emm0a), emm0b, 1));

/* get the polynom selection mask
there is one polynom for 0 <= x <= Pi/4
and another one for Pi/4<x<=Pi/2

Both branches will be computed.
*/
emm2a = _mm_cmpeq_epi32(_mm_and_si128(emm2a, _v4si_pi32_2), _mm_setzero_si128()),
emm2b = _mm_cmpeq_epi32(_mm_and_si128(emm2b, _v4si_pi32_2), _mm_setzero_si128());

poly_mask = _mm256_castsi256_ps(_mm256_insertf128_si256(_mm256_castsi128_si256(emm2a), emm2b, 1));

正如评论中所提到的,cast内在函数纯粹是编译时并且没有发出指令。

答案 1 :(得分:1)

也许您可以将您的代码与Julien Pommier SSE数学函数的已经工作的AVX扩展进行比较?

http://software-lisc.fbk.eu/avx_mathfun/

此代码适用于GCC但不支持MSVC,只支持浮点数(float8),但我认为您可以轻松扩展它以使用双精度数(double4)。对sin函数的快速比较表明它们非常相似,除了SSE2整数部分。