C++ 我的循环还能再优化吗?

C++ 我的循环还能再优化吗?,c++,math,optimization,loops,physics,C++,Math,Optimization,Loops,Physics,下面是我最里面的循环,它运行了几千次,输入大小为20-1000或更多。这段代码占用99-99.5%的执行时间。我能做些什么来帮助你从中挤出更多的表现吗 我不打算将此代码移到类似使用树代码(Barnes-Hut)的地方,而是要优化内部发生的实际计算,因为在Barnes-Hut算法中也会发生相同的计算 感谢您的帮助 编辑:我在Windows7 64位上运行,在核心2 Duo T5850(2.16GHz)上运行VisualStudio2008Edition typedef双实数; 结构粒子 { 矢量位

下面是我最里面的循环,它运行了几千次,输入大小为20-1000或更多。这段代码占用99-99.5%的执行时间。我能做些什么来帮助你从中挤出更多的表现吗

我不打算将此代码移到类似使用树代码(Barnes-Hut)的地方,而是要优化内部发生的实际计算,因为在Barnes-Hut算法中也会发生相同的计算

感谢您的帮助

编辑:我在Windows7 64位上运行,在核心2 Duo T5850(2.16GHz)上运行VisualStudio2008Edition

typedef双实数;
结构粒子
{
矢量位置、水平、加速度、加速度;
向量oldPos、oldVel、oldAcc、oldJerk;
实际质量;
};
类向量
{
私人:
实vec[3];
公众:
//此处定义的运算符
};
真实重力::相互作用(粒子*p,大小\u t个粒子)
{
PROFILE_FUNC();
实际tau_q=1e300;
对于(大小i=0;i
是。尝试查看程序集输出。它可能会提供编译器哪里出错的线索

现在,请始终首先应用算法优化,并且只有当没有更快的算法可用时,才应按汇编进行逐段优化。然后,先做内部循环

首先,您可能想分析一下这是否真的是瓶颈

  • 内联调用lengthsq()

  • 将pow(r2,-1.5)更改为1/(r2*sqrt(r2)),以降低计算r^1.5的成本

  • 在Inner most循环中使用标量(p_i_acc等),而不是p[i].acc来收集结果。编译器可能不知道p[i]不是p[j]的别名,这可能会不必要地强制在每个循环迭代中对p[i]进行寻址

  • 4a。尝试将if(…)tau_q=替换为

        tau_q=minimum(...,...)
    
    许多编译器将mininum函数识别为可以使用谓词操作而不是实际分支来执行的函数,从而避免了管道刷新

    4b。[编辑分割4a和4b分开]您可以考虑存储Tauiq.Q2而不是Tauq q,并与R2/V2进行比较,而不是R2*R2/V2*V2。然后,避免在内部循环中对每次迭代执行两次乘法,以换取在最后计算tau..q2的一次平方运算。为此,分别收集tau_q1和tau_q2的最小值(非平方),并在循环完成时在单个标量操作中取这些结果的最小值]

  • [编辑:我提出了以下建议,但实际上这对OP的代码无效,因为他在循环中更新的方式。]将两个循环折叠在一起。有了这两个循环和足够大的粒子集,就可以重击缓存并强制从非缓存中重新蚀刻第二个循环中的那些初始值。折叠是琐碎的事
  • 在此之外,你需要考虑一个循环展开,b)矢量化(使用SIMD指令;或者手工编码汇编程序或使用英特尔编译器,它应该是相当好的,但是我没有经验),C)使用多核(使用OpenMP)。< /P> < UL>

  • 首先,您需要分析代码。此方法将取决于您正在运行的CPU和操作系统

  • 你可以考虑使用浮动而不是双打。

  • 如果您使用的是gcc,那么请确保您使用的是
    -O2
    或者可能是
    -O3

  • 您可能还想尝试一个好的编译器,比如Intel的ICC(假设它在x86上运行?)

  • 再次假设这是(英特尔)x86,如果您有64位CPU,那么构建一个64位可执行文件(如果您还没有),额外的寄存器可以产生明显的差异(大约30%)


  • 这一行
    real r3i=常数::G*pow(r2,-1.5)会受伤的。任何类型的sqrt查找或具有平方根的特定于平台的帮助都会有所帮助

    如果你有simd能力,把向量减法和平方分解成自己的循环,一次计算它们会有帮助。质量/挺举计算也一样

    我想到的是——你的计算精度是否足够高?把东西带到第四次方和第四次方,真的可以通过下/溢出混合器来搅动你的可用位。我确信你的答案在完成时确实是你的答案

    除此之外,它是一个数学量很大的函数,需要一些CPU时间。汇编程序对此的优化不会产生比编译器已经能为您做的更多的结果

    另一个想法。由于这似乎与重力有关,有没有办法根据距离检查剔除你繁重的数学题?基本上是半径/
        tau_q=minimum(...,...)
    
      for (size_t i = 0; i < numParticles; i++)
        {
            for (size_t j = i+1; j < numParticles; j++)
            {
    
    float InvSqrt(float x)
    {
        union {
            float f;
            int i;
        } tmp;
        tmp.f = x;
        tmp.i = 0x5f3759df - (tmp.i >> 1);
        float y = tmp.f;
        return y * (1.5f - 0.5f * x * y * y);
    }
    
    struct ParticleData
    {
        Vector pos, vel, acc, jerk;
    };
    
    ParticleData* currentParticles = ...
    ParticleData* oldParticles = ...
    real* masses = ...
    
    struct ParticleData
    {
        // data_x[0] == pos.x, data_x[1] = vel.x, data_x[2] = acc.x, data_x[3] = jerk.x
        Vector4 data_x;
    
        // data_y[0] == pos.y, data_y[1] = vel.y, etc.
        Vector4 data_y;
    
        // data_z[0] == pos.z, data_y[1] = vel.z, etc.
        Vector4 data_z;
    };
    
    Vector r;
    Vector v;
    real r2;
    real v2;
    Vector da;
    Vector dj;
    real r3i;
    real mij;
    real tau_est_q1;
    real tau_est_q2;
    for (size_t i = 0; i < numParticles; i++)
        {
            for (size_t j = i+1; j < numParticles; j++)
            {
                r = p[j].pos - p[i].pos;
                v = p[j].vel - p[i].vel;
                r2 = lengthsq(r);
                v2 = lengthsq(v);
    
                // Calculate inverse of |r|^3
                r3i = Constants::G * pow(r2, -1.5);
    
                // da = r / |r|^3
                // dj = (v / |r|^3 - 3 * (r . v) * r / |r|^5
                da = r * r3i;
                dj = (v - r * (3 * dot(r, v) / r2)) * r3i;
    
                // Calculate new acceleration and jerk
                p[i].acc += da * p[j].mass;
                p[i].jerk += dj * p[j].mass;
                p[j].acc -= da * p[i].mass;
                p[j].jerk -= dj * p[i].mass;
    
                // Collision estimation
                // Metric 1) tau = |r|^2 / |a(j) - a(i)|
                // Metric 2) tau = |r|^4 / |v|^4
                mij = p[i].mass + p[j].mass;
                tau_est_q1 = r2 / (lengthsq(da) * mij * mij);
                tau_est_q2 = (r2*r2) / (v2*v2);
    
                if (tau_est_q1 < tau_q)
                    tau_q = tau_est_q1;
                if (tau_est_q2 < tau_q)
                    tau_q = tau_est_q2;
            }
        }
    
    a = b/c
    d = e/f
    
    icf = 1/(c*f)
    a = bf*icf
    d = ec*icf