加速简单 Eigen 程序的基本方法

Basic ways to speed up a simple Eigen program

我正在寻找使用 Eigen 进行简单操作的最快方法。可用的数据结构太多了,很难说哪个是最快的。

我尝试过预定义我的数据结构,但即便如此,我的代码仍被类似的 Fortran 代码超越。我猜 Eigen::Vector3d 是满足我需求的最快方法(因为它是预定义的),但我很容易出错。在编译时使用 -O3 优化给了我很大的提升,但我仍然 运行 比相同代码的 Fortran 实现慢 4 倍。

我使用 'Atom' 结构,然后将其存储在由以下定义的 'atoms' 向量中:

struct Atom {
    std::string element;
    //double x, y, z;
    Eigen::Vector3d coordinate;
};
std::vector<Atom> atoms;

我的代码中最慢的部分如下:

distance = atoms[i].coordinate - atoms[j].coordinate;
distance_norm = distance.norm();

我可以使用更快的数据结构吗?或者有没有更快的方法来执行这些基本操作?

鉴于您大约有 4 倍的折扣,可能值得检查您是否在编译时启用了矢量化,例如 AVX 或 AVX2。处理双打时当然还有SSE2(~2x)和AVX512(~8x)

要么尝试其他编译器,如 Intel C++ 编译器(免费供学术和 non-profit 使用),要么使用其他库,如 Intel MKL(比你自己的代码快得多)或什至其他 BLAS/LAPACK 实现密集矩阵或 PARDISO 或 SuperLU(不确定是否仍然存在)用于稀疏矩阵。

正如您在评论中指出的那样,添加 -fno-math-errno 编译器标志可以大大提高速度。至于为什么会发生这种情况,您的代码被剪断表明您正在通过 distance_norm = distance.norm();.

执行 sqrt

这使得编译器不会在每次 sqrt 之后设置 ERRNO(这是对线程局部变量的保存写入),这更快 启用任何正在执行此操作的循环的矢量化 repeatedly.The 唯一的缺点是失去了 IEEE 遵守。参见 gcc man

你可能想尝试的另一件事是添加 -march=native 并添加 -mfma 如果 -march=native 没有为你打开它(我似乎记得在某些情况下它未被 native 打开,必须手动打开 - check here for details)。与 Eigen 一样,您可以使用 -DNDEBUG.

禁用边界检查

SoA 而不是 AoS!!!如果性能实际上是一个真正的问题,请考虑使用单个 4xN 矩阵来存储位置(并让 Atom 保留列索引而不是 Eigen::Vector3d)。在您显示的小代码片段中应该没有太大关系,但根据您的其余代码,可能会给您带来另一个巨大的性能提升。