try
块的成本是多少。我们是否应该期望更多地像Java一样使用异常来处理一般逻辑?
try
块的成本是多少。#include <iostream>
#include <stdexcept>
struct SpaceWaster {
SpaceWaster(int l, SpaceWaster *p) : level(l), prev(p) {}
// we want the destructor to do something
~SpaceWaster() { prev = 0; }
bool checkLevel() { return level == 0; }
int level;
SpaceWaster *prev;
};
void thrower(SpaceWaster *current) {
if (current->checkLevel()) throw std::logic_error("some error message goes here\n");
SpaceWaster next(current->level - 1, current);
// typical exception-using code doesn't need error return values
thrower(&next);
return;
}
int returner(SpaceWaster *current) {
if (current->checkLevel()) return -1;
SpaceWaster next(current->level - 1, current);
// typical exception-free code requires that return values be handled
if (returner(&next) == -1) return -1;
return 0;
}
int main() {
const int repeats = 1001;
int returns = 0;
SpaceWaster first(1000, 0);
for (int i = 0; i < repeats; ++i) {
#ifdef THROW
try {
thrower(&first);
} catch (std::exception &e) {
++returns;
}
#else
returner(&first);
++returns;
#endif
}
#ifdef THROW
std::cout << returns << " exceptions\n";
#else
std::cout << returns << " returns\n";
#endif
}
米老鼠基准测试结果:
$ make throw -B && time ./throw
g++ throw.cpp -o throw
1001 returns
real 0m0.547s
user 0m0.421s
sys 0m0.046s
$ make throw CPPFLAGS=-DTHROW -B && time ./throw
g++ -DTHROW throw.cpp -o throw
1001 exceptions
real 0m2.047s
user 0m1.905s
sys 0m0.030s
异常性能非常依赖于编译器。您需要对应用程序进行剖析以查看是否存在问题。通常情况下,不应该成为问题。
您真的应该将异常用于“异常情况”,而不是一般逻辑处理。异常非常适合将代码的正常路径和错误路径分离。
我曾经创建过一个x86仿真库,并使用异常来处理中断等。这是个坏主意。即使我没有抛出任何异常,它也会对我的主循环产生很大的影响。以下是我的主循环的代码:
try{
CheckInterrupts();
*(uint32_t*)&op_cache=ReadDword(cCS,eip);
(this->*Opcodes[op_cache[0]])();
//operate on the this class with the opcode functions in this class
eip=(uint16_t)eip+1;
}
//eventually, handle these and do CpuInts...
catch(CpuInt_excp err){
err.code&=0x00FF;
switch(err.code){
将该代码放入try块中的开销使异常函数成为CPU时间使用率前5名中的2个。
对我来说,这太昂贵了。
人们会认为它们在C++03中的性能大致相同,即“非常慢”!而且,不管是哪种语言,由于try-catch-throw结构,异常应该只在特殊情况下使用。如果您在Java中使用throw来控制程序流程,则说明您做错了什么。
@Steve Jessop已经发布了事实上的答案,但我仍然认为有一件事情:尝试不同的优化级别,比如大多数产品常用的“-O2”。
异常处理是一项昂贵的功能,因为抛出/捕获意味着需要执行额外的代码来确保堆栈展开和捕获条件评估。
据我从一些阅读中了解到,例如对于Visual C++,代码中嵌入了一些相当复杂的结构和逻辑来确保这一点。由于大多数函数可能调用其他可能引发异常的函数,因此即使在这些情况下也可能存在堆栈展开的一些开销。
然而,在考虑异常开销之前,最好在任何优化操作之前测量异常使用对您的代码的影响。避免过度使用异常应该可以防止异常处理造成的重大开销。
想象一下,铃声响起,计算机停止接受任何输入三秒钟,然后有人踢了用户的头。
这就是异常的代价。如果它可以防止数据丢失或机器着火,那么它的代价是值得的。否则,可能不值得。
编辑:由于这篇文章被踩了(还有一个赞,所以对我来说是+8!),我将用更少的幽默和更多的信息来澄清上面的内容:异常,至少在C++领域,需要RTTI和编译器以及可能的操作系统魔法,这使得它们的性能成为一个巨大的不确定性黑洞。(你甚至不能保证它们会触发,但这些情况发生在其他更严重的事件中,比如内存耗尽、用户杀死进程或机器实际着火。)因此,如果你使用它们,应该是因为你想从本来会导致某些可怕事情发生的情况中优雅地恢复过来,但是这种恢复不能对运行性能有任何期望(无论对于你特定的应用程序来说是什么)。
因此,如果你要使用异常,你不能假设任何关于性能影响的事情。