【问题标题】:AVX: data alignment: store crash, storeu, load, loadu doesn'tAVX:数据对齐:存储崩溃,存储,加载,加载不
【发布时间】:2015-07-31 11:51:46
【问题描述】:

我正在修改 RNNLM 一个神经网络来研究语言模型。但是考虑到我的语料库的大小,它的运行速度真的很慢。我尝试优化 matrix*vector 例程(对于小数据集,它占总时间的 63%(我希望它在更大的数据集上会更糟))。现在我被内在函数困住了。

    for (b=0; b<(to-from)/8; b++) 
    {
        val = _mm256_setzero_ps();
        for (a=from2; a<to2; a++) 
        {
            t1 = _mm256_set1_ps (srcvec.ac[a]);
            t2 = _mm256_load_ps(&(srcmatrix[a+(b*8+from+0)*matrix_width].weight));
            //val =_mm256_fmadd_ps (t1, t2, t3)
            t3 = _mm256_mul_ps(t1,t2);
            val = _mm256_add_ps (val, t3);
        }
        t4 = _mm256_load_ps(&(dest.ac[b*8+from+0]));
        t4 = _mm256_add_ps(t4,val);
        _mm256_store_ps (&(dest.ac[b*8+from+0]), t4);
    }

这个例子崩溃了:

_mm256_store_ps (&(dest.ac[b*8+from+0]), t4);

但是如果我改成

_mm256_storeu_ps (&(dest.ac[b*8+from+0]), t4);

(我想用 u 表示未对齐)一切都按预期工作。我的问题是:为什么 load 会起作用(而如果数据未对齐,则不应该这样做)而 store 不会。 (而且两者都在同一个地址上运行)。

dest.ac 已使用

分配
void *_aligned_calloc(size_t nelem, size_t elsize, size_t alignment=64)
{
    size_t max_size = (size_t)-1;

    // Watch out for overflow
    if(elsize == 0 || nelem >= max_size/elsize)
        return NULL;

    size_t size = nelem * elsize;
    void *memory = _mm_malloc(size+64, alignment);
    if(memory != NULL)
        memset(memory, 0, size);
    return memory;
}

它至少有 50 个元素长。 (顺便说一句,VS2012 我有一个关于一些随机分配的非法指令,所以我使用 linux。)

提前谢谢你, 阿坎图斯。

【问题讨论】:

  • from 的值是多少? _mm256_load_ps intrinsic 是否有可能实际实现为 2 128 位加载?
  • 崩溃时from的值为891. &(dest.ac[b*8+from+0]) = 0x957e6c。所以表格中间有一个访问,这个没有对齐。
  • 有了这个值,负载工作更令人惊讶。您是否检查过您实际上加载了正确的值(对于 from 的值)?
  • 您应该检查生成的 ASM,看看它是否每次通过内部循环都重新计算数组索引。如果是这样,请将恒定的部分拉出循环。通常效果很好的方法是让外部循环将b 增加8 * matrix_width,而不是在索引表达式中将b * 8 相乘。当您不以这种方式编写循环时,gcc 似乎不擅长将循环转换为仅维护循环计数器的缩放版本。
  • 另外,set1 内部函数可能很慢。小心对待他们。希望编译为vbroadcastss ymm, [mem]。如果您可以安排您的数据结构在内循环中不需要它,那可能会更快。只是交换内部/外部循环,因此相同的srcvec 用于所有b 值,因为必须从srcmatrix 收集非连续数据,所以会更慢。 vbroadcastss 是 2 微秒,来自内存的 5 个周期延迟(在 Haswell 上)。使用 128 位目标而不是 256 可减少 1 个周期。吞吐量为每个周期 1(只能在 SnB/IvB/HSW 上的端口 5 上运行)。

标签: c++ avx


【解决方案1】:

TL:DR:在优化代码中,loads will fold into memory operands for other operations, which don't have alignment requirements in AVX。商店不会。


您的示例代码无法自行编译,因此我无法轻松检查 _mm256_load_ps 编译为什么指令。

我用 gcc 4.9 做了一个小实验,它根本不会为 _mm256_load_ps 生成 vmovaps,因为我只使用加载的结果作为另一条指令的输入。它使用内存操作数生成该指令。 AVX 指令对其内存操作数没有对齐要求。 (越过缓存线会影响性能,越过页面边界会造成更大的影响,但您的代码仍然有效。)

另一方面,商店确实会生成vmov... 指令。由于您使用了需要对齐的版本,因此它会在未对齐的地址上出错。只需使用未对齐的版本;当地址对齐时它会一样快,当它不对齐时它仍然可以工作。

我没有仔细检查您的代码以查看是否所有访问都应该对齐。我认为不会,从您的措辞方式来看,只是问为什么您没有因未对齐的负载而出现故障。就像我说的那样,可能您的代码没有编译为任何 vmovaps 加载指令,或者即使是“对齐”的 AVX 加载也不会在未对齐的地址上出错。

您是否在 Sandy/Ivybridge CPU 上运行 AVX(没有 AVX2 或 FMA?)?我认为这就是为什么您的 FMA 内在函数被注释掉的原因。

【讨论】:

  • 是的,我在 Sandy CPU 上使用 AVX。是的,有些访问没有对齐!谢谢 !现在我明白为什么了,我将使用 u 版本!
猜你喜欢
  • 1970-01-01
  • 2013-08-25
  • 2013-11-12
  • 2017-04-16
  • 2021-11-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多