OpenMP 代码帮助

Posted

技术标签:

【中文标题】OpenMP 代码帮助【英文标题】:OpenMP help on code 【发布时间】:2012-03-24 11:12:33 【问题描述】:

这是我第一次发帖,所以我会为我的新手错误道歉。还请原谅并非所有变量名称都是英文的事实。我的问题如下:我在 Visual Studio 2010 和 eclipse 中使用 cygwin gcc 编译器工具链在 c/c++ 中使用 openMP 编写了此代码。在视觉上我得到了加速,但在日食中我得到了两倍于串行版本的减速。有人可以解释一下我做错了什么吗?简而言之,我只是在模拟从 3D 矢量数组复制到双精度数组以通过 MPI 发送时的加速。

#include <omp.h>
#include <time.h>
#include <stdio.h>
#include <vector>
const int NUMAR_FORME=10;
  const int NUMAR_SECUNDE_SIMULATE=60; //number of buffers
  const int dimensiuni_forme[10]=100,200,300,400,500,600,700,800,900,10000; //size of each buffer
  //-------- the buffers, cuurently only worker_buffer and buff is used
     std::vector<std::vector<std::vector<double> > > worker_buffer;
  std::vector<std::vector<double> > send_buffer,corect;
  double **buff;
   double **worker_buffer1;
  long i,j,k,l;
int flag=0;
int numarator=0; //number of tests runed
clock_t start;
start=clock();
worker_buffer.resize(1);
buff = new double* [2];
int de_scris=0; //this tells me in which buffer to store, nou I alternate buff[0], buff[1], buff[0], buff[1]
worker_buffer[0].resize(NUMAR_SECUNDE_SIMULATE);
for(i=0;i<NUMAR_SECUNDE_SIMULATE;i++)
    worker_buffer[0][i].resize(dimensiuni_forme[9]);



while(numarator<60)


        if(numarator!=0)
            delete [] buff[de_scris];

    if(numarator!=0)
        de_scris=(de_scris+1)%2;
    long limita;
    limita=NUMAR_SECUNDE_SIMULATE*dimensiuni_forme[9]*3; //3-comes from the fact that I will have a 3D vector structure
    buff[de_scris]= new double [limita];
    for(i=0;i<NUMAR_SECUNDE_SIMULATE;i++)
       for(j=0;j<dimensiuni_forme[9];j++)
        
            worker_buffer[0][i][j]=(i*dimensiuni_forme[9]+j)*3;
            buff[de_scris][(i*dimensiuni_forme[9]+j)*3]=worker_buffer[0][i][j];
            buff[de_scris][(i*dimensiuni_forme[9]+j)*3+1]=worker_buffer[0][i][j]+0.5;
            buff[de_scris][(i*dimensiuni_forme[9]+j)*3+2]=worker_buffer[0][i][j]+0.75;
        
    
    numarator++;



start=clock()-start;
printf("TICKS TOTAL %ld \n",start);
bool ad=true;
long nr;
for(i=0;i<NUMAR_SECUNDE_SIMULATE*dimensiuni_forme[9]*3;i++)

    if(i%3==0)
        nr=i;
    if(i%3==0 && buff[de_scris][i]!=i)
        ad=false;
    else
        if(i%3==1 &&buff[de_scris][i]!=(nr+0.5))
            ad=false;
        else
            if(i%3==2 && buff[de_scris][i]!=(nr+0.75))
                ad=false;

if(ad==false)
    printf("not correct \n");
start=clock();

    numarator=0;
//parallel version
while(numarator<60)



        if(numarator!=0)
            delete [] buff[de_scris];

    long index, limita,id;
    omp_set_num_threads(2);

    if(numarator!=0)
        de_scris=(de_scris+1)%2;
    limita=NUMAR_SECUNDE_SIMULATE*dimensiuni_forme[9]*3; //3-
    buff[de_scris]= new double [limita];
 #pragma omp parallel shared(worker_buffer,limita,buff) private(index,id)
    
        printf("intram cu %d threaduri \n", omp_get_num_threads());
        id=omp_get_thread_num();
        //index=id;
        for(index=id;(index*3)<limita;index+=omp_get_num_threads())
        
            buff[de_scris][index*3]=worker_buffer[0][index/dimensiuni_forme[9]][index%dimensiuni_forme[9]];  //aici va veni send_buff[index].x
            buff[de_scris][index*3+1]=buff[de_scris][index*3]+0.5;
            buff[de_scris][index*3+2]=buff[de_scris][index*3]+0.75;
        

    //  index+=omp_get_num_threads();


    //end parallel zone
    numarator++;


start=clock()-start;
printf("TICKS TOTAL %ld \n",start);
 ad=true;
    //testing for correctness
for(i=0;i<NUMAR_SECUNDE_SIMULATE*dimensiuni_forme[9]*3;i++)

    if(i%3==0)
        nr=i;
    if(i%3==0 && buff[de_scris][i]!=i)
        ad=false;
    else
        if(i%3==1 &&buff[de_scris][i]!=(nr+0.5))
            ad=false;
        else
            if(i%3==2 && buff[de_scris][i]!=(nr+0.75))
                ad=false;

if(ad==false)
    printf("not correct \n");
 return 0;
 

【问题讨论】:

您是否仅使用代码中的时钟功能来测量时间?如果是这样,您应该知道时钟返回使用的 cpu 时间,这意味着如果您使用 2 个处理器,clock() 返回的时间将增加两倍于实际时间。我不能 100% 确定 visucal c++ 是否以相同的方式实现 clock() 函数,这可能是您测量不同时间的原因。 我试过使用 time() 和 clock_gettime() 结果是一样的 【参考方案1】:

从你组织这个 for 循环的方式来看:

    for(index=id;(index*3)<limita;index+=omp_get_num_threads())
    
        buff[de_scris][index*3]=worker_buffer[0][index/dimensiuni_forme[9]][index%dimensiuni_forme[9]];  //aici va veni send_buff[index].x
        buff[de_scris][index*3+1]=buff[de_scris][index*3]+0.5;
        buff[de_scris][index*3+2]=buff[de_scris][index*3]+0.75;
    

假设您有 4 个线程,您的线程将获得交错的索引值:

thread 0: 0, 4,  8, 12,...
thread 1: 1, 5,  9, 13,...
thread 2: 2, 6, 10, 14,...
thread 3: 3, 7, 11, 15,...

这可能会导致缓存乒乓效应,因为不同线程写入的值可能会落在同一缓存行上,从而减慢您的执行速度。

尝试使用带有静态分区的简单 for 循环,以获得连续分区:

    #pragma omp parallel for
    for(index = 0; index < limita / 3;index++)
    
        buff[de_scris][index*3]=worker_buffer[0][index/dimensiuni_forme[9]][index%dimensiuni_forme[9]];  //aici va veni send_buff[index].x
        buff[de_scris][index*3+1]=buff[de_scris][index*3]+0.5;
        buff[de_scris][index*3+2]=buff[de_scris][index*3]+0.75;
    

【讨论】:

你是对的,非常感谢,我使用了你写的循环,它显示出加速。奇怪的是,它在不使用 for 指令的情况下如何在视觉上显示速度。 @Ray:很高兴为您提供帮助。如果它解决了您的问题,请不要忘记接受答案。

以上是关于OpenMP 代码帮助的主要内容,如果未能解决你的问题,请参考以下文章

如何在英特尔 MIC 上本地分析 openmp 代码?

用于矩阵乘法的 OpenMP

加速位图灰度转换,OpenMP 是 C# 中的一个选项吗?

OpenMP 矩阵向量乘法仅在一个线程上执行

使用 OpenMP 的 Viterbi 算法

如何在makefile中使用OpenMP?