4

我有两个无符号向量,大小均为 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);
4

5 回答 5

4

_mm_set_epi32对所有元素使用集合内在函数是低效的。最好使用负载内在函数。有关SSE 指令在何处优于普通指令的更多信息,请参阅此讨论。如果数组是 16 字节对齐的,您可以使用_mm_load_si128或者_mm_loadu_si128(对于对齐的内存,它们具有几乎相同的效率),否则使用_mm_loadu_si128. 但是对齐的内存效率更高。要获得对齐的内存,我建议使用_mm_mallocand_mm_free或 C11 aligned_alloc,以便您可以正常使用free


要回答您的其余问题,假设您将两个向量加载到 SSE 寄存器中__m128i a,并且__m128i b

对于 SSE 版本 >=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)
于 2013-06-24T05:04:19.903 回答
2

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

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

于 2013-06-23T20:15:26.963 回答
1

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

__m128i _mm_mullo_epi32 (__m128i a, __m128i b);

将压缩的 32 位整数相乘。否则,您必须将两个包都洗牌才能使用_mm_mul_epu32两次。有关显式代码,请参阅@user2088790 的答案。

请注意,您也可以使用_mm_mul_epi32,但那是 SSE4,因此您还是宁愿使用_mm_mullo_epi32

于 2013-06-23T19:54:39.907 回答
1

_mm_mullo_epi32 可能是您需要的,尽管它的预期用途是用于有符号整数。只要 v1 和 v2 非常小以至于这些整数的最高有效位为 0,这不会导致问题。它是 SSE 4.1。作为替代方案,您可能需要考虑 _mm_mul_epu32。

于 2013-06-23T20:00:38.543 回答
0

std::transform将给定函数应用于一个范围并将结果存储在另一个范围中

std::vector<unsigned> result;

std::transform( v1.begin()+1, v1.end(), v2.begin()+1, v.begin(),std::multiplies<unsigned>() );
于 2013-06-23T19:23:57.860 回答