优化' std::vector operator [] ' (vector访问),当它成为瓶颈时



gprof说我的高计算应用程序在std::vector <...> operator [] (unsigned long)中花费了53%的时间,其中32%的时间用于一个经常使用的向量。更糟糕的是,我怀疑我的并行代码无法扩展到3-6核以上是由于相关的内存瓶颈。虽然我的应用确实花了很多时间访问和写入内存,但似乎我应该能够(或至少尝试)做得比52%更好。我应该尝试使用动态数组代替(大小保持不变,在大多数情况下)?这可能有助于解决可能出现的瓶颈吗?

实际上,为了方便起见,我更喜欢的解决方案是解决瓶颈并保持向量不变。基于以上,是否有任何可能的罪魁祸首或解决方案(tcmalloc出局)?

你检查你的内存访问模式本身了吗?

在访问数组时是否尝试使用原始指针?

// 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];

我怀疑gprof会阻止函数内联。尝试使用另一种分析方法。std::vector operator []不能成为瓶颈,因为它与原始数组访问没有太大区别。SGI的实现如下所示:

reference operator[](size_type __n) { return *(begin() + __n); }
iterator begin() { return _M_start; }

您不能信任gprof进行高速代码分析,您应该使用oprofile这样的被动分析方法来获得真实的情况。

作为另一种选择,你可以通过手动修改代码来进行分析(例如,将一个计算调用10次而不是一次,并检查执行时间增加了多少)。请注意,这将受到缓存问题的影响,所以YMMV.

vector类很受欢迎,并以牺牲性能为代价提供了一定的便利,当您不是特别需要性能时,这是很好的。

如果您确实需要性能,那么绕过vector类并直接使用简单的老式手工数组(无论是静态分配还是动态分配)不会对您造成太大伤害。然后1)你现在花在索引上的时间就会消失,从而加快你的应用程序的速度,2)你可以转移到任何"下一件大事"上,那就是在你的应用程序中花费时间。

编辑:大多数程序的加速空间比您想象的要大得多。我制作了一个演练项目来说明这一点。如果我可以快速总结一下,它是这样的:

  • 初始时间为2.7 msec每个"作业"("作业"的数量可以改变,以获得足够的运行时间来分析它)。

  • 第一次切割显示大约60%的时间花在矢量操作上,包括索引、追加和删除。我从MFC替换了一个类似的矢量类,时间减少到1.8毫秒/工作。(这是1.5倍或50%的加速)

  • 即使使用这个数组类,大约40%的时间花在[]索引操作符上。我想让它直接索引,所以我强制它直接索引,而不是通过运算符函数。

  • 将时间降低到1.5毫秒/作业,速度提高了1.2倍。
  • 现在大约60%的时间是在数组中添加/删除项。另外一部分花费在"新建"one_answers"删除"上。我决定放弃数组,做两件事。一种是使用自己动手的链表,并汇集使用过的对象。第一个将时间减少到1.3毫秒(1.15倍)。第二次减少到0.44毫秒(2.95倍)。

  • 在那段时间里,我发现大约60%的时间是在我编写的代码中对列表进行索引(就好像它是一个数组)。我决定用一个直接指向列表的指针来代替。结果:0.14 msec (3.14x)

  • 现在我发现几乎所有的时间都花在了打印到控制台的诊断I/O行上。我决定去掉:0.0037 msec (38x)。

我本可以继续走下去,但我停了下来。每次作业的总时间减少了约700倍的复合系数。

我想让你明白的是,如果你需要的性能差到足以偏离可能被认为是公认的做事方式,你不必在遇到一个"瓶颈"后就停下来。仅仅因为你有一个大的加速并不意味着没有更多。事实上,就加速因素而言,下一个"瓶颈"可能比第一个更大。所以提高你的期望,你可以得到加速,并尝试打破。

最新更新