支持 SIMD 的代码?
Posted
技术标签:
【中文标题】支持 SIMD 的代码?【英文标题】:SIMD-able code? 【发布时间】:2013-01-10 12:40:11 【问题描述】:什么代码可以使用 SIMD 指令集的严格定义是什么?是否可以并行运行计算?
如果我有:
for(int i=0; i<100; i++)
sum += array[i];
这可以利用 SIMD,因为我们可以运行:
for(int i=0; i<100;i=i+4)
sum0 += array[i];
sum1 += array[i+1];
sum2 += array[i+2];
sum3 += array[i+3];
sum = sum0 + sum1 + sum2 + sum3;
?
它必须是浮点类型,还是可以是双精度和整数?
【问题讨论】:
没有真正有用的定义。你可以在可以使用的时候使用它——这真的只能通过经验来回答。 【参考方案1】:假设您谈论的是 x86 (SSE et al),那么支持的算术类型是 8、16、32 和 64 位整数,以及单精度和双精度浮点数。但请注意,并非所有数据类型都支持所有算术运算 - SSE 在这方面缺乏正交性。
假设 32 位整数和适当对齐的数组(16 字节对齐),那么您可以将上述循环示例实现为:
#include <emmintrin.h> // SSE2 intrinsics
int32_t a[100] __attribute__ ((aligned(16)));
// suitably aligned array
__m128i vsum = _mm_set1_epi32(0); // init vsum = 0, 0, 0, 0
for (int i = 0; i < 100; i += 4)
__m128i v = _mm_load_si128(&a[i]); // load 4 ints from a[i]..a[i+3]
vsum = _mm_add_epi32(vsum, v); // accumulate 4 partial sums
// final horizontal sum of partial sums
vsum = _mm_add_epi32(vsum, _mm_srli_si128(vsum, 8));
int32_t sum = _mm_cvtsi128_si32(vsum); // sum = scalar sum of a[]
【讨论】:
感谢保罗。你能为我澄清一些事情吗?关于对齐,这是 SSE/AVX 扩展类型为程序员考虑的事情吗?或者这是我们必须做的事情,如果是这样,这是怎么做的?我想你指的是填充字节? 基本 SIMD 数据类型(例如__m128i
)会自动正确对齐,但对于非 SIMD 数据类型和动态内存分配(new
、malloc
等),那么您需要额外注意以确保对齐。如果您的数据由于某种原因无法正确对齐,那么您可以使用未对齐的 SIMD 加载/存储作为最后的手段,但这会降低性能。
为什么要关注动态内存分配?我们不是只处理“原始”SSE 类型(“8、16、32 和 64 位整数,以及单精度和双精度浮点数”)吗?
根据目标平台,动态内存分配可能只有 4 或 8 字节对齐。对于与 SIMD 一起使用的数组等,我们通常需要 16 字节对齐。您可以使用例如在 Linux 上 memalign
或 posix_memalign
为此。 Windows 和 Mac OS X 具有类似的专用功能。如果您需要使用 C++ 的 new 运算符,那么您将需要一个自定义分配器。
如果底层数据类型是 SIMD 类型,则静态分配是可以的,例如__m128i
,但如果底层数据类型是 int32_t
,它只有 4 字节自然对齐,那么仍然存在问题。我已经更新了上面的答案,以说明使用 GNU 扩展对非 SIMD 数组强制正确对齐。以上是关于支持 SIMD 的代码?的主要内容,如果未能解决你的问题,请参考以下文章
演示代码在禁用优化的情况下未能显示 SIMD 速度快 4 倍