C++ 空vs抛出与性能
我有一门课:C++ 空vs抛出与性能,c++,performance,exception,null,C++,Performance,Exception,Null,我有一门课: class Vector { public: element* get(int i); private: element* getIfExists(int i): }; get调用getIfExists;若元素存在,则返回该元素,若不存在,则执行某些操作getIfExists可以表示某个元素i不存在 通过抛出异常或返回NULL 问:在表现上会有什么不同吗?在一种情况下,get需要检查==NULL,在另一种情况下,尝试。。。捕捉 是的,性能会有所不同
class Vector {
public:
element* get(int i);
private:
element* getIfExists(int i):
};
get
调用getIfExists
;若元素存在,则返回该元素,若不存在,则执行某些操作getIfExists
可以表示某个元素i
不存在
通过抛出异常或返回NULL
问:在表现上会有什么不同吗?在一种情况下,
get
需要检查==NULL
,在另一种情况下,尝试。。。捕捉
是的,性能会有所不同:返回NULL比抛出异常花费更少,检查NULL比捕获异常花费更少
附录:但只有在您预计这种情况会频繁发生的情况下,性能才是相关的,在这种情况下,它可能不是例外情况。在C++中,使用异常来实现正常程序逻辑是一种坏的风格,这似乎是:我假设
returnnull
变量具有更好的性能
如果可以使用返回值,则通常不应使用异常。
由于该方法名为get
,我假设NULL不是有效的结果值,因此传递NULL应该是最好的解决方案。
如果调用方不测试结果值,它将取消引用null值,并呈现SIGSEGV,这也是合适的
如果很少调用该方法,则根本不应该关心微观优化
哪种翻译方法看起来更容易
$g++-Os-c test.cpp
#include <cstddef>
void *get_null(int i) throw ();
void *get_throwing(int i) throw (void*);
int one(int i) {
void *res = get_null(i);
if(res != NULL) {
return 1;
}
return 0;
}
int two(int i) {
try {
void *res = get_throwing(i);
return 1;
} catch(void *ex) {
return 0;
}
}
#包括
void*get_null(inti)throw();
虚空*获得投掷(int i)投掷(虚空*);
int一(int一){
void*res=get_null(i);
如果(res!=NULL){
返回1;
}
返回0;
}
整数二(整数一){
试一试{
void*res=get_投掷(i);
返回1;
}捕获(无效*ex){
返回0;
}
}
$objdump-dC test.o
0000000000000000 <one(int)>:
0: 50 push %rax
1: e8 00 00 00 00 callq 6 <one(int)+0x6>
6: 48 85 c0 test %rax,%rax
9: 0f 95 c0 setne %al
c: 0f b6 c0 movzbl %al,%eax
f: 5a pop %rdx
10: c3 retq
0000000000000011 <two(int)>:
11: 56 push %rsi
12: e8 00 00 00 00 callq 17 <two(int)+0x6>
17: b8 01 00 00 00 mov $0x1,%eax
1c: 59 pop %rcx
1d: c3 retq
1e: 48 ff ca dec %rdx
21: 48 89 c7 mov %rax,%rdi
24: 74 05 je 2b <two(int)+0x1a>
26: e8 00 00 00 00 callq 2b <two(int)+0x1a>
2b: e8 00 00 00 00 callq 30 <two(int)+0x1f>
30: e8 00 00 00 00 callq 35 <two(int)+0x24>
35: 31 c0 xor %eax,%eax
37: eb e3 jmp 1c <two(int)+0xb>
0000000000000000:
0:50推力%rax
1:e8 00 00 callq 6
6:48 85 c0测试%rax,%rax
9:0f 95 c0设定值%al
c:0f b6 c0 movzbl%al,%eax
f:5a pop%rdx
10:c3 retq
0000000000000011 :
11:56%rsi
12:e8 00 00 callq 17
17:b8 01 00 mov$0x1,%eax
1c:59%持久性有机污染物循环利用率
1d:c3 retq
1e:48日约12%rdx
21:48 89 c7 mov%rax,%rdi
24:74 05
26:e8 00 00 callq 2b
2b:e8 00 00呼叫Q 30
30:e8 00呼叫35
35:31 c0异或%eax,%eax
37:eb e3 jmp 1c
这是设计问题,而不是性能问题。如果这是一个异常情况,比如在get
函数中,那么抛出一个异常;或者更好地触发断言,因为违反函数先决条件是编程错误。如果这是一个预期的情况,比如在getIfExist
函数中,那么不要抛出异常
关于性能,存在零成本异常实现(尽管并非所有编译器都使用该策略)。这意味着只有在抛出异常时才会支付开销,异常应该是。。。好。。。异常情况。现代编译器实现“零成本”异常-它们只在抛出时产生成本,成本与清除加上缓存未命中以获得要清理的列表成比例。因此,如果异常是异常的,它们确实可以比返回代码更快。如果它们是普通的,它们可能会慢一些。如果您的错误在函数调用中的函数中,它实际上做的工作要少得多。这些细节很吸引人,值得谷歌搜索 但成本非常低。在一个紧密的循环中,它可能会有所不同,但通常不会
您应该编写最容易推理和维护的代码,然后对其进行分析,只有在遇到瓶颈时才重新考虑您的决定。如果调用者希望处理项目不存在的可能性,您应该以一种指示返回的方式返回,而不引发异常。如果调用方没有准备好,您应该抛出一个异常。当然,被调用的例程不太可能神奇地知道调用方是否准备好应付麻烦。需要考虑的几种方法: