使向量大小()脱离循环条件进行优化
Posted
技术标签:
【中文标题】使向量大小()脱离循环条件进行优化【英文标题】:Taking vector size() out of loop condition to optimize 【发布时间】:2011-06-18 11:04:09 【问题描述】:fibs 是一个 std::vector。使用 g++,我被建议将 fibs.size() 退出循环,以节省每次计算它(因为向量可能会改变)
int sum = 0;
for(int i = 0; i < fibs.size(); ++i)
if(fibs[i] % 2 == 0)
sum += fibs[i];
编译器中肯定有一些数据流分析会告诉我们 fibs 不会改变大小。有没有?或者我应该将其他一些变量设置为 fibs.size() 并在循环条件中使用它?
【问题讨论】:
1 + 1 + 2 + 3 + 5 + ... + Fn = Fn+2 - 1. 因此,无论谁建议您都不希望每次都计算size
(g++ 中的减法,在优化之前),但不介意调用 operator[]
(以及比较的额外加法)在优化之前使用迭代器)两次。什么,确实如此。最好是查看发出的指令,或者计时。
Steve,如果我知道向量的大小保证不会改变,那么使用 operator[] 不只是节省时间吗?另外,我是一个 C++ 小丑。那么使用迭代器是否更快?
@shuttle87:我是说 如果 有人(无论谁给出了这个建议)将对性能做出疯狂的概括,我有点惊讶他们是猜测size()
比访问变量慢,但他们并没有猜测operator[]
比使用迭代器或指针慢。我不会准确猜测优化器会做什么——它实际上可能会设法用指针替换索引i
,但要这样做,它必须以某种方式说服自己向量永远不会重新分配,这几乎比说服自己容易它可以提升size
的值。
基本问题是operator[]
必须将向量的基指针从向量对象中加载出来(并添加索引),就像 gcc 上的 size()
将基指针和结束指针加载出来一样矢量对象的(并减去它们)。因此,在这两种情况下,它几乎是同一种繁琐的微优化。就我个人而言,在这段代码被证明是瓶颈之前,我不会担心它们,然后检查发出的代码。但是,如果您要担心一个,我认为您应该同时担心两者。
【参考方案1】:
编译器可能会确定它不会改变。即使是这样,向量的size()
也是一个 O(1) 操作。
【讨论】:
O(1) 只说它是有界的。它仍然可能很昂贵(好吧,不是矢量)。【参考方案2】:除非您知道这是个问题,否则请保持原样。先正确,再清楚,再快速(如有必要)。
vector::size
无论如何都非常快。在我看来,编译器很可能会优化这种情况,因为很明显向量没有被修改,所有调用的函数都将被内联,以便编译器知道。
您可以随时查看生成的代码,看看是否发生了这种情况。
如果你确实想改变它,你需要能够测量它之前和之后所花费的时间。这是相当多的工作 - 你可能有更好的事情要做。
【讨论】:
【参考方案3】:size() 是恒定时间操作,这样调用它没有任何惩罚。如果您担心性能和更通用的遍历集合的方法,请使用迭代器:
int sum = 0;
for(auto it = fibs.cbegin(); it != fibs.cend(); ++it)
if((*it) % 2 == 0)
sum += *it;
【讨论】:
【参考方案4】:我认为您在这里遗漏了另一个更重要的观点:这个循环是否会导致您的应用程序变慢?如果您不确定(即,如果您没有分析),您可能会关注应用程序的错误部分。
在编写程序时,您已经不得不将成千上万的事情记在脑海中(编码指南、应用程序的架构(大图)、变量名、函数名、类名、可读性等),您可以忽略速度初始实现期间的代码(至少 95% 的时间)。这将使您能够专注于更重要和更有价值的事情(例如正确性、可读性和可维护性)。
【讨论】:
【参考方案5】:在您的示例中,编译器可以轻松分析流程并确定它没有改变。在更复杂的代码中它不能:
for(int i = 0; i < fibs.size(); ++i)
complicated_function();
complicated_function
可以更改fibs
。但是,由于上述代码涉及函数调用,编译器无法将fibs.size()
存储在寄存器中,因此您无法消除内存访问。
【讨论】:
以上是关于使向量大小()脱离循环条件进行优化的主要内容,如果未能解决你的问题,请参考以下文章