php php中try-catch的性能
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/104329/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Performance of try-catch in php
提问by Travis
What kind of performance implications are there to consider when using try-catch statements in php 5?
在 php 5 中使用 try-catch 语句时需要考虑什么样的性能影响?
I've read some old and seemingly conflicting information on this subject on the web before. A lot of the framework I currently have to work with was created on php 4 and lacks many of the niceties of php 5. So, I don't have much experience myself in using try-catchs with php.
我之前在网上读过一些关于这个主题的旧的和看似矛盾的信息。我目前必须使用的许多框架都是在 php 4 上创建的,并且缺乏 php 5 的许多优点。因此,我自己在 php 中使用 try-catchs 方面没有太多经验。
回答by Steve Jessop
One thing to consider is that the cost of a try block where no exception is thrown is a different question from the cost of actually throwing and catching an exception.
需要考虑的一件事是,没有抛出异常的 try 块的成本与实际抛出和捕获异常的成本是不同的问题。
If exceptions are only thrown in failure cases, you almost certainly don't care about performance, since you won't fail very many times per execution of your program. If you're failing in a tight loop (a.k.a: banging your head against a brick wall), your application likely has worse problems than being slow. So don't worry about the cost of throwing an exception unless you're somehow forced to use them for regular control flow.
如果异常只在失败的情况下抛出,你几乎肯定不会关心性能,因为每次执行你的程序不会失败很多次。如果您在一个紧密的循环中失败(又名:用头撞砖墙),您的应用程序可能会遇到比缓慢更严重的问题。所以不要担心抛出异常的成本,除非你以某种方式被迫将它们用于常规控制流。
Someone posted an answer talking about profiling code which throws an exception. I've never tested it myself, but I confidently predict that this will show a much bigger performance hit than just going in and out of a try block without throwing anything.
有人发布了一个关于抛出异常的分析代码的答案。我自己从来没有测试过它,但我自信地预测这将显示出比只进出一个 try 块而不抛出任何东西更大的性能损失。
Another thing to consider is that where you nest calls a lot of levels deep, it can even be faster to have a single try...catch right at the top than it is to check return values and propagate errors on every call.
另一件要考虑的事情是,在嵌套调用很多级别的地方,在顶部进行一次 try...catch 甚至比检查返回值并在每次调用时传播错误更快。
In the opposite of that situation, where you find that you're wrapping every call in its own try...catch block, your code will be slower. And uglier.
与这种情况相反,您发现您将每个调用都包装在自己的 try...catch 块中,您的代码会变慢。而且更丑。
回答by jmucchiello
I was bored and profiled the following (I left the timing code out):
我很无聊并分析了以下内容(我省略了计时代码):
function no_except($a, $b) {
$a += $b;
return $a;
}
function except($a, $b) {
try {
$a += $b;
} catch (Exception $e) {}
return $a;
}
using two different loops:
使用两个不同的循环:
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) {}
}
With 1000000 runs on my WinXP box run apache and PHP 5.2.6:
在我的 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
These results were consistent and remained in similar proportion no matter which order the tests ran.
无论测试运行的顺序如何,这些结果都是一致的并且保持相似的比例。
Conclusion: Adding code to handle rare exceptions is no slower than code the ignores exceptions.
结论:添加处理罕见异常的代码并不比忽略异常的代码慢。
回答by Brilliand
Try-catch blocks are not a performance problem - the real performance bottleneck comes from creating exception objects.
Try-catch 块不是性能问题——真正的性能瓶颈来自于创建异常对象。
Test code:
测试代码:
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";
}
Results:
结果:
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
回答by Aeon
Generally, use an exception to guard against unexpected failures, and use error checking in your code against failures that are part of normal program state. To illustrate:
通常,使用异常来防止意外故障,并在您的代码中使用错误检查来防止作为正常程序状态一部分的故障。为了显示:
Record not found in database - valid state, you should be checking the query results and messaging the user appropriately.
SQL error when trying to fetch record - unexpected failure, the record may or may not be there, but you have a program error - this is good place for an exception - log error in error log, email the administrator the stack trace, and display a polite error message to the user advising him that something went wrong and you're working on it.
在数据库中未找到记录 - 有效状态,您应该检查查询结果并适当地向用户发送消息。
尝试获取记录时出现 SQL 错误 - 意外失败,记录可能存在也可能不存在,但您有程序错误 - 这是异常的好地方 - 在错误日志中记录错误,通过电子邮件向管理员发送堆栈跟踪,并显示向用户发送礼貌的错误消息,告知他出现问题并且您正在处理它。
Exceptions are expensive, but unless you handle your whole program flow using them, any performance difference should not be human-noticeable.
异常代价高昂,但除非您使用它们处理整个程序流,否则任何性能差异都不应该是人类可察觉的。
回答by Patrick Desjardins
I have not found anything on Try/Catch performance on Google but a simple test with a loop throwing error instead of a IF statement produce 329ms vs 6ms in a loop of 5000.
我在 Google 上没有发现任何关于 Try/Catch 性能的内容,但是一个简单的测试,使用循环抛出错误而不是 IF 语句在 5000 次循环中产生 329 毫秒与 6 毫秒。
回答by Fabrizio
Sorry to post to a very old message, but I read the comments and I somewhat disagree, the difference might be minimal with simple piece of codes, or it could be neglectable where the Try/Catch are used for specific parts of code that are not always predictable, but I also believe (not tested) that a simple:
很抱歉发布一条非常旧的消息,但我阅读了评论,但我有点不同意,简单的代码段之间的差异可能很小,或者在 Try/Catch 用于非特定代码部分的情况下可能可以忽略不计总是可以预测的,但我也相信(未经测试)一个简单的:
if(isset($var) && is_array($var)){
foreach($var as $k=>$v){
$var[$k] = $v+1;
}
}
is faster than
比
try{
foreach($var as $k=>$v){
$var[$k] = $v+1;
}
}catch(Exception($e)){
}
I also believe (not tested) that a:
我也相信(未经测试)a:
<?php
//beginning code
try{
//some more code
foreach($var as $k=>$v){
$var[$k] = $v+1;
}
//more code
}catch(Exception($e)){
}
//output everything
?>
is more expensive than have extra IFs in the code
比代码中额外的 IF 更昂贵
回答by Arkemlar
I updated Brilliand's test code to make it's report more understandable and also statitistically truthfully by adding more randomness. Since I changed some of its tests to make them more fair the results will be different, therefore I write it as different answer.
我更新了 Brilliand 的测试代码,通过增加更多的随机性,使其报告更易于理解,并且在统计上更真实。由于我更改了一些测试以使其更公平,因此结果会有所不同,因此我将其写为不同的答案。
My tests executed by: PHP 7.4.4 (cli) (built: Mar 20 2020 13:47:45) ( NTS )
我的测试由: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;
}
The results are following:
结果如下:
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
Conclusions are:
结论是:
- adding extra try-catch adds ~0.01 microsecond per 1000000 iterations
- exception throwing and catching adds ~0.12 microsecond (x12 compared to previous when nothing were thrown and nothing were catched)
- exception creation adds ~0.91 microsecond (x7.6 compared to execution of try-catch mechanism calculated in previous line)
- 添加额外的 try-catch 每 1000000 次迭代会增加 ~0.01 微秒
- 异常抛出和捕获增加了约 0.12 微秒(与之前没有抛出和没有捕获到任何内容时相比,x12)
- 异常创建增加了约 0.91 微秒(与上一行计算的 try-catch 机制的执行相比,x7.6)
So the most expensive part is exception creation - not the throw-catch mechanism, however latter made this simple routine 2 times slower compared to do_nothing scenarion.
所以最昂贵的部分是异常创建 - 不是 throw-catch 机制,但是与 do_nothing 场景相比,后者使这个简单的例程慢了 2 倍。
* all measurements in conclusions are rounded and are not pretends to be scientifically accurate.
* 结论中的所有测量值都是四舍五入的,并不是科学上准确的。
回答by Tom
Thats a very good question!
这是一个很好的问题!
I have tested it many times and never saw any performance issue ;-) It was true 10 years ago in C++ but I think today they have improved it a lot since its so usefull and cleaner.
我已经对其进行了多次测试,但从未发现任何性能问题 ;-) 10 年前在 C++ 中确实如此,但我认为今天他们已经对其进行了很多改进,因为它非常有用和干净。
But I am still afraid to surround my first entry point with it:
但我仍然害怕用它包围我的第一个切入点:
try {Controller::run();}catch(...)
I didnt test with plenty of functions call and big include .... Is anyone has fully test it already?
我没有测试大量的函数调用和大包含......有人已经完全测试过了吗?
回答by Ian P
Generally speaking, they're expensive and not worthwhile in PHP.
一般来说,它们很贵,在 PHP 中不值得。
Since it is a checked expressions language, you MUST catch anything that throws an exception.
由于它是一种检查表达式语言,因此您必须捕获任何引发异常的内容。
When dealing with legacy code that doesn't throw, and new code that does, it only leads to confusion.
在处理不会抛出的遗留代码和会抛出的新代码时,只会导致混乱。
Good luck!
祝你好运!

