使用SSE在C ++中乘以32位整数的两个向量的最快方法

时间:2013-06-23 19:21:32

标签: c++ x86 sse simd intrinsics

我有两个无符号向量,大小都是4

vector<unsigned> v1 = {2, 4, 6, 8}
vector<unsigned> v2 = {1, 10, 11, 13}

现在我想将这两个向量相乘并得到一个新的

vector<unsigned> v_result = {2*1, 4*10, 6*11, 8*13}

使用什么SSE操作?它是跨平台还是仅跨平台 在某些指定的平台上?

添加: 如果我的目标是增加不是乘法,我可以超级快速地做到这一点:

__m128i a = _mm_set_epi32(1,2,3,4);
__m128i b = _mm_set_epi32(1,2,3,4);
__m128i c;
c = _mm_add_epi32(a,b);

5 个答案:

答案 0 :(得分:3)

对所有元素使用_mm_set_epi32等集合内在函数是低效的。使用load intrinsics会更好。有关Where does the SSE instructions outperform normal instructions的更多信息,请参阅此讨论。如果数组是16字节对齐,您可以使用_mm_load_si128_mm_loadu_si128(对于对齐的内存,它们具有几乎相同的效率),否则使用_mm_loadu_si128。但是对齐的内存效率要高得多。为了获得对齐的内存,我建议_mm_malloc_mm_free或C11 aligned_alloc,以便您可以使用普通free


要回答你的其余问题,我们假设您已将两个向量加载到SSE寄存器__m128i a__m128i b

对于SSE版本&gt; = SSE4.1使用

_mm_mullo_epi32(a, b);

没有SSE4.1:

此代码是从Agner Fog的Vector Class Library复制的(并且被此答案的原作者抄袭):

// Vec4i operator * (Vec4i const & a, Vec4i const & b) {
// #ifdef
__m128i a13    = _mm_shuffle_epi32(a, 0xF5);          // (-,a3,-,a1)
__m128i b13    = _mm_shuffle_epi32(b, 0xF5);          // (-,b3,-,b1)
__m128i prod02 = _mm_mul_epu32(a, b);                 // (-,a2*b2,-,a0*b0)
__m128i prod13 = _mm_mul_epu32(a13, b13);             // (-,a3*b3,-,a1*b1)
__m128i prod01 = _mm_unpacklo_epi32(prod02,prod13);   // (-,-,a1*b1,a0*b0) 
__m128i prod23 = _mm_unpackhi_epi32(prod02,prod13);   // (-,-,a3*b3,a2*b2) 
__m128i prod   = _mm_unpacklo_epi64(prod01,prod23);   // (ab3,ab2,ab1,ab0)

答案 1 :(得分:2)

_mm_mul_epu32只有SSE2并使用pmuludq指令。由于它是SSE2指令,99.9%的CPU支持它(我认为最现代的CPU不是AMD Athlon XP)。

它有一个明显的缺点,它一次只能乘以两个整数,因为它返回64位结果,并且你只能在寄存器中放入其中的两个。这意味着您可能需要进行一系列改组,这会增加成本。

答案 2 :(得分:1)

您可以(如果SSE 4.1可用)使用

__m128i _mm_mullo_epi32 (__m128i a, __m128i b);

乘以打包的32位整数。 否则你必须洗牌两包才能使用_mm_mul_epu32两次。有关显式代码,请参阅@ user2088790的答案。

请注意,您也可以使用_mm_mul_epi32,但这是SSE4,因此您最好还是使用_mm_mullo_epi32

答案 3 :(得分:1)

可能_mm_mullo_epi32是你需要的,虽然它的用途是用于有符号整数。只要v1和v2很小,这些整数的最高有效位为0,就不会引起问题。这是SSE 4.1。作为替代方案,您可能需要考虑_mm_mul_epu32。

答案 4 :(得分:0)

  

std::transform将给定函数应用于范围并存储   导致另一个范围

std::vector<unsigned> result;

std::transform( v1.begin()+1, v1.end(), v2.begin()+1, v.begin(),std::multiplies<unsigned>() );