65

在 php 5 中使用 try-catch 语句时需要考虑什么样的性能影响?

我以前在网上读过一些关于这个主题的古老且看似矛盾的信息。我目前必须使用的许多框架都是在 php 4 上创建的,并且缺乏 php 5 的许多细节。所以,我自己在使用 php 的 try-catchs 方面没有太多经验。

4

9 回答 9

72

要考虑的一件事是,没有抛出异常的 try 块的成本与实际抛出和捕获异常的成本是不同的问题。

如果仅在失败情况下抛出异常,那么您几乎肯定不会关心性能,因为每次执行程序都不会失败很多次。如果你在一个紧密的循环中失败了(又名:把你的头撞在砖墙上),你的应用程序可能会遇到比速度慢更严重的问题。所以不要担心抛出异常的成本,除非你被迫将它们用于常规控制流。

有人发布了一个关于引发异常的分析代码的答案。我自己从未测试过它,但我有信心地预测,这将显示出比只进出 try 块而不抛出任何东西更大的性能影响。

另一件要考虑的事情是,在嵌套调用很多级别的地方,在顶部进行一次 try...catch 甚至比检查返回值并在每次调用时传播错误更快。

在与这种情况相反的情况下,您发现将每个调用都包装在自己的 try...catch 块中,您的代码会变慢。而且更丑。

于 2008-09-19T18:58:02.090 回答
57

我很无聊,并分析了以下内容(我将计时代码省略了):

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

这些结果是一致的,并且无论以何种顺序进行测试,都保持相似的比例。

结论:添加处理罕见异常的代码并不比忽略异常的代码慢。

于 2009-01-14T23:28:00.257 回答
24

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
于 2013-07-16T19:07:25.377 回答
9

通常,使用异常来防止意外故障,并在代码中使用错误检查来防止属于正常程序状态的故障。为了显示:

  1. 在数据库中找不到记录 - 有效状态,您应该检查查询结果并适当地向用户发送消息。

  2. 尝试获取记录时出现 SQL 错误 - 意外失败,记录可能存在也可能不存在,但您有程序错误 - 这是异常的好地方 - 在错误日志中记录错误,通过电子邮件向管理员发送堆栈跟踪,并显示向用户发送一条礼貌的错误消息,告知他出了点问题,您正在处理它。

异常是昂贵的,但除非您使用它们处理整个程序流程,否则任何性能差异都不应该是人为的。

于 2008-09-19T18:36:04.470 回答
5

我在 Google 上没有发现任何有关 Try/Catch 性能的信息,但是一个简单的测试使用循环抛出错误而不是 IF 语句在 5000 个循环中产生 329 毫秒和 6 毫秒。

于 2008-09-19T18:35:45.657 回答
3

很抱歉发布了一条非常旧的消息,但我阅读了评论,但我有点不同意,简单的代码段差异可能很小,或者在 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)){
}

我也相信(未经测试):

<?php
//beginning code
try{
    //some more code
    foreach($var as $k=>$v){
        $var[$k] = $v+1;
    }
    //more code
}catch(Exception($e)){
}
//output everything
?>

比代码中额外的 IF 更昂贵

于 2012-10-08T16:29:07.663 回答
1

这是一个非常好的问题!

我已经对其进行了多次测试,从未发现任何性能问题;-) 10 年前在 C++ 中确实如此,但我认为今天他们已经改进了很多,因为它非常有用和清洁。

但我仍然害怕用它来包围我的第一个入口点:

try {Controller::run();}catch(...)

我没有用大量的函数调用和大的包含进行测试....有人已经完全测试了吗?

于 2009-12-03T11:43:34.860 回答
1

我更新了 Brilliand 的测试代码,使其报告更易于理解,并且通过增加更多随机性在统计上更真实。由于我更改了一些测试以使其更公平,因此结果会有所不同,因此我将其写为不同的答案。

我的测试执行者:PHP 7.4.4 (cli) (build: 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

结论是:

  • 添加额外的 try-catch 每 1000000 次迭代增加约 0.01 微秒
  • 异常抛出和捕获增加了 ~0.12 微秒(x12 与之前没有抛出任何东西并且没有捕获任何东西相比)
  • 异常创建增加了 ~0.91 微秒(x7.6 与前一行计算的 try-catch 机制的执行相比)

所以最昂贵的部分是异常创建 - 不是 throw-catch 机制,但是后者使这个简单的例程比 do_nothing 场景慢 2 倍。

* 结论中的所有测量值都是四舍五入的,并不假装科学准确。

于 2020-04-09T21:30:10.417 回答
-9

一般来说,它们在 PHP 中很昂贵且不值得。

由于它是一种检查表达式语言,因此您必须捕获任何引发异常的东西。

在处理不会抛出的遗留代码和会抛出的新代码时,只会导致混乱。

祝你好运!

于 2008-09-19T18:31:57.020 回答