[英]Performance of try-catch in php
在 php 5 中使用 try-catch 语句时需要考虑什么样的性能影响?
我之前在网上读过一些关于这个主题的旧的和看似矛盾的信息。 我目前必须使用的许多框架都是在 php 4 上创建的,并且缺乏 php 5 的许多优点。因此,我自己在 php 中使用 try-catchs 方面没有太多经验。
需要考虑的一件事是,没有抛出异常的 try 块的成本与实际抛出和捕获异常的成本是不同的问题。
如果异常只在失败的情况下抛出,你几乎肯定不会关心性能,因为每次执行你的程序不会失败很多次。 如果您在一个紧密的循环中失败(又名:用头撞砖墙),您的应用程序可能会遇到比缓慢更严重的问题。 所以不要担心抛出异常的成本,除非你以某种方式被迫将它们用于常规控制流。
有人发布了一个关于抛出异常的分析代码的答案。 我自己从来没有测试过它,但我自信地预测这将显示出比只进出一个 try 块而不抛出任何东西更大的性能损失。
另一件要考虑的事情是,在嵌套调用很多级别的地方,在顶部进行一次 try...catch 甚至比检查返回值并在每次调用时传播错误更快。
与这种情况相反,您发现您将每个调用都包装在自己的 try...catch 块中,您的代码会变慢。 而且更丑。
我很无聊并分析了以下内容(我省略了计时代码):
function no_except($a, $b) {
$a += $b;
return $a;
}
function except($a, $b) {
try {
$a += $b;
} catch (Exception $e) {}
return $a;
}
使用两个不同的循环:
echo 'no except with no surrounding try';
for ($i = 0; $i < NUM_TESTS; ++$i) {
no_except(5, 7);
}
echo 'no except with surrounding try';
for ($i = 0; $i < NUM_TESTS; ++$i) {
try {
no_except(5, 7);
} catch (Exception $e) {}
}
echo 'except with no surrounding try';
for ($i = 0; $i < NUM_TESTS; ++$i) {
except(5, 7);
}
echo 'except with surrounding try';
for ($i = 0; $i < NUM_TESTS; ++$i) {
try {
except(5, 7);
} catch (Exception $e) {}
}
在我的 WinXP 机器上运行 1000000 次,运行 apache 和 PHP 5.2.6:
no except with no surrounding try = 3.3296
no except with surrounding try = 3.4246
except with no surrounding try = 3.2548
except with surrounding try = 3.2913
无论测试运行的顺序如何,这些结果都是一致的并且保持相似的比例。
结论:添加处理罕见异常的代码并不比忽略异常的代码慢。
Try-catch 块不是性能问题——真正的性能瓶颈来自于创建异常对象。
测试代码:
function shuffle_assoc($array) {
$keys = array_keys($array);
shuffle($keys);
return array_merge(array_flip($keys), $array);
}
$c_e = new Exception('n');
function no_try($a, $b) {
$a = new stdclass;
return $a;
}
function no_except($a, $b) {
try {
$a = new Exception('k');
} catch (Exception $e) {
return $a + $b;
}
return $a;
}
function except($a, $b) {
try {
throw new Exception('k');
} catch (Exception $e) {
return $a + $b;
}
return $a;
}
function constant_except($a, $b) {
global $c_e;
try {
throw $c_e;
} catch (Exception $e) {
return $a + $b;
}
return $a;
}
$tests = array(
'no try with no surrounding try'=>function() {
no_try(5, 7);
},
'no try with surrounding try'=>function() {
try {
no_try(5, 7);
} catch (Exception $e) {}
},
'no except with no surrounding try'=>function() {
no_except(5, 7);
},
'no except with surrounding try'=>function() {
try {
no_except(5, 7);
} catch (Exception $e) {}
},
'except with no surrounding try'=>function() {
except(5, 7);
},
'except with surrounding try'=>function() {
try {
except(5, 7);
} catch (Exception $e) {}
},
'constant except with no surrounding try'=>function() {
constant_except(5, 7);
},
'constant except with surrounding try'=>function() {
try {
constant_except(5, 7);
} catch (Exception $e) {}
},
);
$tests = shuffle_assoc($tests);
foreach($tests as $k=>$f) {
echo $k;
$start = microtime(true);
for ($i = 0; $i < 1000000; ++$i) {
$f();
}
echo ' = '.number_format((microtime(true) - $start), 4)."<br>\n";
}
结果:
no try with no surrounding try = 0.5130
no try with surrounding try = 0.5665
no except with no surrounding try = 3.6469
no except with surrounding try = 3.6979
except with no surrounding try = 3.8729
except with surrounding try = 3.8978
constant except with no surrounding try = 0.5741
constant except with surrounding try = 0.6234
通常,使用异常来防止意外故障,并在您的代码中使用错误检查来防止作为正常程序状态一部分的故障。 为了显示:
在数据库中未找到记录 - 有效状态,您应该检查查询结果并适当地向用户发送消息。
尝试获取记录时出现 SQL 错误 - 意外失败,记录可能存在也可能不存在,但您有程序错误 - 这是异常的好地方 - 在错误日志中记录错误,通过电子邮件向管理员发送堆栈跟踪,并显示向用户发送礼貌的错误消息,告知他出现问题并且您正在处理它。
异常代价高昂,但除非您使用它们处理整个程序流,否则任何性能差异都不应该是人类可察觉的。
我在 Google 上没有发现任何关于 Try/Catch 性能的内容,但是一个简单的测试,使用循环抛出错误而不是 IF 语句在 5000 次循环中产生 329 毫秒与 6 毫秒。
很抱歉发布一条非常旧的消息,但我阅读了评论,但我有点不同意,简单的代码段之间的差异可能很小,或者在 Try/Catch 用于非特定代码部分的情况下可能可以忽略不计总是可以预测的,但我也相信(未经测试)一个简单的:
if(isset($var) && is_array($var)){
foreach($var as $k=>$v){
$var[$k] = $v+1;
}
}
比
try{
foreach($var as $k=>$v){
$var[$k] = $v+1;
}
}catch(Exception($e)){
}
我也相信(未经测试)a:
<?php
//beginning code
try{
//some more code
foreach($var as $k=>$v){
$var[$k] = $v+1;
}
//more code
}catch(Exception($e)){
}
//output everything
?>
比代码中额外的 IF 更昂贵
这是一个很好的问题!
我已经对其进行了多次测试,但从未发现任何性能问题 ;-) 10 年前在 C++ 中确实如此,但我认为今天他们已经对其进行了很多改进,因为它非常有用和干净。
但我仍然害怕用它包围我的第一个切入点:
try {Controller::run();}catch(...)
我没有测试大量的函数调用和大包含......有人已经完全测试过了吗?
我更新了 Brilliand 的测试代码,通过增加更多的随机性,使其报告更易于理解,并且在统计上更真实。 由于我更改了一些测试以使其更公平,因此结果会有所不同,因此我将其写为不同的答案。
我的测试由:PHP 7.4.4 (cli) (built: Mar 20 2020 13:47:45) ( NTS )
<?php
function shuffle_assoc($array) {
$keys = array_keys($array);
shuffle($keys);
return array_merge(array_flip($keys), $array);
}
$c_e = new Exception('n');
function do_nothing($a, $b) {
return $a + $b;
}
function new_exception_but_not_throw($a, $b) {
try {
new Exception('k');
} catch (Exception $e) {
return $a + $b;
}
return $a + $b;
}
function new_exception_and_throw($a, $b) {
try {
throw new Exception('k');
} catch (Exception $e) {
return $a + $b;
}
return $a + $b;
}
function constant_exception_and_throw($a, $b) {
global $c_e;
try {
throw $c_e;
} catch (Exception $e) {
return $a + $b;
}
return $a + $b;
}
$tests = array(
'do_nothing with no surrounding try'=>function() {
do_nothing(5, 7);
},
'do_nothing with surrounding try'=>function() {
try {
do_nothing(5, 7);
} catch (Exception $e) {}
},
'new_exception_but_not_throw with no surrounding try'=>function() {
new_exception_but_not_throw(5, 7);
},
'new_exception_but_not_throw with surrounding try'=>function() {
try {
new_exception_but_not_throw(5, 7);
} catch (Exception $e) {}
},
'new_exception_and_throw with no surrounding try'=>function() {
new_exception_and_throw(5, 7);
},
'new_exception_and_throw with surrounding try'=>function() {
try {
new_exception_and_throw(5, 7);
} catch (Exception $e) {}
},
'constant_exception_and_throw with no surrounding try'=>function() {
constant_exception_and_throw(5, 7);
},
'constant_exception_and_throw with surrounding try'=>function() {
try {
constant_exception_and_throw(5, 7);
} catch (Exception $e) {}
},
);
$results = array_fill_keys(array_keys($tests), 0);
$testCount = 30;
const LINE_SEPARATOR = PHP_EOL; //"<br>";
for ($x = 0; $x < $testCount; ++$x) {
if (($testCount-$x) % 5 === 0) {
echo "$x test cycles done so far".LINE_SEPARATOR;
}
$tests = shuffle_assoc($tests);
foreach ($tests as $k => $f) {
$start = microtime(true);
for ($i = 0; $i < 1000000; ++$i) {
$f();
}
$results[$k] += microtime(true) - $start;
}
}
echo LINE_SEPARATOR;
foreach ($results as $type => $result) {
echo $type.' = '.number_format($result/$testCount, 4).LINE_SEPARATOR;
}
结果如下:
do_nothing with no surrounding try = 0.1873
do_nothing with surrounding try = 0.1990
new_exception_but_not_throw with no surrounding try = 1.1046
new_exception_but_not_throw with surrounding try = 1.1079
new_exception_and_throw with no surrounding try = 1.2114
new_exception_and_throw with surrounding try = 1.2208
constant_exception_and_throw with no surrounding try = 0.3214
constant_exception_and_throw with surrounding try = 0.3312
结论是:
所以最昂贵的部分是异常创建 - 不是 throw-catch 机制,但是与 do_nothing 场景相比,后者使这个简单的例程慢了 2 倍。
* 结论中的所有测量值都是四舍五入的,并不假装在科学上是准确的。
一般来说,它们很贵,在 PHP 中不值得。
由于它是一种检查表达式语言,因此您必须捕获任何引发异常的内容。
在处理不会抛出的遗留代码和会抛出的新代码时,只会导致混乱。
祝你好运!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.