在 C++ 中使用 OpenMP 并行化算法

Posted

技术标签:

【中文标题】在 C++ 中使用 OpenMP 并行化算法【英文标题】:Parallelize Algorithm with OpenMP in C++ 【发布时间】:2018-10-27 16:10:56 【问题描述】:

我的问题是这样的:

我想用 C++ 中的蚁群优化算法解决 TSP。 现在我已经实现了一个迭代解决这个问题的算法。

例如:我生成了 500 只蚂蚁——它们一个接一个地找到自己的路线。 每只蚂蚁直到前一只蚂蚁完成后才开始。

现在我想并行化整个事情 - 我考虑过使用 OpenMP。

所以我的第一个问题是:我可以生成大量工作的线程吗 同时(蚂蚁数量> 500)?

我已经尝试了一些东西。这是我的 main.cpp 中的代码:

 #pragma omp parallel for       
    for (auto ant = antarmy.begin(); ant != antarmy.end(); ++ant) 
        #pragma omp ordered
        if (ant->getIterations() < ITERATIONSMAX) 
            ant->setNumber(currentAntNumber);
            currentAntNumber++;
            ant->antRoute();
        

    

这是我的 Ant 类中“关键”的代码,因为每个 Ant 读取和写入同一个矩阵(信息素矩阵):

 void Ant::antRoute()
 
     this->route.setCity(0, this->getStartIndex());
     int nextCity = this->getNextCity(this->getStartIndex());
     this->routedistance += this->data->distanceMatrix[this->getStartIndex()][nextCity];
     int tempCity;
     int i = 2;
     this->setProbability(nextCity);
     this->setVisited(nextCity);
     this->route.setCity(1, nextCity);
     updatePheromone(this->getStartIndex(), nextCity, routedistance, 0);

     while (this->getVisitedCount() < datacitycount) 
         tempCity = nextCity;
         nextCity = this->getNextCity(nextCity);
         this->setProbability(nextCity);
         this->setVisited(nextCity);
         this->route.setCity(i, nextCity);
         this->routedistance += this->data->distanceMatrix[tempCity][nextCity];
         updatePheromone(tempCity, nextCity, routedistance, 0);
         i++;
     

     this->routedistance += this->data->distanceMatrix[nextCity][this->getStartIndex()];
     // updatePheromone(-1, -1, -1, 1);
     ShortestDistance(this->routedistance);
     this->iterationsshortestpath++;


void Ant::updatePheromone(int i, int j, double distance, bool reduce)


     #pragma omp critical(pheromone) 

     if (reduce == 1) 
        for (int x = 0; x < datacitycount; x++) 
             for (int y = 0; y < datacitycount; y++) 
                 if (REDUCE * this->data->pheromoneMatrix[x][y] < 0)
                     this->data->pheromoneMatrix[x][y] = 0.0;
                 else
                    this->data->pheromoneMatrix[x][y] -= REDUCE * this->data->pheromoneMatrix[x][y];
             
         
     
     else 

         double currentpheromone = this->data->pheromoneMatrix[i][j];
         double updatedpheromone = (1 - PHEROMONEREDUCTION)*currentpheromone + (PHEROMONEDEPOSIT / distance);

         if (updatedpheromone < 0.0) 
            this->data->pheromoneMatrix[i][j] = 0;
            this->data->pheromoneMatrix[j][i] = 0;
         
          else 
             this->data->pheromoneMatrix[i][j] = updatedpheromone;
             this->data->pheromoneMatrix[j][i] = updatedpheromone;
         
     

 

因此,由于某些原因,omp 并行 for 循环无法在这些基于范围的循环上工作。 所以这是我的第二个问题 - 如果你们对如何完成基于范围的循环的代码有任何建议我很高兴。

感谢您的帮助

【问题讨论】:

您不需要大于硬件并行化的线程数,即系统上的逻辑 CPU 内核数 【参考方案1】:

所以我的第一个问题是:我能否生成大量同时工作的线程(蚂蚁数量 > 500)?

在 OpenMP 中,您通常不应该关心有多少线程处于活动状态,而是确保通过工作共享结构(例如 omp foromp task)公开足够的并行工作。因此,虽然您可能有一个包含 500 次迭代的循环,但您的程序可以运行在一个线程和 500 个线程(或更多,但它们只是空闲)之间的任何线程。这与其他并行化方法不同,例如 pthreads,您必须管理所有线程以及它们的作用。

现在您的示例错误地使用了ordered。仅当循环体的一小部分需要按顺序执行时,Ordered 才有用。即便如此,它也可能对性能造成很大的问题。如果你想在里面使用ordered,你还需要声明一个循环为ordered。另见this excellent answer。

您不应该使用有序。相反,请确保蚂蚁事先知道number,编写代码使得它们不需要数字,或者至少数字的顺序对蚂蚁来说无关紧要。在后一种情况下,您可以使用omp atomic capture

关于访问共享数据。尽量避免它。添加omp critical 是获得正确并行程序的第一步,但通常会导致性能问题。衡量您的并行效率,使用并行性能分析工具来确定您是否属于这种情况。然后可以使用原子数据访问或归约(每个线程都有自己工作的数据,只有在主要工作完成后,才会合并来自所有线程的数据)。

【讨论】:

以上是关于在 C++ 中使用 OpenMP 并行化算法的主要内容,如果未能解决你的问题,请参考以下文章

使用 OpenMP 在 C、C++ 中并行化嵌套 for 循环的几种方法之间的区别

如何使用 OpenMP 通过 C++ std::list 并行化 for 循环?

C++ 并行化库:OpenMP 与线程构建块 [关闭]

C++ Armadillo 和 OpenMp:外积求和的并行化 - 定义 Armadillo 矩阵的约简

C++ OpenMP 和 gcc 4.8.1 - 并行化循环时的性能问题

非for循环的OpenMP并行化