Почему return 139? С

Пытаемся векторизовать сложение

https://godbolt.org/z/KaYPn51jK

код

    #include <immintrin.h>
#include <stdio.h>
double inner2(double* x, double* y, int n){
    __m256d *xx, *yy;
    __m256d p, s,u;
    xx = (__m256d*)x;
    yy = (__m256d*)y;
    s = _mm256_setzero_pd();
    for(int i=0; i<(n/4); ++i){
        p = _mm256_mul_pd(xx[i],yy[i]);
        s = _mm256_add_pd(s,p);
        }
    u = _mm256_permute2f128_pd(s , s , 1);
    s = _mm256_add_pd(s, u);
    s = _mm256_hadd_pd(s, s);
    s = _mm256_hadd_pd(s, s);    
    double sum;
    _mm256_store_pd(&sum,s);
    return sum;
    
    }

int main()
{
    double a[4], b[4];
    a[0]=a[1]=a[2]=a[3]=1.0;
    b[0]=b[1]=b[2]=b[3]=1.0;
    double * c = &(a[0]);
    double * d = &(b[0]);
    double e = inner2(c,d,4);
    printf("%f" ,e);

    return 0;
}

Почему здесь return 139? Подозреваю, что-то не так с адресами, но не могу понять, что. Что-то видимо крайне простое и в функции main, но как я не пытался методом тыка поменять, не получалось.

А в то же время такое:

https://godbolt.org/z/dnz1Gne5b

то есть то же самое, но для float и m128, работает корректно


Ответы (1 шт):

Автор решения: maestro

У вас две ошибки.

  1. Инструкции AVX требуют выравнивания по границе 32 байт. У вас это требование не выполнено. Для создания выровненных массивов можно использовать функцию aligned_alloc.
  2. Функция _mm256_store_pd сохраняет всё содержимое регистра (4 числа double) по переданному указателю. А у вас передаётся указатель на одно число double. Это приводит к разрушению стека. Предлагаю сначала извлечь из типа __m256d два числа в регистр __m128d, а затем уже из него извлечь одно нужное нам число.

Про массив типа __m256d* написал в комментариях. Не уверен, что так можно. Но сделал так, как считаю правильным.

#include <immintrin.h>
#include <stdio.h>
double inner2(double* x, double* y, int n){
    __m256d p, s,u;
    s = _mm256_setzero_pd();
    for(int i=0; i<n; i += 4) {
        //Здесь загрузка 4 чисел из массива в регистр
        __m256d vx = _mm256_load_pd(x + i);
        __m256d vy = _mm256_load_pd(y + i);
        p = _mm256_mul_pd(vx, vy);
        s = _mm256_add_pd(s, p);
    }
    u = _mm256_permute2f128_pd(s, s, 1);
    s = _mm256_add_pd(s, u);
    s = _mm256_hadd_pd(s, s);
    s = _mm256_hadd_pd(s, s);    
    //Это непереносимая конструкция, работает для GCC
    double sum __attribute__ ((aligned (32)));
    __m128d a = _mm256_extractf128_pd(s, 0);
    _mm_storel_pd(&sum, a);
    return sum;
    
}

int main()
{
    //Создаём указатели на выровненную память с помощью стандартной функции языка C11
    double *a = aligned_alloc(32, 4 * sizeof(double));
    double *b = aligned_alloc(32, 4 * sizeof(double));
    a[0]=a[1]=a[2]=a[3]=1.0;
    b[0]=b[1]=b[2]=b[3]=1.0;
    double e = inner2(a,b,4);
    printf("%f" ,e);
    free(a);
    free(b);
    return 0;
}

→ Ссылка