使用 SSE 向量化的错误结果

Posted

技术标签:

【中文标题】使用 SSE 向量化的错误结果【英文标题】:Wrong result in vectorization with SSE 【发布时间】:2014-01-28 22:13:09 【问题描述】:

下面的代码生成以下输出:

6 6 0 140021597270387

这意味着只有前两个位置计算正确。但是,我正在处理 long(4 个字节),而 __m128i 可以容纳超过 4 个 long。

long* AA = (long*)malloc(32*sizeof(long));
long* BB = (long*)malloc(32*sizeof(long));

for(i = 0; i<4;i++)
    AA[i] = 2;
    BB[i] = 3;


__m128i* m1 = (__m128i*) AA;
__m128i* m2 = (__m128i*) BB;

__m128i m3 = _mm_mul_epu32(m1[0],m2[0]);

long* CC = (long*) malloc(16 * sizeof(long));
CC = (long*)&m3;

for (i = 0; i < 4; i++)
    printf("%ld \n",CC[i]);

分配:

long* AA = (long*) memalign(16 * sizeof(long), 16);

(和剩余的向量)生成一个段。过错。有人可以评论吗?

谢谢

【问题讨论】:

为什么要分配CC,然后马上给它分配m3的地址? __m128i 可能持有也可能不会持有四个多头。 long 的大小是特定于实现的,可能是 32 位或更大。在许多 64 位架构上,long 实际上是 64 位,所以 __m128i 可能只包含两个长整数。你应该检查sizeof(__m128i) == 4*sizeof(long) 相关:SSE multiplication of four 32-bit integers 在 Windows 上 long 是 32 位,但大多数 64 位类 Unix 系统都有 64 位长 你们是对的。在我的例子中,sizeof(long) 是 8... 【参考方案1】:

1) 不要使用不确定大小的类型,如long,使用特定的固定类型,如uint32_t

2) 不要使用 malloc - 不保证返回 16 字节对齐的内存,使用 memalign 或等效*

3) 不要在 C 中转换 malloc(或任何其他函数返回 void *)的结果

4) 无需再分配另一个缓冲区来打印结果

固定代码:

uint32_t* AA = memalign(32*sizeof(uint32_t), 16);
uint32_t* BB = memalign(32*sizeof(uint32_t), 16);

for (i = 0; i < 4; i++)
    AA[i] = 2;
    BB[i] = 3;


__m128i* m1 = (__m128i*)AA;
__m128i* m2 = (__m128i*)BB;

__m128i m3 = _mm_mul_epu32(m1[0], m2[0]);    // 2 x 32x32->64 bit unsigned multiplies -> m3

uint64_t* CC = (uint64_t*)&m3;

for (i = 0; i < 2; i++)                      // display 2 x 64 bit result values
    printf("%llu\n", CC[i]);

*请注意,根据您的平台,您可能需要使用 memalign 以外的调用来分配适当对齐的内存,例如posix_memalign_mm_malloc_aligned_malloc (WIN32)。

【讨论】:

值得注意的是posix_memalign 不是跨平台的(没有Windows)。它也不是这样称呼的:pubs.opengroup.org/onlinepubs/007904975/functions/… @PaulR: _mm_mul_epu32 被记录为只有两个乘法 a0 * b0a2 * b2。一次调用会乘以 4 int32s 吗? @legends2k:确实,_mm_mul_epi32/_mm_mul_epu32 执行 2 x 32x32->64 位 int 乘法。如果您想要 4 x 32x32->32 位乘法,那么您可以使用几个 16 位乘法将其组合在一起,或者可能使用 2 x _mm_mul_epu32 和一些移位/改组,或者如果精度不重要,则转换为浮点数,使用_mm_mul_ps,然后转换回 int。没有单独的指令。 @PaulR:在上面的示例中,您将四个int32_ts 打包到寄存器中并调用_mm_mul_epu32 一次,其结果是两个int64_t,而您将每个int32 访问四次打印输出。你不应该给_mm_mul_epu32打两次电话吗? 是的,抱歉 - 我只是在修复 OPs 代码中的基本问题(即崩溃),并不太担心更精细的细节。我也会整理一下显示部分。

以上是关于使用 SSE 向量化的错误结果的主要内容,如果未能解决你的问题,请参考以下文章

SSE向量化与内存对齐的关系

是否可以使用 SSE 对这个嵌套进行矢量化?

使用结果浮点数时的 SSE SIMD 分段错误

使用矢量化 C++ 的矩阵乘法

SSE 内在函数向右移位

将错误消息从 SSE (Webflux) Spring Boot 应用程序传递到 Angular 7 前端