告诉C++指针数据是 16 字节对齐的

Tell C++ that pointer data is 16 byte aligned

本文关键字:字节 对齐 C++ 指针 数据 告诉      更新时间:2023-10-16

我用静态数组写了一些代码,它矢量化得很好。

float data[1024] __attribute__((aligned(16)));

我想动态分配数组。我尝试做这样的事情:

float *data = (float*) aligned_alloc(16, size*sizeof(float));

但是编译器(GCC 4.9.2)不再能够对代码进行矢量化。我认为这是因为它不知道指针数据是 16 字节对齐的。我收到如下消息:

note: Unknown alignment for access: *_43

我尝试在使用数据之前添加此行,但它似乎没有任何作用:

data = (float*) __builtin_assume_aligned(data, 16);

使用不同的变量和restrict没有帮助:

float* __restrict__ align_data = (float*) __builtin_assume_aligned(data,16);

例:

#include <iostream>
#include <stdlib.h>
#include <math.h>
#define SIZE 1024
#define DYNAMIC 0
#define A16 __attribute__((aligned(16)))
#define DA16 (float*) aligned_alloc(16, size*sizeof(float))
class Test{
public:
    int size;
#if DYNAMIC
    float *pos;
    float *vel;
    float *alpha;
    float *k_inv;
    float *osc_sin;
    float *osc_cos;
    float *dosc1;
    float *dosc2;
#else
    float pos[SIZE] A16;
    float vel[SIZE] A16;
    float alpha[SIZE] A16;
    float k_inv[SIZE] A16;
    float osc_sin[SIZE] A16;
    float osc_cos[SIZE] A16;
    float dosc1[SIZE] A16;
    float dosc2[SIZE] A16;
#endif
    Test(int arr_size){
        size = arr_size;
#if DYNAMIC
        pos = DA16;
        vel = DA16;
        alpha = DA16;
        k_inv = DA16;
        osc_sin = DA16;
        osc_cos = DA16;
        dosc1 = DA16;
        dosc2 = DA16;
#endif
    }
    void compute(){
        for (int i=0; i<size; i++){
            float lambda = .67891*k_inv[i],
                omega = (.89 - 2*alpha[i]*lambda)*k_inv[i],
                diff2 = pos[i] - omega,
                diff1 = vel[i] - lambda + alpha[i]*diff2;
            pos[i] = osc_sin[i]*diff1 + osc_cos[i]*diff2 + lambda*.008 + omega;
            vel[i] = dosc1[i]*diff1 - dosc2[i]*diff2 + lambda;
        }
    }
};
int main(int argc, char** argv){
    Test t(SIZE);
    t.compute();
    std::cout << t.pos[10] << std::endl;
    std::cout << t.vel[10] << std::endl;
}

这是我的编译方式:

g++ -o test test.cpp -O3 -march=native -ffast-math -fopt-info-optimized

DYNAMIC设置为 0 时,它会输出:

test.cpp:46:4: note: loop vectorized

但是当它设置为1时,它不会输出任何内容。

编译器不会对循环进行矢量化处理,因为它无法确定动态分配的指针不会相互别名。允许对示例代码进行矢量化的一种简单方法是传递 --param vect-max-version-for-alias-checks=1000 选项。这将允许编译器发出所有必要的检查,以查看指针是否实际被别名化。

允许示例代码矢量化的另一个简单解决方案是重命名main ,正如 Marc Glisse 在他的评论中建议的那样。名为 main 的函数显然禁用了某些优化。命名为其他名称,GCC 4.9.2 可以跟踪 this->foo(和其他指针成员)的使用,compute回到它们在 Test() 中的分配。

但是,我假设在名为main的函数中使用您的类以外的其他东西阻止了您的代码在实际代码中被矢量化。允许代码在不进行锯齿或对齐检查的情况下进行矢量化的更通用的解决方案是使用 restrict 关键字和 aligned 属性。像这样:

typedef float __attribute__((aligned(16))) float_a16;
__attribute__((noinline))
static void _compute(float_a16 * __restrict__ pos,
         float_a16 * __restrict__ vel,
         float_a16 * __restrict__ alpha,
         float_a16 * __restrict__ k_inv,
         float_a16 * __restrict__ osc_sin,
         float_a16 * __restrict__ osc_cos,
         float_a16 * __restrict__ dosc1,
         float_a16 * __restrict__ dosc2,
         int size) {
    for (int i=0; i<size; i++){
        float lambda = .67891*k_inv[i],
            omega = (.89 - 2*alpha[i]*lambda)*k_inv[i],
            diff2 = pos[i] - omega,
            diff1 = vel[i] - lambda + alpha[i]*diff2;
        pos[i] = osc_sin[i]*diff1 + osc_cos[i]*diff2 + lambda*.008 + omega;
        vel[i] = dosc1[i]*diff1 - dosc2[i]*diff2 + lambda;
    }
}
void compute() {
    _compute(pos, vel, alpha, k_inv, osc_sin, osc_cos, dosc1, dosc2,
         size);
}

noinline属性至关重要,否则内联可能会导致指针失去其限制性和对齐性。编译器似乎忽略了函数参数以外的上下文中的 restrict 关键字。