使用OpenMP进行并行加速

时间:2011-10-28 05:30:08

标签: performance parallel-processing openmp

我有两种测量指标的方案,如计算时间和并行加速(sequential_time / parallel_time)。

情景1:

顺序时间测量:

startTime=omp_get_wtime();  
for loop computation  
endTime=omp_get_wtime();  
seq_time = endTime-startTime;

平行时间测量:

startTime = omp_get_wtime();  
for loop computation (#pragma omp parallel for reduction (+:pi) private (i)  
for (blah blah) {   
    computation;   
}  
endTime=omp_get_wtime();  
paralleltime = endTime-startTime; 

speedup = seq_time/paralleltime;

情景2:

顺序时间测量:

for loop{  
startTime=omp_get_wtime();  
   computation;  
endTime=omp_get_wtime();  
seq_time += endTime-startTime;  
}

平行时间测量:

for loop computation (#pragma omp parallel for reduction (+:pi, paralleltime) private (i,startTime,endTime)  
for (blah blah) {  
    startTime=omp_get_wtime();  
    computation;  
    endTime=omp_get_wtime();  
    paralleltime = endTime-startTime;  
}  

speedup = seq_time/paralleltime;

我知道场景2不是最好的生产代码,但我认为它通过忽略openmp生成和管理(线程上下文切换)几个线程所涉及的开销来衡量实际的理论性能。所以它会给我们一个线性加速。但是场景1考虑了产生和管理线程所涉及的开销。

我怀疑是这样的: 在场景1中,我得到一个开始线性的加速,但随着我们进行更多次迭代而逐渐减少。在场景2中,无论迭代次数如何,我都会获得完整的线性加速。我被告知,实际上,无论迭代次数如何,场景1都会给我一个线性加速。但我认为它不会因为线程管理导致的高过载。有人可以向我解释为什么我错了吗?

谢谢!对于相当长的帖子感到抱歉。

4 个答案:

答案 0 :(得分:5)

在很多情况下,方案2也不会给你线性加速 - 线程之间的错误共享(或者,就此而言,真正共享被修改的共享变量),内存带宽争用等。子线性加速通常是真实的,而不是测量神器。

更一般地说,一旦你到了将时间器置于 for循环中的那一点,你就会考虑使用比使用这样的定时器测量更精细的时序信息。您可能希望能够将线程管理开销从实际工作中解脱出来,原因有多种,但是您可以尝试通过向omp_get_wtime()插入N个额外函数调用来实现这一点,以及算术和简化操作,所有这些操作都有不可忽视的开销。

如果你真的想要准确计算在computation;行中花费多少时间,你真的想要使用像采样而不是手动仪器这样的东西(我们会谈谈区别{{3} })。使用heregprofscalasca(所有免费软件)或英特尔的VTune(商业软件包)将为您提供有关该线路上花费的时间的信息 - 通常甚至是线程 - 开销要低得多。

答案 1 :(得分:5)

首先,通过加速的定义,您应该使用方案1,其中包括并行开销。


在方案2中,paralleltime的测量中的代码错误。为了满足方案2中的目标,您需要通过分配paralleltime并通过int paralleltime[NUM_THREADS]访问它们来获得每个线程omp_get_thread_num()(请注意,此代码将具有错误共享,因此您最好用padding分配64字节结构。然后,测量每线程计算时间,最后采用最长的来计算不同类型的加速(我说的是一种并行性)。


不,即使是场景2,您也可能会看到亚线性加速,甚至可以获得超线性加速。潜在的原因(即排除并行开销)是:

  1. 负载不平衡:compuation中的工作负载长度在迭代时不同。这将是低速加速的最常见原因(但是,你说负载不平衡并非如此)。
  2. 同步成本:如果存在任何类型的同步(例如,互斥,事件,屏障),您可能有等待/阻止时间。
  3. 缓存和内存成本:当computation需要大带宽和高工作集时,并行代码可能会受到带宽限制(尽管实际上很少见)和缓存冲突。此外,虚假共享是一个重要原因,但很容易避免它。也可以观察到超线性效应,因为使用多核可能有更多的缓存(即私有L1 / L2缓存)。
  4. 在方案1中,它将包括并行库的开销:

    1. 分叉/加入线程:尽管大多数并行库实现都不会在每个并行构造上进行物理线程创建/终止。
    2. 调度/加入逻辑任务:即使已经创建了物理线程,您也需要将逻辑任务分派给每个线程(通常是M任务到N线程),并且还要进行一种加入最后的操作(例如,隐含障碍)。
    3. 调度开销:对于静态调度(如代码所示,使用OpenMP的静态调度),开销很小。当工作负载足够时(例如0.1秒),您可以安全地忽略开销。但是,动态调度(例如TBB中的工作窃取)会产生一些开销,但是一旦您的工作量足够,它就不会很重要。
    4. 我不认为您的代码(1级静态调度并行循环)由于线程管理而确实具有高并行开销,除非此代码每秒被调用数百万次。所以,可能是我在上面提到的其他原因。

      请记住,有很多因素会决定加速;从固有的并行性(=负载不平衡和同步)到并行库的开销(例如,调度开销)。

答案 2 :(得分:2)

您想要准确测量什么?由并行性引起的开销是实际执行时间的一部分,因此恕我直言方案1更好。

此外,根据您的OpenMP指令,您正在减少某些数组。在方案1中,您考虑到了这一点。在方案2中,你不是。所以基本上,你测量的东西比方案1少。这可能对你的测量产生一些影响。

否则,Jonathan Dursi的回答非常好。

答案 3 :(得分:0)

OpenMP有多个选项可供选择如何在线程之间分配工作。这会影响测量方法1的线性度。您的测量方法2似乎没有用。你想要用什么来解决这个问题?如果您想了解单线程性能,请运行单个线程。如果您想要并行性能,则需要包含开销。