使用openmp优化N-queen

时间:2013-09-29 13:35:53

标签: parallel-processing openmp

我正在学习OPENMP并编写以下代码来解决nqueens问题。

//Full Code: https://github.com/Shafaet/Codes/blob/master/OPENMP/Parallel%20N-  Queen%20problem.cpp
int n;

int call(int col,int rowmask,int dia1,int dia2)
{
    if(col==n) 
    {
        return 1;

    }
    int row,ans=0;
    for(row=0;row<n;row++)
    {
        if(!(rowmask & (1<<row)) & !(dia1 & (1<<(row+col))) & !(dia2 & (1<<((row+n-1)-col))))
        {           
            ans+=call(col+1,rowmask|1<<row,dia1|(1<<(row+col)), dia2|(1<<((row+n-1)-col)));
        }
    }
    return ans;

}

double parallel()
{
    double st=omp_get_wtime();
    int ans=0;
    int i;
    int rowmask=0,dia1=0,dia2=0;
     #pragma omp parallel for reduction(+:ans) shared(i,rowmask)
    for(i=0;i<n;i++)
    {
        rowmask=0;
        dia1=0,dia2=0;
        int col=0,row=i;
        ans+=call(1,rowmask|1<<row,dia1|(1<<(row+col)), dia2|(1<<((row+n-1)-col)));
    }
    printf("Found %d configuration for n=%d\n",ans,n);
    double en=omp_get_wtime();
    printf("Time taken using openmp %lf\n",en-st);
    return en-st;

}
double serial()
{

    double st=omp_get_wtime();
    int ans=0;
    int i;
    int rowmask=0,dia1=0,dia2=0;
    for(i=0;i<n;i++)
    {
        rowmask=0;
        dia1=0,dia2=0;
        int col=0,row=i;
        ans+=call(1,rowmask|1<<row,dia1|(1<<(row+col)), dia2|(1<<((row+n-1)-col)));
    }
    printf("Found %d configuration for n=%d\n",ans,n);
    double en=omp_get_wtime();
    printf("Time taken without openmp %lf\n",en-st);
    return en-st;

}
int main()
{
    double average=0;
    int count=0;
    for(int i=2;i<=13;i++)
    {
        count++;
        n=i;

        double stime=serial();
        double ptime=parallel();
        printf("OpenMP is %lf times faster for n=%d\n",stime/ptime,n);
        average+=stime/ptime;
        puts("===============");
    }
    printf("On average OpenMP is %lf times faster\n",average/count);
    return 0;

}

并行代码已经比普通代码更快但我想知道如何使用openmp pragma更好地优化它。我想知道我应该做些什么以获得更好的表现以及我不该做的事情。

提前致谢。

(请不要建议任何与并行编程无关的优化)

2 个答案:

答案 0 :(得分:0)

你的代码似乎使用经典的回溯N-Queens递归算法,这对于N-Queens求解并不是最快的,但是(由于简单性)是练习方面最生动的算法并行性基础知识。 这就是说:这很简单,因此你不要指望它自然地展示了许多先进的OpenMP手段,除了基本的“并行”和减少。

但是,只要您正在寻找学习并行性,并且可能为了更清晰和更好的学习曲线,还有一个(在许多可能的实现中)可用的实现,它使用相同的算法但从教育角度来看,往往更具可读性和生动性:

void setQueen(int queens[], int row, int col) {
//check all previously placed rows for attacks
for(int i=0; i<row; i++) {
   // vertical attacks
   if (queens[i]==col) {
       return;
   }

   // diagonal attacks
   if (abs(queens[i]-col) == (row-i) ) {
      return;
   }
}

// column is ok, set the queen
queens[row]=col;
if(row==size-1) {
#pragma omp atomic
    nrOfSolutions++;  //Placed final queen, found a solution
}
else {
     // try to fill next row
     for(int i=0; i<size; i++) {
         setQueen(queens, row+1, i);
     }
}
}

//Function to find all solutions for nQueens problem on size x size chessboard.
void solve() {
#pragma omp parallel for
    for(int i=0; i<size; i++) {
         // try all positions in first row
         int * queens = new int[size];  //array representing queens placed on a chess board.  Index is row position, value is column.
         setQueen(queens, 0, i);
         delete[](queens);
     }
}

这个给定的代码是Intel Advisor XE个样本之一(对于C ++和Fortran);给定样本的并行化方面将在给定Parallel Programming Book的第10章中以非常详细的方式进行讨论(事实上,给定章节仅使用N-Queens来演示如何使用工具来并行化串行代码 )。

鉴于Advisor n-queens样本使用与您的算法基本相同的算法,但它用+ atomic的简单并行组合替换了显式缩减。预计此代码效率较低,但更具“程序风格”且更具“教育性”,因为它展示了“隐藏”数据竞争。如果您上传给定的样本代码,您实际上将使用TBB,Cilk Plus和OpenMP(OMP用于C ++和Fortran)找到4个等效的N-Queens并行实现。

答案 1 :(得分:0)

我知道我对派对来说有点晚了,但你可以使用任务排队进行进一步的优化。(结果大约快7-10%)。不知道为什么。这是我正在使用的代码:

#include <iostream>  // std::cout, cin, cerr ...
#include <iomanip>   // modify std::out
#include <omp.h>

using namespace std;

int nrOfSolutions=0;
int size=0;

void print(int queens[]) {
  cerr << "Solution " << nrOfSolutions << endl; 
  for(int row=0; row<size; row++) {
    for(int col=0; col<size; col++) {
      if(queens[row]==col) {
  cout << "Q";
      }
      else {
  cout << "-";
      }
    }
    cout << endl;
  }
}

void setQueen(int queens[], int row, int col, int id) {

  for(int i=0; i<row; i++) {
    // vertical attacks
    if (queens[i]==col) {
      return;
    }
    // diagonal attacks
    if (abs(queens[i]-col) == (row-i) ) {
      return;
    }
  }

  // column is ok, set the queen
  queens[row]=col;

  if(row==size-1) {


    // only one thread should print allowed to print at a time
    {
      // increasing the solution counter is not atomic
#pragma omp critical
      nrOfSolutions++;
#ifdef _DEBUG
#pragma omp critical
      print(queens);
#endif
    }

  }
  else {
    // try to fill next row
    for(int i=0; i<size; i++) {
      setQueen(queens, row+1, i, id);
    }
  }
}

void solve() {
  int myid=0 ;

#pragma omp parallel
#pragma omp single
  {
      for(int i=0; i<size; i++) {
/*
#ifdef _OMP //(???)
  myid = omp_get_thread_num();  
#endif
#ifdef _DEBUG
  cout << "ThreadNum: " << myid << endl ;
#endif
  */
  // try all positions in first row
  // create separate array for each recursion
  // started here
#pragma omp task
    setQueen(new int[size], 0, i, myid);
      }
    }
}

int main(int argc, char*argv[]) {

  if(argc !=2) {
    cerr << "Usage: nq-openmp-taskq boardSize.\n";
    return 0;
  }

  size = atoi(argv[1]);
  cout << "Starting OpenMP Task Queue solver for size " << size << "...\n";

    double st=omp_get_wtime();
    solve();

    double en=omp_get_wtime();
    printf("Time taken using openmp %lf\n",en-st);

  cout << "Number of solutions: " << nrOfSolutions << endl;

return 0;
}