Подсказка компилятору, что число с плавающей точкой делится на 8? - PullRequest
5 голосов
/ 26 сентября 2019
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 ]

6 голосов
/ 26 сентября 2019

Подсказка компилятору, что число векторов с плавающей запятой делится на 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, которые можно использовать с массивом plain float.

0 голосов
/ 28 сентября 2019

Я бы не пытался микрооптимизировать здесь, компилятор сгенерирует самый быстрый код, если вы просто сообщите ему, что вы хотите сделать, а не как вы хотите, чтобы это было сделано.Поэтому я бы использовал std :: transform.

std::transform(vec, vec + cnt, vec, 
[toSubstract](float f){return f - toSubstract;});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...