软件中有多种方法来处理错误情况。在 C 或 Go 中,返回错误代码。其他编程语言如 C++ 或 Java 更喜欢抛出异常。使用异常的一个好处是它可以使您的代码大部分保持干净,因为错误处理代码通常是分开的。
处理异常是否比处理错误代码更好是有争议的。我会很乐意使用其中一个。
然而,我反对的是在控制流中使用异常。意外地无法打开文件时抛出异常是可以的。但是您不应该使用异常来对值的类型进行分支。
让我来说明一下。
假设我的代码期望整数总是正数。然后我可能有一个检查这种情况的函数:
int get_positive_value ( int x ) { if ( x < 0 ) { throw std :: runtime_error ( "它不是正数! " ) ; } 返回x ; }
到现在为止还挺好。我假设通常永远不会抛出异常。如果我有某种错误,它会被抛出。
如果我想对数组中包含的整数的绝对值求和,则可以使用以下分支代码:
整数总和= 0 ; 对于( int x : a ) { 如果( x < 0 ) { 总和+ = - x ; }其他{ 总和+ = x ; } }
不幸的是,我经常看到滥用异常的解决方案:
整数总和= 0 ; 对于( int x : a ) { 试试{ 总和+ = get_positive_value ( x ) ; }抓住( ... ) { _ 总和+ = - x ; } }
后者显然是丑陋且难以维护的代码。但更重要的是,它可能非常低效。为了说明,我针对包含几千个元素的随机数组编写了一个小型基准测试。我在 Skylake 处理器上使用 LLVM clang 12 编译器。在我的测试中,普通代码要快 10000 倍!
普通代码 | 0.05 ns/值 |
例外 | 500 纳秒/值 |
您的结果会有所不同,但通常情况下,将异常用于控制流会导致性能欠佳。而且也很丑!
原文: https://lemire.me/blog/2022/05/13/avoid-exception-throwing-in-performance-sensitive-code/