加载GCC向量扩展的数据

时间:2012-02-16 19:53:25

标签: gcc checksum vectorization simd

GCC的vector extensions提供了一种不错的,合理的可移植方式,可以在不使用hardware specific intrinsics(或自动矢量化)的情况下访问不同硬件架构上的某些SIMD指令。

一个真实的用例,就是计算一个简单的加法校验和。有一点不明确的是如何将数据安全地加载到矢量中。

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()函数,但似乎没有。

将数据加载到矢量冒险对齐问题的更安全的方法是什么?

2 个答案:

答案 0 :(得分:1)

编辑(感谢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

请注意,还有一些用于加载和卸载这些寄存器的专用内置函数(编辑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-conversionschar转到v16qi

另见:C - How to access elements of vector using GCC SSE vector extension
另见:SSE loading ints into __m128

(提示:google的最佳短语类似于“gcc loading __m128i”。)

答案 1 :(得分:0)

您可以使用初始化程序加载值,即执行

const vec16qi e = { buf[0], buf[1], ... , buf[15] }

并希望GCC将其转换为SSE加载指令。我会用一个反汇编来验证;-)。此外,为了获得更好的性能,您尝试使buf 16字节对齐,并通过aligned属性通知该编译器。如果您可以保证输入缓冲区将被对齐,则按字节顺序处理它,直到达到16字节的边界。