优化n体模拟

时间:2019-12-17 21:06:03

标签: c algorithm optimization

我正在尝试优化n体算法,我发现最昂贵的功能是这样的:

run

使用性能记录,我可以看到除法是最昂贵的指令,该指令的复杂度为O(n ^ 2),但我真的不知道如何优化它。

2 个答案:

答案 0 :(得分:6)

转换

for(int i=0;i<N;i++)
for(int j=0;j<N;j++)

进入

for(int i=0;  i<N;i++)
for(int j=i+1;j<N;j++)

进行重组以利用SIMD运算符,这可以使吞吐量提高四倍。

使用OpenMP在CPU上并行化循环,或者通过分流到GPU(OpenMP 4.5+)并行化循环。

了解有关Barnes-Hut algorithm的知识,该方法将粒子分组以实现 O(N log N)复杂度(低于您的 O(N ^ 2))。

答案 1 :(得分:2)

对于SIMD来说,这实际上是一个不错的选择。值得注意的是:

real s = jMass / POW(distSqr,3.0/2.0);

如果您取消此功能,则可以重构为:(删除一个部分)

real s = jMass * POW(distSqr, -3.0/2.0);

现在值得注意的是,由于要处理非常简单的指数,因此您可以在此处完全删除对pow的调用。所以...

real s = jMass * std::sqrt(distSqr) / (distSqr * distSqr);

如果您知道自己的权力定律,则可以在此处执行其他重构步骤:

real s = jMass / (std::sqrt(distSqr) * distSqr);

现在运气不错,您的编译器应该应该已经为您执行了此转换(通常需要-O2和-ffast-math)。例: https://godbolt.org/z/8YqFYA

这很好的原因是,现在您已经从代码中完全删除了cmath调用。这使得掉落到simd之类的东西非常容易,如果您碰巧使用clang或gcc,则非常容易。例如

#include <immintrin.h>

typedef __m256 real;
struct real3 { real x, y, z; };

// i had to make up a value
const __m256 SOFTENING_SQUARED = _mm256_set1_ps(1.23f); 

real3 bodyBodyInteraction(real iPosx, real iPosy, real iPosz, 
                          real jPosx, real jPosy, real jPosz, real jMass)
{
  real rx, ry, rz;

  rx = jPosx - iPosx;
  ry = jPosy - iPosy;
  rz = jPosz - iPosz;

  real distSqr = rx*rx+ry*ry+rz*rz;
  distSqr += SOFTENING_SQUARED;

  real s = jMass / (_mm256_sqrt_ps(distSqr) * distSqr);

  real3 f;
  f.x = rx * s;
  f.y = ry * s;
  f.z = rz * s;

  return f;
}

而在螺栓中:

https://godbolt.org/z/JTCwm-