【发布时间】:2016-05-09 20:09:41
【问题描述】:
更新:我的错。这不是双倍减速的原因。我还有其他错误。
C++ MFC。视觉工作室 12。
我正在尝试优化绘图循环中的性能。我有一个我所有对象的列表(ListAll),假设它有 300 个对象,所有对象都有唯一的 ID。我有第二个需要渲染的 ID 列表(ListNow),大小为 100。ListNow 中的所有值都有关联的对象存储在 ListAll 中。
目前,ListAll 是一个 CMap ,而 ListNow 是一个 CArray。
// this is the slower, current method
for (int i = 0; i < ListNow.GetSize(); i++)
{
UINT id = ListNow.GetAt(i);
if (ListAll->Lookup(id, object))
{
object->draw();
}
}
过去我只有 ListAll(CMap),我在其中的每个对象上都调用了 draw()。它只有我想画的 100 个,每次我切换正在绘制的内容时,我都会“重建”它。
// this is the faster, old method
POSITION pos = ListAll->GetStartPosition();
while (pos)
{
ListAll->GetNextAssoc(pos, id, object);
object->Draw();
}
从技术上讲,这两种算法都以 O(n) 的速度执行......但只需将 CMap::Lookup 函数添加到循环中,它所花费的时间就会增加一倍。我已将 CMap 大小正确设置为大于 CMap 中对象数量的素数。对于 300,000 及以上的列表,这种放缓是明显的。
我切换到这个系统,以便我可以将所有对象存储在绘制列表中,并且可以使用相同的对象列表在不同窗口之间快速交换正在绘制的内容。这会在大幅切换时加快时间,但会减慢每个单独的绘图调用。现在切换回来不是一种选择,我们知道它会稍微减慢每次绘制调用,但不会这么多。减速肯定在我展示给你的代码中,因为当我切换回绘制所有内容(删除查找)时,它会将时间缩短一半。
我提高性能的唯一想法是将 LastDrawn 对象指针记录在一个列表中,并通知函数是否需要更改(调用 lookup())或者它是否可以简单地重新使用最后绘制的(GetNext() )。由于 90% 的时间,两次通话之间没有任何变化。
有没有比这更快的解决方案?我梦想着一个棘手的位掩码解决方案,它以某种方式产生我想要的对象指针,我不知道。在这一点上,任何事情都会有所帮助。
【问题讨论】:
-
你确定减速是在 CMap::lookup 中吗?良好的哈希映射实现不应该以 O(1) 的平均复杂度减慢您的速度。如果被分析,并确认,尝试
std::unordered_map查看lookup,签名,看起来它正在复制数据?呃……你真的应该看看unordered_map。 -
ListNow中有多少项对应ListAll?
-
@SergeyA 没错,它正在复制到提供的
rvalue,但GetNextAssoc也会在第二个循环中为地图中的每个成员执行此操作。 -
如何生成 id,它们是顺序的还是随机的,有初始值还是从 0 开始? CMap 使用散列算法,因此如果您的唯一标识符导致大量散列冲突,您可能会看到您描述的那种减速。另外,你能在你测得的速度上加上一些实数吗?重绘 300 个对象中的 100 个需要多少秒/毫秒/微秒?如果不是哈希冲突问题,它可能有助于了解我们预期的减速类型。
-
你能不能只在
ListAll中存储的对象类中添加一个简单的布尔标志?遍历所有项目以确定需要绘制哪些项目是您无论如何都需要以一种或另一种方式做的事情;那么,简单地检查渲染循环中的标志应该不会那么昂贵(除非ListAll比ListNow大几个数量级)。
标签: c++ algorithm performance