提示编译器浮点向量计数可被8整除吗?

时间:2019-09-26 13:09:24

标签: c++ vectorization compiler-optimization

static inline void R1_sub_R0(float *vec,  size_t cnt,  float toSubtract){
    for(size_t i=0; cnt; ++i){
        vec[i] -= toSubtract;
    }
}

我知道cnt总是可以被8整除,因此可以通过SSE和AVX对代码进行矢量化处理。换句话说,我们可以将*vec迭代为__m256类型。 但是编译器可能不会知道这一点。 如何重新确保编译器确保此计数可以被8整除?

这样的东西会有所帮助吗? (如果我们将其粘贴在函数的开头)

assert(((cnt*sizeof(float)) % sizeof(__m256)) ==0 );  //checks that it's "multiple of __m256 type".

当然,我可以简单地将整个内容编写为矢量化代码:

static inline void R1_sub_R0(float *vec,  size_t cnt,  float toSubtract){
    assert(cnt*sizeof(float) % sizeof(__m256) == 0);//check that it's "multiple of __m256 type".
    assert(((uintptr_t)(const void *)(POINTER)) % (16) == 0);//assert that 'vec' is 16-byte aligned

    __m256 sToSubtract = _mm256_set1_ps(toSubtract);
    __m256 *sPtr = (__m256*)vec;
    const __m256 *sEnd = (const __m256*)(vec+cnt);

    for(sPtr;  sPtr != sEnd;  ++sPtr){
        *sPtr = _mm256_sub_ps(*sPtr, sToSubtract);
    }
}

但是,它的运行速度比原始版本慢10%。 因此,我只想向编译器提供更多信息。这样,它可以更高效地向量化代码。

2 个答案:

答案 0 :(得分:6)

  

是否提示编译器浮点向量计数可以被8整除?

您可以通过嵌套另一个循环来半展开循环:

for(size_t i=0; i < cnt; i += 8){
    for(size_t j=0; j < 8; j++){
        vec[i + j] -= toSubtract;
    }
}

编译器可以很容易地看到内部循环具有恒定的迭代次数,并且可以展开该循环,并且可以选择使用SIMD。

  

是否提示编译器浮点向量计数为[16字节对齐]?

这有点棘手。

您可以使用类似的内容:

struct alignas(16) sse {
    float arr[8];
};

 // cnt is now number of structs which is 8th fraction of original cnt
R1_sub_R0(sse *vec,  size_t cnt,  float toSubtract) {
    for(size_t i=0; i < cnt; i ++){
        for(size_t j=0; j < 8; j++){
            vec[i].arr[j] -= toSubtract;
        }
    }

除此之外,还有__builtin_assume_aligned之类的编译器扩展可以与普通float数组一起使用。

答案 1 :(得分:0)

我不会在这里进行微优化,如果您只是向其传达您想要完成的事情,而不是您想要完成的事情,那么编译器将生成最快的代码。因此,我将使用std :: transform。

std::transform(vec, vec + cnt, vec, 
[toSubstract](float f){return f - toSubstract;});