检查std :: vector的大小是否为零

时间:2012-02-28 07:17:28

标签: c++ visual-studio-2010 stl

在vs2010 std :: vector.size()中:

return (this->_Mylast - this->_Myfirst);

和std :: vector.empty():

return (this->_Myfirst == this->_Mylast);

我的问题是,如果要检查矢量是否为零,那么这两个函数之间是否有任何不同的速度。 减去和等于几乎是相同的二进制操作,所以这两个函数的速度是相同的吗?

8 个答案:

答案 0 :(得分:9)

除非你这样做并且每秒数百万次(而且严重的是,为什么你?),否则它不会造成任何差别。

如果你真的有兴趣,请检查一下。设置几百万次的循环,看看每次循环的时间。

我想你会发现差异可以忽略不计。

如果专注于宏优化问题(例如算法选择)以及为可读性优化此类内容,您会好得多。

而且,无论如何,优化可读性是一种有效的方法,除非存在严重的性能瓶颈,否则我通常采用这种方法。换句话说,如果您根据其是否为空而执行某些操作,请使用empty()!empty()。任何其他尺寸检查(如果其中至少有12个元素)应使用size()(显然)。

作为一些微观优化无关紧要的例子,这里有一些C代码需要思考:

#include <stdio.h>

int main(void) {
    int i, j, k, diff;
    for (i = 0; i < 1000; i++)
        for (j = 0; j < 1000000; j++)
            //diff = (i == j);
            diff = (i - j);
    return 0;
}

当我使用默认优化(a)编译它,并使用time命令运行它时,我得到CPU时间(超过五次运行),其中一行没有注释:

diff = (i - j)     diff = (i == j)
==============     ===============
         2.488              2.216
         2.424              2.220
         2.452              2.224
         2.484              2.152
         2.464              2.152
         =====              =====
Avrgs:   2.463              2.193

现在第一个选项慢了12%,但有一件事需要你理解。虽然速度较慢,但​​仍需要两秒钟才能完成十亿次次。如果你这样做一次,差异在0.000000002463秒和0.000000002193秒之间,不值得优化。

选择你的战斗,瞄准你的优化。您可以通过宏优化策略获得大量速度提升。


(a)使用gcc“疯狂”优化级别-O3,它们都需要0.000秒(有时为0.004,但很少 - 我认为分辨率有限制time命令),使差异更加无关紧要: - )

答案 1 :(得分:8)

使用empty()优于size()来检查矢量空白有两个原因:

  • std::vector.empty() 可能比std::vector.size()更快,具体取决于具体实施方式。

  • 使用empty()检查向量空白比使用size()更直观,更易读。


参考:
Nicolai M. Josutil的:
C ++标准库:教程和参考

指出:

<强> size()
返回容器的实际元素数。

<强> empty()
是检查元素数量是否为零(size()==0)的快捷方式。 但是,empty()可能会更有效地实现,因此您应该使用它 可能的。


请注意,为了确保哪一个更快,你必须为你的环境描绘两者。

答案 2 :(得分:5)

对于矢量,性能可能相同。即使它不相同,它也具有相同的Big O复杂性,并且速度的差异可以忽略不计。因此,如果您只想检查向量是否为空,则使用empty可能更方便。它更准确地描述了您真正想要做的事情。

使用empty的另一个原因是,当您稍后将容器更改为list时,它可能具有更好的Big O复杂性。因为有std::list个实现,其中size具有线性复杂度,而空值始终为O(1)。

答案 3 :(得分:3)

Scott Meyers,在“有效STL”中,建议调用empty()而不是检查所有容器的大小。他给出的原因是empty对于所有标准包含都是恒定时间,而size在某些列表实现中需要线性时间。

如果您使用尺寸而后来碰巧更换容器,则可能会出现性能问题。

因此,除非检查容器是否没有元素是一个真正的瓶颈(首先测量,如果存在问题,则进行优化),使用empty

答案 4 :(得分:2)

让我们停止制作

bool empty_by_difference(int* b, int* e) {
  return (e - b) == 0;
}

bool empty_by_equality(int* b, int* e) {
  return e == b;
}

由Clang 3.0编译成以下IR:

define zeroext i1 @_Z19empty_by_differencePiS_(i32* %b, i32* %e) nounwind uwtable readnone {
  %1 = icmp eq i32* %e, %b
  ret i1 %1
}

define zeroext i1 @_Z17empty_by_equalityPiS_(i32* %b, i32* %e) nounwind uwtable readnone {
  %1 = icmp eq i32* %e, %b
  ret i1 %1
}

你可能不知道IR代表,但我认为这将会回家。

所以让我推测,基准只是浪费我的时间和你的时间。


现在,从语义的角度来看,我个人觉得阅读if (x.empty())比阅读if (x.size() == 0)更清楚。

在后一种情况下:

  • 我的眼睛需要更多工作:区分==!=<=>=,...
  • 我的大脑必须更多地工作:记住而不是0是一种哨兵价值,与1等其他任何价值都非常不同......

答案 5 :(得分:1)

如果空白和尺寸之间存在差异,那么即使是基准也不会看到它。由于从C ++ 11开始,列表的大小为O(1),我认为写入大小然后为空更可读。但那只是我的观点:))

答案 6 :(得分:0)

  

减去和等于几乎是相同的二元操作

确实,几乎相同。但是AFAIK布尔运算符比其他运算符更快。

答案 7 :(得分:0)

应该没有速度差异。实际上,您应该看到为两种情况生成的完全相同的代码。 编译器应该能够执行以下转换:

if (vec.size() == 0)
// After inlining:
if (_Mylast - _Myfirst == 0)
// Equivalent to:
if (_Mylast == _Myfirst) // Which is the same as empty() after it is inlined
相关问题