GCC vector extensions提供一种不错的、合理的可移植方式来访问不同硬件架构上的一些 SIMD 指令,而无需求助于 hardware specific intrinsics (或自动矢量化)。
一个真正的用例是计算一个简单的附加校验和。尚不清楚的一件事是如何安全地将数据加载到向量中。
typedef char v16qi __attribute__ ((vector_size(16)));
static uint8_t checksum(uint8_t *buf, size_t size)
{
assert(size%16 == 0);
uint8_t sum = 0;
vec16qi vec = {0};
for (size_t i=0; i<(size/16); i++)
{
// XXX: Yuck! Is there a better way?
vec += *((v16qi*) buf+i*16);
}
// Sum up the vector
sum = vec[0] + vec[1] + vec[2] + vec[3] + vec[4] + vec[5] + vec[6] + vec[7] + vec[8] + vec[9] + vec[10] + vec[11] + vec[12] + vec[13] + vec[14] + vec[15];
return sum;
}
将指针转换到向量类型似乎有效,但我担心如果 SIMD 硬件期望向量类型正确对齐,这可能会以一种可怕的方式爆炸。
我想到的唯一其他选择是使用临时向量并显式加载值(通过 memcpy 或按元素分配),但在测试此抵消时,大部分加速都使用了 SIMD 指令。理想情况下,我认为这将类似于通用
__builtin_load()
功能,但似乎不存在。将数据加载到向量中可能会出现对齐问题的更安全方法是什么?
最佳答案
编辑 (感谢 Peter Cordes)您可以转换指针:
typedef char v16qi __attribute__ ((vector_size (16), aligned (16)));
v16qi vec = *(v16qi*)&buf[i]; // load
*(v16qi*)(buf + i) = vec; // store whole vector
这编译为
vmovdqa
加载和 vmovups
储藏。如果不知道数据是否对齐,请设置 aligned (1)
生成 vmovdqu
. ( godbolt )请注意,还有几个用于加载和卸载这些寄存器的专用内置函数( Edit 2 ):
v16qi vec = _mm_loadu_si128((__m128i*)&buf[i]); // _mm_load_si128 for aligned
_mm_storeu_si128((__m128i*)&buf[i]), vec); // _mm_store_si128 for aligned
好像有必要用
-flax-vector-conversions
出发地 char
转至 v16qi
有了这个功能。另见:C - How to access elements of vector using GCC SSE vector extension
另见:SSE loading ints into __m128
(提示:谷歌最好的短语是“gcc loading __m128i”。)
关于gcc - 为 GCC 的矢量扩展加载数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9318115/