c++ - OpenMP:为什么这个应用程序有时会扩展?

标签 c++ multithreading opencv parallel-processing openmp

我正在尝试在 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平均而言,但经常发生的情况是:200us50us .

然而,我们非常幸运:每个 for 之间没有依赖关系。循环,所以我们可以添加:
#pragma omp parallel for schedule(dynamic,8)

并获得初始加速。 dynamic引入选项是因为它的性能似乎比 static 好一点。 (不知道为什么)。

问题是它真的很不稳定,不能扩展 .这是在并行模式下计算函数所需的时间:
25ms 43ms 32ms 15ms 27ms 53ms 21ms 24ms
正如您所看到的,只有在四核系统中达到最佳加速比 ( 15ms )。大多数情况下,我们达到了最佳加速比的一半:25ms在四核系统中只有理论最佳加速比的一半。

为什么会发生这种情况?我们该怎样改进这个?

更新:
正如评论中所建议的,我尝试使用更大的数据集。使用巨大的图像,串行版本需要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
你可以看到很多好的结果( 14ms15ms )但也有很多可怕的结果( >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%以上,如下图所示:

enter image description here

相反,下图显示了我通过 make -j4 编译 OpenCV 时的 CPU 利用率。 .如您所见,它更稳定并且几乎 100% 使用了它:

enter image description here

我认为这是第一张图像的变化是正常的,因为我们多次执行同一个短程序,这比一个大程序更不稳定。我不明白的是为什么我们从未达到超过 80% 的 CPU 利用率 .

最佳答案

我强烈建议您使用一些性能工具,例如 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 或指令),是否存在由于内存带宽或错误共享问题而导致的任何限制,以及许多其他问题。

关于c++ - OpenMP:为什么这个应用程序有时会扩展?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38216689/

相关文章:

c++ - 如何在 gcc v4.1.2 中获取 unordered_set 的 header ?

c++ - 为什么 headerFileName_H

c++ - 继承时,基类的基类是否也被继承?

java - 如何在 run 方法之外发送对象?

opencv - 英特尔性能基元是否仅适用于英特尔处理器驱动的设备?

c++ - 将窄字符串转换为宽字符串

.net - 多线程循环卡住ui

java - 在 Java 中,如何创建一个重用 Git 存储库的线程安全应用程序?

python - opencv中图像的二值化

image - 如何使用 C++ 或 Python 从图像中读取图像描述?