[英]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.