我正在尝试在 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
在四核系统中只有理论最佳加速比的一半。为什么会发生这种情况?我们该怎样改进这个?
更新:
正如评论中所建议的,我尝试使用更大的数据集。使用巨大的图像,串行版本需要
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% 使用了它:我认为这是第一张图像的变化是正常的,因为我们多次执行同一个短程序,这比一个大程序更不稳定。我不明白的是为什么我们从未达到超过 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/