gprof说我的高计算应用程序花费53%的时间在std::vector <...> operator [] (unsigned long)
内,其中32%用于一个使用频繁的向量。更糟糕的是,我怀疑我的并行代码未能扩展到3-6核心之外是由于相关的内存瓶颈。虽然我的应用确实花费了大量时间来访问和编写内存,但似乎我应该能够(或者至少尝试)做得比52%更好。我应该尝试使用动态数组(大多数情况下大小保持不变)吗?这可能有助于解决可能出现的瓶颈问题吗?
实际上,我的首选解决方案是解决瓶颈问题,并将矢量保留为方便。基于以上所述,是否有任何可能的罪魁祸首或解决方案(tcmalloc已经出局)?
答案 0 :(得分:3)
您是否检查过您的内存访问模式?它可能效率低下 - 缓存不友好。
答案 1 :(得分:2)
您是否尝试在数组访问时使用原始指针?
// regular place
for (int i = 0; i < arr.size(); ++i)
wcout << arr[i];
// In bottleneck
int *pArr = &arr.front();
for (int i = 0; i < arr.size(); ++i)
wcout << pArr[i];
答案 2 :(得分:2)
我怀疑gprof会阻止函数内联。尝试使用另一种分析方法。 std::vector operator []
不能成为瓶颈,因为它与原始数组访问没有太大区别。 SGI实施如下所示:
reference operator[](size_type __n) { return *(begin() + __n); }
iterator begin() { return _M_start; }
答案 3 :(得分:1)
您不能相信gprof
进行高速代码分析,您应该使用像oprofile
这样的被动分析方法来获得真实的图片。
作为替代方案,您可以通过手动代码更改进行分析(例如,调用计算10次而不是1次,并检查执行时间增加了多少)。请注意,这将受到缓存问题的影响,因此YMMV。
答案 4 :(得分:0)
矢量类非常受欢迎,并且以牺牲性能为代价提供了一定的便利性,当你不特别需要性能时这很好。
如果你真的需要性能,那么绕过vector类并直接转到一个简单的旧手工制作的数组(无论是静态还是动态分配)都不会太麻烦。然后1)你当前花在索引上的时间应该基本消失,加快你的应用程序的数量,2)你可以继续你的应用程序需要时间的“下一件大事”。
编辑: 大多数程序都有比你想象的更多的加速空间。我做了walk-through project来说明这一点。如果我能够很快地总结它,那就是这样的:
每个“作业”的原始时间为2.7毫秒(可以改变“作业”的数量以获得足够的运行时间来分析它)。
首次剪切显示大约60%的时间花在向量操作上,包括索引,追加和删除。我用MFC中的类似矢量类替换,时间减少到1.8毫秒/作业。 (这是1.5倍或50%的加速。)
即使使用该数组类,大约有40%的时间花在[]索引运算符上。我想让它直接索引,所以我强迫它直接索引,而不是通过运算符函数。这减少了1.5毫秒/工作时间,1.2倍加速。
现在大约60%的时间是在数组中添加/删除项目。在“新”和“删除”中花费了额外的部分。我决定扔掉阵列并做两件事。一种是使用自己动手链接列表,并汇集使用过的对象。第一次减少时间为1.3毫秒(1.15倍)。第二个将它减少到0.44毫秒(2.95倍)。
在那段时间里,我发现大约60%的时间都是在我编写的代码中编写索引(就好像它是一个数组)。我决定只需将指针直接放入列表就可以完成。结果:0.14毫秒(3.14x)。
现在我发现几乎所有的时间都花在我正在打印到控制台的诊断I / O行中。我决定摆脱它:0.0037毫秒(38x)。
我本可以继续前进,但我停了下来。 每个工作的总时间减少了约700倍的复合因子。
我想要你带走的是,如果你需要的表现足够糟糕,偏离可能被认为是可接受的做事方式,你就不必在一个“瓶颈”之后停下来。 仅仅因为你有一个大的加速并不意味着没有更多。 事实上,就加速因素而言,下一个“瓶颈”可能比第一个更大“瓶颈”。 所以提高你对加速的期望,然后去破产。