【问题标题】:AVX code segfaults when compiled with -ffast-math?使用 -ffast-math 编译时 AVX 代码段错误?
【发布时间】:2016-08-24 20:53:45
【问题描述】:

我正在尝试使用 GCC 内置 simd 支持编写几个内核。我有这段代码对 AVX 点积内核进行基准测试:

#include <time.h>
#include <stdio.h>
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>

// define rtdsc instruction
static __inline__ uint64_t tick(void) {
    uint32_t hi, lo;
    __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
    return ( (uint64_t)lo)|( ((uint64_t)hi)<<32 );
}

// AVX dot product 
float avx_dot(float* __restrict__ ans, float* __restrict__ A, float* __restrict__ B, int N, ssize_t nprod, ssize_t shift) {
    assert(N % 32 == 0 && "N not divisible by 32");
    const int VECTOR_SIZE = 8;

    typedef float vec
        __attribute__ ((vector_size (sizeof(float) * VECTOR_SIZE)));

    N /= VECTOR_SIZE;

    for (ssize_t ii=0; ii < nprod; ii++) {
        vec *Av = (vec*)A;
        vec *Bv = (vec*)(B + ii*shift);

        vec temp[4] = {0,0,0,0};
        for(int jj = 0; jj < N; jj += 4) {
            temp[0] += Av[jj+0] * Bv[jj+0];
            temp[1] += Av[jj+1] * Bv[jj+1];
            temp[2] += Av[jj+2] * Bv[jj+2];
            temp[3] += Av[jj+3] * Bv[jj+3];
        }

        union {
            vec   tempv;
            float tempf[VECTOR_SIZE];
        };

        tempv = temp[0] + temp[1] + temp[2] + temp[3];

        ans[ii] = 0;
        for(int jj = 0; jj < VECTOR_SIZE; ++jj) {
            ans[ii] += tempf[jj];
        }
    }
}

int main(int argc, const char *argv[]) {
    const ssize_t NITER   = 1000;
    const ssize_t DECIM   = atoi(argv[2]);
    const ssize_t DOTPROD = atoi(argv[3]);
    ssize_t size = atoi(argv[1]);

    float* A; posix_memalign((void**)&A, 128, size*sizeof(float));
    float* B; posix_memalign((void**)&B, 128, (size+(DOTPROD-1)*DECIM)*sizeof(float));

    srand(time(NULL));
    for (ssize_t ii=0; ii < size;                   ii++) A[ii] = rand();
    for (ssize_t ii=0; ii < size+(DOTPROD-1)*DECIM; ii++) B[ii] = rand();

    printf("# size: %i  nproducts: %i  shift: %i\n", size, DOTPROD, DECIM);
    printf("# iter  answer  cycles  seconds  samprate\n");
    float results[DOTPROD];
    for (ssize_t ii=0; ii < NITER; ii++) {
        uint64_t beg = tick();
        avx_dot(results, A, B, size, DOTPROD, DECIM);
        uint64_t end = tick();

        float ans = 0;
        for (ssize_t jj=0; jj < DOTPROD; jj++) {
            ans += results[jj];
        }

        double    CLOCK   = 3300e6; 
        uint64_t cycles   = end-beg;
        double   seconds  = (double)cycles/CLOCK;
        double   samprate = (size*DOTPROD)/seconds;

        printf("%-5zd %f %lli %.3e %e\n", ii, ans, (unsigned long long)cycles, seconds, samprate);
    }

    return 0;
}

奇怪的是,当编译时:

g++ -O3 -march=corei7-avx dotprod.cc -ffast-math -o dotprod

第一次访问 avx_dot 中的 temp 时出现段错误。但是,当编译时:

g++ -O3 -march=corei7-avx dotprod.cc -o dotprod

IE,没有 -ffast-math ,它运行良好。我很困惑,因为我相信快速数学不应该影响内存访问,所以我不知道段错误来自哪里。

我正在跑步:

CentOS Linux release 7.2.1511
gcc version 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)

任何人都可以在他们的机器上确认这种行为并阐明正在发生的事情吗?

【问题讨论】:

  • asm 中有什么有趣的地方吗?
  • 我不确定,我不一定最擅长原始组装,但我会看看
  • 在 gcc 下运行它,并向我们展示它出错的 insn。此外,还有-march=sandybridge。 IDK 它与-march=corei7-avx 有何不同。顺便说一句,你不应该从自动矢量化中得到错误,除非你以某种方式告诉 gcc 你的数据是对齐的,但事实证明不是这样。 (vmovaps 会在未对齐的地址上出错,这与其他指令的 AVX 内存操作数不同。vmovups 具有相同的性能。)哦,你做到了,我猜是那个 typedef。 _mm256_loadu_ps 与 _mm256_load_ps 内在函数的存在是为了将对齐信息传达给编译器。

标签: c linux gcc simd fast-math


【解决方案1】:

我的随机猜测是数据对齐,考虑到它无法加载数据(失败指令是 .... vmovaps (%rcx),%ymm4 ... %rcx=0x603228 和 Bv 位于 0x603228,并阅读关于该指令揭示了 16 字节对齐的要求)。

进一步调查:

由于这一行(而 AVX 需要 16 字节对齐),当 Bv 向 B 偏移 8 个字节时会出现问题:

vec *Bv = (vec*)(B + ii*shift);


./dotprod-fast 64 10 10
A=0x1125080
B=0x1125200
# size: 64  nproducts: 10  shift: 10
# iter  answer  cycles  seconds  samprate
Av=0x1125080
Bv=0x1125200
Av=0x1125080
Bv=0x1125228
Segmentation fault (core dumped)

【讨论】:

  • ymm 表单需要 32 字节对齐,但是是的,您可能是对的。
  • 只有对齐的加载/存储指令在 AVX 中有对齐要求。 AVX 的主要功能之一是内存操作数不需要对齐,因此即使数据有时可能未对齐,vaddps ymm0, ymm1, [mem] 也是安全的。跨越缓存线边界会带来性能损失,但如果数据在运行时通常对齐,那么尽可能快地保持对齐的情况并让硬件处理未对齐的情况是有意义的只有小幅放缓。
猜你喜欢
  • 2018-08-03
  • 2015-09-01
  • 2013-12-10
  • 2013-08-14
  • 2011-11-07
  • 2020-07-04
  • 1970-01-01
  • 2014-12-14
  • 1970-01-01
相关资源
最近更新 更多