OpenMP:为啥这个应用程序有时会扩展?
Posted
技术标签:
【中文标题】OpenMP:为啥这个应用程序有时会扩展?【英文标题】:OpenMP: why this application scale sometimes?OpenMP:为什么这个应用程序有时会扩展? 【发布时间】:2016-11-08 01:45:22 【问题描述】:我正在尝试在Intel® Core™ i5-6500 CPU @ 3.20GHz × 4
上使用 OpenMP 加速 OpenCV SIFT 算法。您可以在sift.cpp
找到代码。
最昂贵的部分是描述符计算,特别是:
static void calcDescriptors(const std::vector<Mat>& gpyr, const std::vector<KeyPoint>& keypoints,
Mat& descriptors, int nOctaveLayers, int firstOctave )
int d = SIFT_DESCR_WIDTH, n = SIFT_DESCR_HIST_BINS;
for( size_t i = 0; i < keypoints.size(); i++ )
KeyPoint kpt = keypoints[i];
int octave, layer;
float scale;
unpackOctave(kpt, octave, layer, scale);
CV_Assert(octave >= firstOctave && layer <= nOctaveLayers+2);
float size=kpt.size*scale;
Point2f ptf(kpt.pt.x*scale, kpt.pt.y*scale);
const Mat& img = gpyr[(octave - firstOctave)*(nOctaveLayers + 3) + layer];
float angle = 360.f - kpt.angle;
if(std::abs(angle - 360.f) < FLT_EPSILON)
angle = 0.f;
calcSIFTDescriptor(img, ptf, angle, size*0.5f, d, n, descriptors.ptr<float>((int)i));
此函数的串行版本平均占用52 ms
。
这个for
具有很高的粒度:它被执行了604
次(即 keypoints.size() )。 for
内部的主要耗时组件是 calcSIFTDescriptor
,它占用了大部分循环时间计算,平均占用 105 us
,但经常会占用 200us
或 50us
。
但是,我们非常幸运:每个 for
循环之间没有依赖关系,所以我们可以添加:
#pragma omp parallel for schedule(dynamic,8)
并获得初始加速。引入了dynamic
选项,因为它的性能似乎比static
好一点(不知道为什么)。
问题在于它真的很不稳定并且无法扩展。这是在并行模式下计算函数所需的时间:
25ms 43ms 32ms 15ms 27ms 53ms 21ms 24ms
您只能在达到四核系统的最佳加速比时看到 (15ms
)。大多数情况下,我们会达到最佳加速比的一半:四核系统中的25ms
只是理论最佳加速比的一半。
为什么会这样?我们该如何改进呢?
更新:
正如 cmets 中所建议的,我尝试使用更大的数据集。使用一个巨大的图像,串行版本使用13574ms
来计算描述符,而并行版本3704ms
使用与之前相同的四核。好多了:即使它不是最好的理论结果,它实际上也可以很好地扩展。但实际上问题仍然存在,因为之前的结果是从典型图像中获得的。
更新 1: 正如评论所建议的,我尝试在“热模式”下的执行之间没有任何间隔进行基准测试(有关更多详细信息,请参阅评论)。更频繁地获得更好的结果,但仍然有很多变化。这是在热模式下运行 100 次的时间(以毫秒为单位):
43 42 14 26 14 43 13 26 15 51 15 20 14 40 34 15 15 31 15 22 14 21 17 15 14 27 14 16 14 22 14 22 15 15 14 43 16 16 15 28 14 24 14 36 15 32 13 21 14 23 14 15 13 26 15 35 13 32 14 36 14 34 15 40 28 14 14 15 15 35 15 22 14 17 15 23 14 24 17 16 14 35 14 29 14 25 14 32 14 28 14 34 14 30 22 14 15 24 14 31
你可以看到很多好的结果(14ms
、15ms
),但也有很多糟糕的结果(>40ms
)。平均值为22ms
注意在顺序模式下最多没有4ms
的变化:
52 54 52 52 51 52 52 53 53 52 53 51 52 53 53 54 53 53 53 53 54 53 54 54 53 53 53 52 53 52 51 52 52 53 54 54 54 55 55 55 54 54 54 53 53 52 52 52 51 52 54 53 54 54 54 55 54 54 52 55 52 52 52 51 52 51 52 52 51 51 52 52 53 53 53 53 55 54 55 54 54 54 55 52 52 52 51 51 52 51 51 51 52 53 53 54 53 54 53 55
更新 2:
我注意到“热模式”基准测试期间的每个 CPU 利用率都是非常随机的,而且它从未达到超过 80%,如下图所示:
相反,下图显示了我通过make -j4
编译 OpenCV 时的 CPU 利用率。如您所见,它更稳定并且几乎 100% 使用它:
我认为这是第一个图像的变化是正常的,因为我们多次执行相同的短程序,这比一个大程序更不稳定。我不明白的是为什么我们的 CPU 利用率永远不会超过 80%。
【问题讨论】:
你能在更大的数据集上给出一些测试结果吗?尝试让它持续几秒钟,以更好地了解不受后台进程影响或完全由线程创建开销决定的加速。 您使用的是什么编译器和操作系统?你在绑定线程吗?将环境中的OMP_PROCBIND
设置为true。此外,请确保没有其他程序占用 CPU 时间。诸如 Intel VTune Amplifier(商业许可)或 Sun Studio 的线程分析器之类的工具,甚至 Score-P 都可以让您深入了解线程将时间花在哪里。
@Micka gettimeofday()
在 Linux 中非常常见且多核安全
此工具可用于您的操作系统吗? developers.redhat.com/blog/2014/03/10/…
正如之前多次提到的,这个问题最好在适当的工具支持下进行调查。理想情况下,您需要一个了解线程并捕获不同迭代动态的工具,就像 Hristo Iliev 提到的那样。不幸的是,没有一个可重复的小例子,我们只能猜测......
【参考方案1】:
我强烈建议您使用一些性能工具,例如 Paraver (http://www.bsc.es/paraver)、TAU (http://www.cs.uoregon.edu/research/tau/home.php) Vampir (https://tu-dresden.de/die_tu_dresden/zentrale_einrichtungen/zih/forschung/projekte/vampir) 甚至 Intel 的 Vtune (https://software.intel.com/en-us/intel-vtune-amplifier-xe)。
这些工具将帮助您了解线程在哪里花费其周期。使用它们,您可以发现应用程序是否不平衡(通过 IPC 或指令),是否存在由于内存带宽或错误共享问题而导致的任何限制,以及许多其他问题。
【讨论】:
以上是关于OpenMP:为啥这个应用程序有时会扩展?的主要内容,如果未能解决你的问题,请参考以下文章
请看看则个fortran结合openmp并行程序,为啥老出错?