C ++exception处理添加多less足迹
这个问题对于embedded式开发尤其重要。 exception处理为生成的二进制输出添加了一些足迹。 另一方面,没有例外,错误需要以其他方式处理,这需要额外的代码,最终也会增加二进制大小。
我对你的经历感兴趣,特别是:
- 什么是编译器为exception处理添加的平均占用空间(如果有这种测量的话)?
- exception处理是否真的比其他error handling策略更昂贵(很多人说),在二进制输出大小方面呢?
- 你会为embedded式开发build议什么error handling策略?
请把我的问题作为指导。 任何input是受欢迎的。
附录:有没有人有一个具体的方法/脚本/工具,对于特定的C ++对象/可执行文件,将显示由编译器生成的代码和专用于exception处理的数据结构占用的加载的内存占用的百分比?
当发生exception时会有时间开销,这取决于你如何实现你的exception处理。 但是,作为一个轶事,应该引起exception的事件的严重性将花费同样多的时间来处理使用任何其他方法。 为什么不使用高度支持的基于语言的方法来处理这些问题呢?
GNU C ++编译器默认使用零成本模型,即当没有发生exception时,没有时间开销。
由于关于exception处理代码的信息和本地对象的偏移量可以在编译时计算一次,所以这些信息可以保存在与每个函数相关的一个地方,而不是每个ARI中。 实际上,您从每个ARI中移除了exception开销,从而避免了额外的时间将其推入堆栈。 这种方法被称为exception处理的零成本模型,前面提到的优化存储称为影子堆栈。 – Bruce Eckel,C ++第2卷思考
规模复杂性开销不容易量化,但Eckel平均说5%和15%。 这将取决于您的exception处理代码的大小与应用程序代码的大小之比。 如果你的程序很小,那么例外将是二进制文件的很大一部分。 如果你使用的是一个零成本的模型,那么exception会占用更多的空间来消除时间开销,所以如果你关心的是空间而不是时间而不是使用零成本的编译。
我的观点是,大多数embedded式系统有足够的内存,如果你的系统有一个C ++编译器,你有足够的空间来包含exception。 我的项目使用的PC / 104计算机具有几GB的二级存储器,512 MB的主存储器,因此没有空间问题,但是我们的微控制器是用C语言编程的。我的启发是“如果有一个主stream的C ++编译器它使用exception,否则使用C“。
测量东西,第2部分。我现在有两个程序。 第一个是用C编译的,用gcc -O2编译:
#include <stdio.h> #include <time.h> #define BIG 1000000 int f( int n ) { int r = 0, i = 0; for ( i = 0; i < 1000; i++ ) { r += i; if ( n == BIG - 1 ) { return -1; } } return r; } int main() { clock_t start = clock(); int i = 0, z = 0; for ( i = 0; i < BIG; i++ ) { if ( (z = f(i)) == -1 ) { break; } } double t = (double)(clock() - start) / CLOCKS_PER_SEC; printf( "%f\n", t ); printf( "%d\n", z ); }
第二个是C ++,有exception处理,用g ++ -O2编译:
#include <stdio.h> #include <time.h> #define BIG 1000000 int f( int n ) { int r = 0, i = 0; for ( i = 0; i < 1000; i++ ) { r += i; if ( n == BIG - 1 ) { throw -1; } } return r; } int main() { clock_t start = clock(); int i = 0, z = 0; for ( i = 0; i < BIG; i++ ) { try { z += f(i); } catch( ... ) { break; } } double t = (double)(clock() - start) / CLOCKS_PER_SEC; printf( "%f\n", t ); printf( "%d\n", z ); }
我想这些回答了我上一篇文章的所有批评。
结果:执行时间使得C版本在C ++版本上比例外有0.5%的优势,而不是其他人讨论的10%(但没有被certificate)
如果其他人可以尝试编译和运行代码(应该只花几分钟时间),以便检查我没有在任何地方犯过一个可怕的和明显的错误,我将不胜感激。 这被称为“科学的方法”!
我在低延迟环境下工作。 (在我的应用程序的生产链中,小于300微秒)按照我的经验,exception处理会根据您的数量增加5-25%的执行时间!
我们通常不关心二元膨胀,但如果你膨胀太多,那么你就像疯了似的,所以你要小心。
只要保持二进制合理(取决于您的设置)。
我做了相当广泛的我的系统分析。
其他讨厌的地方:
logging
坚持(我们只是不这样做,或者如果我们做并行)
我想这将取决于该特定平台的硬件和工具链端口。
我没有这个数字。 然而,对于大多数embedded式开发来说,我已经看到了两个东西(VxWorks / GCC工具链):
- 模板
- RTTI
在大多数情况下,exception处理都会使用这两种处理方式,所以也有一种倾向。
在那些我们真的想要接近金属的情况下,使用setjmp
/ longjmp
。 请注意,这可能不是最好的解决scheme(或非常强大),但这就是我们所用的。
您可以使用两个版本的基准testing套件在您的桌面上运行简单的testing,而无需进行exception处理,并获取最可靠的数据。
关于embedded式开发的另一件事是:像鼠疫一样避免使用模板 – 它们造成太多的膨胀。 在模板和RTTI的例外标签,由Johann Gerell在评论中解释(我认为这很好理解)。
再次,这正是我们所做的。 什么是所有的downvoting?
有一件事要考虑:如果你在embedded式环境中工作,你想让应用程序尽可能小。 Microsoft C运行时为程序增加了相当多的开销。 通过删除C运行时作为一个要求,我能够得到一个简单的程序是一个2KB的exe文件,而不是一个70千字节的文件,这是所有优化大小打开。
C ++exception处理需要C运行时提供的编译器支持。 这些细节笼罩着神秘的面纱,根本没有logging。 通过避免C ++exception,我可以删除整个C运行库。
你可能会争辩只是dynamic链接,但在我的情况下,这是不实际的。
另一个问题是,C ++exception至less在MSVC上需要有限的RTTI(运行时types信息),这意味着exception的types名称存储在可执行文件中。 在空间方面,这不是一个问题,但是我觉得没有这个信息在文件中感觉更干净。
在我看来,exception处理不是embedded式开发普遍接受的东西。
GCC和Microsoft都没有“零开销”exception处理。 两个编译器都将序言和尾声语句插入到跟踪执行范围的每个函数中。 这导致性能和内存占用的可测量增加。
在我的经验中,性能差异大约是10%,这对于我的工作领域(实时graphics)来说是一个巨大的数字。 内存开销less得多,但仍然很重要 – 我记不清这个数字,但是使用GCC / MSVC可以很容易地从两种方式编译程序并衡量差异。
我曾经看到一些人把exception处理说成是“只有使用它”的成本。 根据我所观察到的,这是不正确的。 当你启用exception处理时,它会影响所有代码,不pipe代码path是否可以抛出exception(当你考虑编译器如何工作时,这是完全合理的)。
我也将远离embedded式开发的RTTI,尽pipe我们在debugging版本中使用它来完整地检查向下转换的结果。
很容易看到对二进制大小的影响,只需在编译器中closuresRTTI和exception即可。 如果您使用了dynamic_cast <>,那么您会收到投诉…但是我们通常会避免在我们的环境中使用依赖于dynamic_cast <>的代码。
我们总是发现,以二进制大小来closuresexception处理和RTTI是一个胜利。 在没有exception处理的情况下,我见过很多不同的error handling方法。 最stream行的似乎是将失败代码传递给调用堆栈。 在我们当前的项目中,我们使用setjmp / longjmp,但是我build议在C ++项目中不要这样做,因为在许多实现中退出范围时,它们不会运行析构函数。 如果我是诚实的,我认为这是代码的原始devise师做出的一个糟糕的select,特别是考虑到我们的项目是C ++。
定义“embedded式”。 在一个8位的处理器上,我当然不会使用exception(我肯定不能在8位处理器上使用C ++)。 如果你正在使用PC104types的电路板,这个电路板几年前已经足够强大,已经成为别人的桌面,那么你可能就会摆脱困境。 但是我必须问 – 为什么有例外? 通常在embedded式应用程序中,像发生exception一样是不可想象的 – 为什么没有在testing中解决这个问题呢?
例如,这是在医疗设备? 医疗设备上的软件已经杀死了人们。 任何意外发生的事情都是不可接受的。 所有失败模式都必须考虑在内,正如Joel Spolsky所说,exception就像GOTO语句,除非你不知道它们从哪里被调用。 所以当你处理你的exception,什么失败了,你的设备处于什么状态? 由于你的例外是你的放射治疗机在FULL卡住,并正在做一个活着的人(这发生了IRL)? 在你的一万多行代码中,exception发生在什么地方? 当然你也许可以把这个代码减less到100行代码,但是你知道每一行代码的意义是什么?
没有更多的信息,我会说不要在你的embedded式系统中计划exception。 如果添加它们,则准备计划可能导致exception的“每行代码”的故障模式。 如果你正在制造一个医疗设备,那么如果你不这样做的话就会死亡。 如果你制作便携式DVD播放机,那么你已经制造了一台坏的便携式DVD播放机。 这是哪个?