假设您已经选择了最有效的算法来解决性能是第一优先的问题,而现在您正在实现它,那么您必须决定如下所示的细节:
v[i*3+0],v[i*3+1]和v[i*3+2]包含了粒子i的速度分量,我们要计算出总动能。考虑到所有粒子的质量都一样,人们可以写:
inline double sqr(double x)
{
    return x*x;
}
double get_kinetic_energy(double v[], int n)
{
    double sum = 0.0;
    for (int i=0; i < n; i++)
        sum += sqr(v[i*3+0]) + sqr(v[i*3+1]) + sqr(v[i*3+2]);
    return 0.5 * mass * sum;
}为了减少乘法次数,它可以写成:
double get_kinetic_energy(double v[], int n)
{
    double sum = 0.0;
    for (int i=0; i < n; i++)
    {
        double *w = v + i*3;
        sum += sqr(w[0]) + sqr(w[1]) + sqr(w[2]);
    }
    return 0.5 * mass * sum;
}(人们可以用更少的乘法来编写函数,但这不是问题的重点)
现在我的问题是:既然许多C编译器可以自动进行这种优化,那么开发人员应该在哪里依赖编译器,她/他应该在哪里尝试手动进行一些优化呢?
发布于 2022-02-16 20:27:18
看看gcc和clang如何处理您的代码,您所考虑的微观优化是徒劳的。编译器已经应用了标准的通用子表达式消除技术,这些技术移除了您想要消除的开销。
实际上,生成的代码一次使用XMM寄存器处理两个组件。
如果性能是必需的,那么下面是可以节省时间的步骤:
如果您有分析器,请使用
-mavx512f -mavx512cd允许gcc使用512位ZMM寄存器生成每次处理8个组件的代码。这是一种非侵入性的技术,因为代码不会改变,所以您不会冒险通过手工优化代码来引入新的bug。
优化是一门困难的艺术。在我的经验中,简化代码可以获得更好的结果和更少的bug,而不是以牺牲可读性和正确性为代价添加额外的微妙内容来提高性能。
看一看代码,一个明显的简化似乎会产生相同的结果,并可能为优化器的工作提供便利(但同样,让墙上的时钟来判断):
double get_kinetic_energy(const double v[], int n, double mass)
{
    double sum = 0.0;
    for (int i = 0; i < 3 * n; i++)
        sum += v[i] * v[i];
    return 0.5 * mass * sum;
}发布于 2022-02-16 16:13:11
开发人员应该在哪里依赖编译器,她/他应该在哪里尝试手动进行一些优化?
当我发现这个瓶颈时,
从那时起,你就可以开始研究系统特定的东西,以及算法本身--有太多的事情要看,无法用这样的答案来回答。优化低端微控制器的代码和64位的桌面PC (以及两者之间的一切)是一个巨大的区别。
发布于 2022-02-16 15:43:05
有一件事看上去有点像过早的优化,但可能只是对语言能力的无知,那就是您拥有描述粒子的所有信息,这些信息被压缩成一个double值数组。
相反,我建议您分解这一点,通过创建一个结构来保存每个粒子上的三个数据点,从而使您的代码更容易阅读。此时,您可以创建一个或多个粒子的函数,并对它们进行计算。
这将比将粒子参数数的三倍传递给函数或尝试“分割”数组要容易得多。如果对您来说更容易推理,那么生成警告/错误的可能性就会降低。
https://stackoverflow.com/questions/71144522
复制相似问题