【问题标题】:Optimize element-wise product of two std::vector<std::complex<float>> allocated with fftw_malloc()优化使用 fftw_malloc() 分配的两个 std::vector<std::complex<float>> 的元素乘积
【发布时间】:2018-08-10 20:55:05
【问题描述】:

在我正在开发的数值模拟中,我必须执行许多 2D 离散傅立叶变换,我使用 FFTW 进行数组乘法运算。

我正在为我的数据使用以下容器:

std::vector<std::complex<float>, fftwAllocator<std::complex<float>>> data(LX*LY);

LXLY 不一定相等。 fftwAllocator 是一个自定义分配器,它使用fftw_malloc() 进行内存对齐。

目前,我的元素乘法看起来像这样:

Wave &operator*=(const Wave &m) {
    for(unsigned int i = 0; i < LX * LY; i++)
        _data[i] *= m._data[i];

    return *this;
}

我知道,编译器可能做了很多魔法,但考虑到我的数组已经通过fftw_malloc() 以 SIMD 兼容的方式对齐,我想我也许可以在这里使用向量指令来加快速度更多的。

有没有一种简单的方法可以在这里引入独立于平台的向量指令?我真的很惊讶,FFTW 中没有包含向量的简单乘法,因为很多人用它来卷积信号......

【问题讨论】:

  • 我会像您现在一样编写最清晰的代码,然后让优化器完成它的工作。然后衡量性能,看看它如何支持自滚动优化。
  • C++ 不知道向量化。您需要一个库来使其可移植。
  • @SombreroChicken:我知道这可能是最简单的,到目前为止我懒得真正衡量性能。但是,我无法判断它是否是quick,因为我不知道使用 SIMD 会多快。
  • @NathanOliver:我知道我需要额外的代码甚至库。我认为对于预对齐的内存,在这里扩展我的简单循环不会太多工作。但是,如果结果很困难,我宁愿听从 Sombrero Chicken 的建议。 :)
  • 你的编译器不是已经自动矢量化了吗?如果你告诉它数组总是对齐的,它可以帮助编译器做得更好,但我认为没有可移植的语法。在 GNU C 中,foo = __builtin_assume_aligned(foo, 64); 是一种方式,但您也可以 typedef 一个 aligned_float 指针,这有点骇人听闻。无论如何,gcc -O3 应该自动矢量化这个就好了。

标签: c++ vector simd


【解决方案1】:

正如 Peter Cordes 在 cmets 中对我的问题所建议的那样,gcc 能够自行矢量化某些指令,这可以通过编译标志 -fopt-info-vec-all 进行检查。

但事实证明,complex&amp; operator*=(const T&amp; other); 无法矢量化,因此我不得不将问题中的函数替换为以下内容:

Wave &operator*=(const Wave &m) {
    // the builtin product of std::complex is not
    // vectorized by gcc, so we're doing it manually
    // here.
    float tmp;
    for(unsigned int i = 0; i < _lx * _ly; i++) {
        tmp = _data[i].real();
        _data[i].real(_data[i].real() * m._data[i].real() - _data[i].imag() * m._data[i].imag());
        _data[i].imag(tmp * m._data[i].imag() + _data[i].imag() * m._data[i].real());
    }

    return *this;
}

这样,gcc -O3 成功地向量化了循环。

【讨论】:

  • 哦,我没注意到你有complex,而不是普通的float。如果您想要高效的 SIMD,那么将实数、图像对存储在一起的数据布局是一个问题。单独的数组更有效(谷歌 SoA 与 AoS),因此编译器不必在向量中混洗以将同一 data[i] 元素的实部与虚部结合起来。相反,它可以在 4 个实部和 4 个虚部的两个向量之间进行垂直 SIMD 操作。
  • 感谢您的建议,但我正在使用 FFTW,我不确定是否可以轻松地使复杂的 DFT 与 SoA 存储一起使用。 (刚才我发现FFTW支持拆分数组的Guru接口:fftw.org/fftw3_doc/Guru-Interface.html#Guru-Interface。我可以看看。)
  • 好的,还有一点评论:我尝试使用 FFTW3 的大师接口,将实部和虚部的拆分数组(实际上,我不得不将所有内容都塞进一个数组中,因为对于 FFTW,相对内存位置实部和虚部需要修复)并且观察到与std::complex 版本相比没有真正的改进。所以我会坚持这一点,因为它使代码更具可读性。
猜你喜欢
  • 2023-03-23
  • 2014-12-12
  • 2021-05-05
  • 2011-12-27
  • 1970-01-01
  • 2012-06-09
  • 2019-11-07
  • 2017-11-30
  • 1970-01-01
相关资源
最近更新 更多