1
#include <iostream>
#include <iomanip>
#include <fstream> 
#include <sstream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <omp.h>
using namespace std;


void output(float a[], float X[], float Y[], int I, int J)
{
  ofstream ft;
  int i;

  ft.open("flow.dat");
  ft<<"variables=\"x\",\"y\",\"a\""<<"\n"
    <<"zone f=point"<<"\n"
    <<"I="<<I<<",J="<<J<<"\n"
    <<endl;

  for(int i=0;i<I*J;i++)
    {
    ft<<setiosflags(ios::scientific)
      <<X[i]<<" "<<Y[i]<<" "<<a[i]<<endl;
    }

  ft.close();

}

void set(float a[], float X[], float Y[], int I, int J, float hx, float hy)
{
  for(int j=0;j<J;j++)
    for(int i=0;i<I;i++)
      {
        int iC=j*I+i;
        X[iC]=i*hx;
        Y[iC]=j*hy;
        a[iC]=0.0;
        if(j==J-1) a[iC]=1.0;
      }
}

void difference_serial(float a[],  int I, int J, const float hx, const float hy)
{
  const float aC=(hx*hx+hy*hy)*2;
  const float aX=hy*hy;
  const float aY=hx*hx;
  for(int j=1;j<J-1;j++)
    for(int i=1;i<I-1;i++)
      {
        int iC=j*I+i;
        int iL=iC-1;
        int iR=iC+1;
        int iU=iC+I;
        int iD=iC-I;
        a[iC]=(aX*(a[iL]+a[iR])+aY*(a[iU]+a[iD]))/aC;
      }


}

void difference_omp(float a[],  int I, int J, const float hx, const float hy)
{
  const float aC=(hx*hx+hy*hy)*2;
  const float aX=hy*hy;
  const float aY=hx*hx;

  int i,j,iC,iL,iR,iU,iD;
#pragma omp parallel for private(i,j,iC,iL,iR,iU,iD) shared(a,I,J) schedule(dynamic) 
  for( j=1;j<J-1;j++)
    for( i=1;i<I-1;i++)
      {
        iC=j*I+i;
        iL=iC-1;
        iR=iC+1;
        iU=iC+I;
        iD=iC-I;
        a[iC]=(aX*(a[iL]+a[iR])+aY*(a[iU]+a[iD]))/aC;
      }
}

int main()
{
  const int I=129;
  const int J=129;
  const int N=I*J;
  const float hx=1.0/(I-1);
  const float hy=1.0/(J-1);

  float *a=new float[N];
  float *X=new float[N];
  float *Y=new float[N];

  //set the grid and flow
  set(a,X,Y,I,J,hx,hy);

  //iteation
  clock_t start=clock();
  for(int it=0;it<10000;it++)
    difference_serial(a,I,J,hx,hy);
  clock_t end=clock();
  printf("Serial time=%f\n",(float)(end-start)/CLOCKS_PER_SEC);


  set(a,X,Y,I,J,hx,hy);
  clock_t start2=clock();
  for(int it2=0;it2<10000;it2++)
    difference_omp(a,I,J,hx,hy);
  clock_t end2=clock();
  printf("Omp time=%f\n",(float)(end2-start2)/CLOCKS_PER_SEC);

  //output
  output(a,X,Y,I,J);

  //free memory
  delete[] a;
  delete[] X;
  delete[] Y;
}

非常に単純なラプラス方程式を 2 次元で解くコードを作成します。シリアルコードと OpenMP コードを比較してみる

g++ tmp.cpp -fopenmp でコードをコンパイルしようとしました

非常に奇妙な結果出力が得られます: Serial time=1.620000 Omp time=9.820000

この背後にある理由とOpenMPコードの修正方法を理解するのを手伝ってくれる人はいますか?

4

1 に答える 1

7

私は面白い結果に出くわしました。

luk32:~/projects/tests$ g++ -fopenmp -lgomp ./laplace.cpp 
luk32:~/projects/tests$ ./a.out 
Omp time=13.000000
Serial time=3.000000
luk32:~/projects/tests$ g++ -O3 -fopenmp -lgomp ./laplace.cpp 
luk32:~/projects/tests$ ./a.out 
Omp time=31.000000
Serial time=1.000000

そのO3ため、OpenMPの場合は時間が悪化し、シリアルバージョンの場合は削除されました。私の推測では、問題のインスタンスは非常に小さいため、並列領域を呼び出すことによる実際のオーバーヘッドがここに現れています。

PCで1.5秒/10k=0.15ミリ秒かかるものを並列化しようとしています。スレッドプールとスケジューリングの初期化には、特にschedule(dynamic)

確認するためにいくつかのテストを試みます。ランダムにぶつかって合法かどうかわからIないJ

テスト後:

OK切り替えJ=I=10240;て設定しましたfor(int it=0;it<50;it++)。時間の測定にも使用omp_get_wtime()しました。以下は完全なdiffファイルです。

結果は次のとおりです。

Serial time=58.982189
Omp time=9.158118

これは、6-phys/12-論理コアマシンで実行されました。これで、結果は期待どおりになりました。OpenMPあなたの例の問題は、オーバーヘッドが計算よりも長くかかった時点まで効率的にするには小さすぎました。

相違:

luk32:~/projects/tests$ diff laplace.orig.cpp laplace.cpp
88,89c88,89
<   const int I=129;
<   const int J=129;
---
>   const int I=10000;
>   const int J=10000;
102,103c102,103
<   clock_t start=clock();
<   for(int it=0;it<10000;it++)
---
>   double start=omp_get_wtime();
>   for(int it=0;it<50;it++)
105,106c105,106
<   clock_t end=clock();
<   printf("Serial time=%f\n",(float)(end-start)/CLOCKS_PER_SEC);
---
>   double end=omp_get_wtime();
>   printf("Serial time=%f\n",(float)(end-start));
110,111c110,111
<   clock_t start2=clock();
<   for(int it2=0;it2<10000;it2++)
---
>   double start2=omp_get_wtime();
>   for(int it2=0;it2<50;it2++)
113,114c113,114
<   clock_t end2=clock();
<   printf("Omp time=%f\n",(float)(end2-start2)/CLOCKS_PER_SEC);
---
>   double end2=omp_get_wtime();
>   printf("Omp time=%f\n",(float)(end2-start2));

編集:私は主な問題を太字にしたので、これに遭遇した人は誰でも自動的にそれに集中します。

于 2013-03-04T16:56:57.010 に答える