我曾经花费了一整个星期的时间,试图制作一个平滑、无卡顿的进度条,以适应非常复杂的算法。
这个算法有6个不同的步骤。每个步骤都有严重依赖于A)正在处理的底层数据的时间特征,不仅仅是数据的“数量”,还包括数据的“类型”和B)其中2个步骤随着CPU数量的增加而极大地扩展,2个步骤在2个线程中运行,而2个步骤则有效地只能单线程运行。
数据的混合对每个步骤的执行时间影响要比核心数更大。
最终解决方案非常简单。我创建了6个函数,分析数据集并尝试预测每个分析步骤的实际运行时间。每个函数的启发式分析了正在分析的数据集和CPU数量。根据我的4核机器上的运行时间数据,每个函数基本上返回了它预计需要花费的毫秒数,在我的机器上。
f1(..) + f2(..) + f3(..) + f4(..) + f5(..) + f6(..) = 总运行时间(毫秒)
现在有了这些信息,你就可以有效地知道每个步骤应该占总执行时间的百分比。如果你说step1应该占40%的执行时间,你基本上需要找出如何从该算法中发出40个1%的事件。比如说,如果for循环正在处理100,000个项目,你可能可以这样做:
for (int i = 0; i < numItems; i++){
if (i % (numItems / percentageOfTotalForThisStep) == 0) emitProgressEvent();
.. do the actual processing ..
}
这个算法给我们带来了一个顺滑流畅、表现完美的进度条。你的实现技术可以在进度条中具有不同形式的缩放和功能,但解决问题的基本思路是相同的。
而且,这并不真的重要,启发式参考数字是在我的机器上计算出来的——唯一真正的问题是如果你想在不同的机器上运行时更改数字。但你仍然知道比率(这是唯一真正重要的事情),所以你可以看到你的本地硬件与我使用的硬件运行的差异。
现在,普通的SO读者可能会想知道为什么有人会花费一周时间制作一个平滑的进度条。这个功能是销售主管提出的要求,我相信他在销售会议中使用它来获得合同。金钱万能 ;)