OpenMP:应该是理想情况下的巨大放缓

时间:2010-08-31 22:11:10

标签: openmp

在下面的代码中,我试图将数组的所有元素与嵌套for循环中的所有其他元素进行比较。 (这是运行一个简单的n体仿真。我只测试了4个核心上的4个线程的4个物体)。没有OpenMP修改的相同顺序版本的代码在大约15秒内运行25M次迭代。昨晚这段代码在大约30秒内完成。现在它运行大约1分钟!我认为问题可能在于线程必须写入通过指针传递给函数的数组 该数组在其他地方动态分配,由我定义的结构组成。这只是一种预感。我已经验证了4个线程在100%的4个独立核心上运行,并且它们正在正确访问阵列的元素。有什么想法吗?

void runSimulation (particle* particles, int numSteps){
  //particles is a pointer to an array of structs I've defined and allocated dynamically before calling the function
  //Variable Initializations


#pragma omp parallel num_threads(4) private(//The variables inside the loop) shared(k,particles) // 4 Threads for four cores
{
  while (k<numSteps){ //Main loop.  

    #pragma omp master //Check whether it is time to report progress.
    {
      //Some simple if statements
      k=k+1; //Increment step counter for some reason omp doesn't like k++
    }


    //Calculate new velocities
    #pragma omp for
    for (i=0; i<numParticles; i++){ //Calculate forces by comparing each particle to all others
      Fx = 0;
      Fy = 0;
      for (j=0; j<numParticles; j++){
        //Calcululate the cumulative force by comparing each particle to all others
      }
      //Calculate accelerations and set new velocities
      ax = Fx / particles[i].mass;
      ay = Fy / particles[i].mass;

                              //ARE THESE TWO LINES THE PROBLEM?!
      particles[i].xVelocity += deltaT*ax;
      particles[i].yVelocity += deltaT*ay;
    }           


    #pragma omp master
    //Apply new velocities to create new positions after all forces have been calculated.
    for (i=0; i<numParticles; i++){
      particles[i].x += deltaT*particles[i].xVelocity;
      particles[i].y += deltaT*particles[i].yVelocity;
    }

    #pragma omp barrier
  }
}
}

3 个答案:

答案 0 :(得分:1)

你正在颠倒缓存。所有内核都写入相同的共享结构,这将通过L2(最佳情况),L3或主内存/内存总线(最坏情况)在内核之间不断反弹。取决于共享内容的方式,这需要20到300个周期,而在L1中对私有内存的写入在理想条件下需要1个周期或更短的时间。

这解释了你的减速。

如果增加粒子数量,情况可能会变得不那么严重,因为你经常会写入不同的缓存行,所以会有更少的颠簸。上面的btown是建议私人阵列的正确想法。

答案 1 :(得分:0)

不确定这是否能解决问题,但您可能会尝试为每个线程提供完整数组的副本;问题可能是线程正在争夺访问共享内存,并且您看到很多缓存未命中。

我不确定你用来执行此操作的确切openmp语法,但请尝试这样做:

  • 分配内存以保存每个线程中的整个粒子数组;这样做一次,并保存所有四个新指针。
  • 在每个主循环迭代开始时,在主线程中,将主数组深度复制四次到每个新数组。您可以使用memcpy()快速完成此操作。
  • 进行计算,使得第一个线程写入索引0&lt;我&lt; numParticles / 4,等等。
  • 在主线程中,在应用新速度之前,通过仅复制相关索引将四个数组合并到主数组中。您可以使用memcpy()快速完成此操作。
  • 请注意,您可以并行化“应用新速度”循环而不会出现任何问题,因为每次迭代仅对单个索引进行操作;这可能是最简单的并行化部分。

与你的O(N ^ 2)计算相比,新操作只有O(N),所以从长远来看,它们不应该占用太多时间。绝对有办法优化我为你布置的步骤,Gabe,但我会留给你。

答案 2 :(得分:0)

我不同意问题是缓存抖动,因为struct粒子的大小必须超过缓存行的大小,只是从成员数量。

我认为更可能的罪魁祸首是初始化omp for的开销是1000个周期http://www.ualberta.ca/CNS/RESEARCH/Courses/2001/PPandV/OpenMP.Eric.pdf,并且循环中只有少量计算。我不会惊讶于只有4个机体的循环速度较慢。如果你有几个100的身体,情况会有所不同。我曾经在这样的循环上工作过,最后直接使用了pthreads。