【问题标题】:Align double vs align float for AVX operations为 AVX 操作对齐双精度与对齐浮点数
【发布时间】:2016-09-22 19:10:18
【问题描述】:

我想将两个(浮点数/双精度)向量与 AVX 运算符相乘。为了做到这一点,我需要对齐内存。我的浮点值函数是:

#define SIZE 65536
float *g, *h, *j;
g = (float*)aligned_alloc(32, sizeof(float)*SIZE);
h = (float*)aligned_alloc(32, sizeof(float)*SIZE);
j = (float*)aligned_alloc(32, sizeof(float)*SIZE);
//Filling g and h with data
for(int i = 0; i < SIZE/8; i++)
    {
        __m256 a_a, b_a, c_a;
        a_a = _mm256_load_ps(g+8*i);
        b_a = _mm256_load_ps(h+8*i);
        c_a = _mm256_mul_ps(a_a, b_a);
        _mm256_store_ps (j+i*8, c_a);
    }
free(g);
free(h);
free(j);

这行得通,但是当我尝试使用双精度值时,我得到一个内存访问错误(例如,如果内存没有正确对齐):

double *g_d, *h_d, *i_d;
g_d = (double*)aligned_alloc(32, sizeof(double)*SIZE);
h_d = (double*)aligned_alloc(32, sizeof(double)*SIZE);
i_d = (double*)aligned_alloc(32, sizeof(double)*SIZE);
for(int i = 0; i < SIZE/4; i++)
{
    __m256d a_a, b_a, c_a;
    a_a = _mm256_load_pd(g_d+4*i);
    b_a = _mm256_load_pd(h_d+4*i);
    c_a = _mm256_mul_pd(a_a, b_a);
    _mm256_store_pd (i_d+i*4, c_a);
}
free(g_d);
free(h_d);
free(i_d);

为什么对齐对double-values 不起作用?

在 gdb 中运行它时,我得到了

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401669 in _mm256_load_pd (__P=0x619f70) at /usr/lib/gcc/x86_64-linux-gnu/5/include/avxintrin.h:836

编辑:我发现了我的错误,这是前一个函数的复制/粘贴错误,它体现在该函数中。由于对其他人没有帮助(我认为),我关闭了这个问题。

【问题讨论】:

  • 这些标识符名称很烂。 a、b、d是什么鬼?始终从文本编辑器复制/粘贴代码。从有这个问题的测试程序中,永远不要编造任何东西。
  • 修正了变量,但稍后会添加一个简短的测试程序。
  • 为我工作 (tm)。你用过调试器吗?它究竟在哪一行发生故障以及它读取(或写入?)的地址的值是什么。什么是确切的失败代码。
  • @MikeVine:我添加了调试器输出。

标签: c++ memory-alignment avx


【解决方案1】:

嗯,您的问题似乎源于不同的数据大小。

  • 在您的第一个 sn-p 中,您将 float 循环增加到 SIZE/8=8192。在这里,我不确定为什么要将元素大小为 4 的 FLOAT 数组增加 8。所以 i &lt; 8192
  • 在第二个 sn-p 中,将 double 循环增加到 SIZE/4=16384。在这里,我不确定为什么要将元素大小为 8 的 DOUBLE 数组增加 4。所以 i &lt; 16384 --- ** 相反!**

DOUBLE 数组的最后一个元素可能会超出你的内存边界!

在这两种情况下,您都使用i++ 增加循环。于是案件进行如下:

首先:(FLOAT (4)) j+i*8 (0

0      4      8      12      16     20     24     28  
v1     .      v2     .       v3     .      v4     . 

第二个:(DOUBLE(8)) j+i*4 (0 v1/v2/v3/v4

0      4      8      12      16     20     24     28     32  
v1(h)  v1(l)  v2(l)  v3(l)   v4(l)  v5(l)  v6(l)  v7(l) 
v1(h)  v2(h)  v3(h)  v4(h)   v5(h)  v6(h)  v7(h)  v8(h)  v8(h)
--------------------------------------------------------------
some thing ... some thing ... some thing .. some thing ...

在第二个 sn-p 中,您将 64 位 Double 的高位部分(32 位)和低位部分(32 位)混合在一起,仅增加 4(sizeof FLOAT)而不是 8(sizeof DOUBLE) )。

另一个问题是_mm256_store_pd 要求...

当源或目标操作数是内存操作数时,操作数必须在 32 字节边界上对齐,否则将生成通用保护异常 (#GP)。

for(int i = 0; i &lt; SIZE/4; i++) 不满足该要求。

我想知道您的FLOAT 版本似乎可以工作,因为_mm256_store_ps 要求...

当源或目标操作数是内存操作数时,操作数必须在 16 字节边界上对齐,否则将生成通用保护异常 (#GP)。

但你只有 8 个字节的对齐方式...

但是,您需要修复 i 变量的“比例”才能使其正常工作。

【讨论】:

  • @arc_lupus:如果你得到正确的结果,你真正的问题/问题可能是什么?我(只是)分析了您的代码中潜在的弱点/错误。而且我不知道为什么您的测试代码会返回正确的值,我只是强调了潜在的问题来源。
  • @arc_lupus:你的问题很奇怪:你说你的代码不工作是因为 - 我引用 - memory access error (such as if the memory is not aligned correctly)。我试图近似那个问题,那么你的问题是什么?换句话说:如果你认为你的代码一切正常,你为什么要问一个问题?
  • 请注意float *x; float* y = x + k;k x sizeof(float)double 类似的步骤,我认为你对arc 代码的分析是错误的。
  • @zx485:我想你在这里可能会感到困惑——4和8的因子是每个向量的元素数量,而不是元素的字节大小——我认为循环增量实际上是正确的在 OP 的代码中。
  • @zx485:是的,这有点令人困惑,特别是因为 OP 使用硬编码文字而不是有意义的常量,但对于 SIMD 循环,增量将是每个向量的元素数,例如如果数组中有SIZEfloats,并且您使用的AVX向量每个向量有8个floats,则循环增量将为SIZE / 8
猜你喜欢
  • 2013-06-01
  • 1970-01-01
  • 2012-07-14
  • 1970-01-01
  • 2015-11-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-23
相关资源
最近更新 更多