我想把一个小次多项式(2-5)应用到一个长度可以在50到3000之间的向量上,并尽可能有效地这样做。例如:我们可以取函数:(1+x^2)^3,当x>3时,0当x<=3时,这样的函数将对双元素的向量执行100 k次。每个向量的大小可以是50到3000之间的任何东西。
一种方法是使用本征: Eigen::ArrayXd v;然后简单地应用函子: v.unaryExpr(& {返回x>3?性病::pow((1+x*x),3.00):0.00;};
在GCC 9和GCC 10的尝试中,我发现这个循环没有被矢量化。我手动地将它矢量化,结果发现增益比我预期的要小得多(1.5倍)。我还用逻辑和指令代替了条件反射,基本上是在x<=3时同时执行分支和对结果进行归零,我认为增益主要来自于没有分支错误预测。
一些考虑,有多个因素在起作用。首先,我的代码中存在原始依赖项(使用本质)。我不知道这对计算有何影响。我用AVX2编写了我的代码,所以我期望得到4倍的收益。我认为这起了一定的作用,但我不能肯定,因为CPU有无序处理。另一个问题是,我不确定我试图编写的循环的性能是否受到内存带宽的限制。
问题如何确定内存带宽或管道危险是否影响此循环的实现?我在哪里可以学到更好地向量化这个循环的技术?在Eigenr、MSVC或Linux中,是否有这方面的好工具?我使用的是AMD CPU,而不是英特尔。
发布于 2020-08-14 08:20:14
您可以用-fno-trapping-math修复GCC错过的优化,这应该是默认的,因为-ftrapping-math甚至不能完全工作。它可以自动矢量化这个选项:https://godbolt.org/z/zfKjjq。
#include <stdlib.h>
void foo(double *arr, size_t n) {
for (size_t i=0 ; i<n ; i++){
double &tmp = arr[i];
double sqrp1 = 1.0 + tmp*tmp;
tmp = tmp>3 ? sqrp1*sqrp1*sqrp1 : 0;
}
}它避免了三元的一边的乘数,因为它们可能会引发FP异常,而C++抽象机器则不会。
你会希望用三值外的立方来写它应该让GCC自动矢量化,因为FP的数学运算在源文件中都没有条件。但这实际上没有帮助:https://godbolt.org/z/c7Ms9G GCC的默认-ftrapping-math仍然决定在输入上分支,以避免所有FP计算,可能不会引发C++抽象机器会引发的溢出(到无穷大)异常。如果输入是NaN,则无效。这就是我对-ftrapping-math不起作用的意思。(相关:How to force GCC to assume that a floating-point expression is non-negative?)
Clang也没有问题:当FMA可用时,我建议使用https://godbolt.org/z/KvM9fh来跨语句使用clang -O3 -march=native -ffp-contract=fast。
(在这种情况下,-ffp-contract=on就足以在该表达式中收缩1.0 + tmp*tmp,但如果您需要避免使用Kahan求和,则不需要跨语句。clang默认值显然是-ffp-contract=off,给出了不同的mulpd和addpd)
当然,您希望避免使用小整数指数的std::pow。编译器可能不会将其优化为2个乘法,而是调用一个完整的pow函数。
https://stackoverflow.com/questions/63408407
复制相似问题