我正试图在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
来计算描述符,而并行版本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
使用之前的四核。好多了:即使它不是最好的理论结果,它实际上也可以很好地扩展。但实际上问题仍然存在,因为之前的结果是从典型的图像中获得的。
UPDATE 1:根据评论的建议,我尝试在“热模式”执行之间没有任何间隔进行基准测试(有关详细信息,请参阅注释)。更频繁地实现更好的结果,但仍然存在许多变化。这是在热模式下100次运行的时间(以毫秒为单位):
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
个变体:
make -j4
更新2:
我注意到“热模式”基准测试期间的每个CPU利用率都非常随机,并且从不达到80%以上,如下图所示:
相反,下图显示了我通过// Get the MultiUserChatManager
MultiUserChatManager manager = MultiUserChatManager.getInstanceFor(xmppConnection);
// Create a MultiUserChat using an CustomXMPPConnection for a room
MultiUserChat multiUserChat = manager.getMultiUserChat(chatRoomName + "@conference." + xmppConnection.getServiceName());
multiUserChat.addMessageListener(new MessageListener()
{
@Override
public void processMessage(Message message) {
System.out.println("Message listener Received message in send message: "
+ (message != null ? message.getBody() : "NULL") + " , Message sender :" + message.getFrom());
}
});
编译OpenCV时的CPU利用率。你可以看到它更稳定,几乎100%使用它:
我认为这是第一张图片中的变化是正常的,因为我们多次执行相同的短程序,这比一个大程序更不稳定。我不明白的是为什么我们永远不会达到超过80%的CPU利用率。
答案 0 :(得分:0)
我强烈建议您使用一些性能工具,例如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)甚至是英特尔的Vtune({{ 3}})。
这些工具可以帮助您了解线程在哪些周期中花费。通过它们,您可以找到应用程序是否不平衡(通过IPC或指令),是否由于内存带宽或错误共享问题而存在任何限制,以及许多其他问题。