检查std::向量的大小是否为零
checking if a std::vector is of size zero
在vs2010 std::vector.size():中
return (this->_Mylast - this->_Myfirst);
和std::vector.empty():
return (this->_Myfirst == this->_Mylast);
我的问题是,如果这两个函数的速度不同,如果你要检查一个向量是否有零个元素。Minus和equal几乎是相同的二进制运算,对吧,所以这两个函数的速度是一样的?
除非你在做这件事,而且每秒要做数百万次(说真的,为什么会你?),否则不会有什么不同。
如果你真的很感兴趣,请检查一下。建立一个数百万次的循环,看看每次需要多长时间。
我想你会发现差别是微不足道的。
你最好专注于宏观优化问题,比如算法选择,并优化这类内容以提高的可读性
而且,在任何情况下,优化可读性都是一种有效的方法,除非存在严重的性能瓶颈,否则我通常会采取这种方法。换言之,如果您正在根据某个内容是否为空进行操作,请使用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%,但有一件事你需要理解。尽管速度较慢,但它仍然只花了两秒钟就完成了10亿次。如果你只做一次,差距在0.000000002463秒和0.000000002193秒之间,不值得优化。
选择你的战斗,瞄准你的优化。您可以通过宏优化策略获得大规模速度改进。
(a)对于gcc
"疯狂"的优化级别-O3
,它们都需要0.000秒(有时为0.004,但很少-我认为time
命令的分辨率有限制),这使得差异变得更加无关紧要:-)
使用empty()
而不是size()
来检查矢量空闲有两个原因:
std::vector.empty()
可能比std::vector.size()
更快,具体取决于实现方式。使用CCD_ 12检查向量空度比使用
size()
更直观和可读。
参考:
Nicolai M.Josutil的:
C++标准库:教程和参考
状态:
size()
返回容器的实际元素数。
empty()
是检查元素数量是否为零的快捷方式(size()==0
)。但是,empty()
可能会更有效地实现,因此您应该在以下情况下使用它可能的
不过要注意的是,为了绝对确定哪一个更快,你必须根据你的环境对两者进行分析。
对于矢量,性能可能是相同的。即使它不一样,它也具有相同的大O复杂性,并且速度上的差异可以忽略不计。因此,如果您只想检查向量是否为空,那么使用empty
可能更方便。它更准确地描述了你真正想做的事情
使用empty
的另一个原因是,当您稍后将容器更改为list
时,它可能具有更好的Big O复杂性。因为存在一些CCD_ 21实现,其中CCD_。
Scott Meyers在"有效STL"中建议调用empty()
,而不是检查所有容器的大小。他给出的原因是,对于所有标准容器,empty
是恒定时间,而在一些列表实现中,size
需要线性时间。
如果您使用大小,并且稍后碰巧更改了容器,则可能会出现性能问题。
因此,除非检查容器是否没有元素是一个真正的瓶颈(首先测量,如果存在问题则进行优化),否则使用empty
。
让我们在此处停止制造:
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
等任何其他值都非常不同
如果空和大小之间有差异,那么它太小了,甚至基准测试都看不到。由于从C++11开始的列表的大小是O(1),我认为写大小比写空更可读。但这只是我的观点:)
减号和相等是几乎相同的二进制运算权
事实上,几乎是一样的。但是AFAIK布尔运算符比其他运算符更快。
应该没有速度差。事实上,您应该看到为这两种情况生成的完全相同的代码。编译器应该能够执行以下转换:
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
- 检查 std::shared_ptr<> 的当前底层类型是否为 T
- 关于std::move的使用,是否有编译警告
- 通过网络、跨平台传递std::变体是否安全
- 是否有类似std::lower_bound的函数,而不需要排序/分区输入
- std::vector::迭代器是否可以合法地作为指针
- MESI协议和std::atomic-它是否确保所有写入立即对其他线程可见?
- 是否将std::packaged_task添加到现有线程
- C++中是否存在 std::conditional 的懒惰等价物?
- 检查某些类型是否是模板类 std::optional 的实例化
- 如何检查两个 std::向量在小于 O(n) 的时间复杂度内是否相等
- 在C++中,使用带有 std::optional 参数的函数<T>来表示可选参数是否有意义?
- C++标准是否允许<double>在没有开销的情况下实现 std::可选
- '[](std::list& list)<int>{return std::move(list)}(list)' 是否保证将 'list' 留空?
- "std::list::splice(std::const_iterator pos, std::list&& other)"是否保证将"其他"留空?
- 为什么程序员同时使用 std::bad_alloc 和 std::exception.是否 std::例外 仅是不够的
- glibcxx STL 在实现 std::valarray::sum() 时是否不正确?
- 在 C++20 中是否不再允许在 std 中对程序定义类型的函数模板进行专用化?
- 将 std::map::emplace 与返回 shared_ptr 的函数一起使用是否正确?
- std::p romise::set_value() 和 std::future::wait() 是否提供内存围栏?
- std::less是否应该允许在编译时比较不相关的指针?