web-dev-qa-db-ja.com

PHPでのtry-catchのパフォーマンス

PHP 5でtry-catchステートメントを使用する際に考慮すべきパフォーマンスへの影響はどのようなものですか?

私は以前、この主題に関する古い、一見矛盾する情報をウェブで読んだことがあります。現在使用しているフレームワークの多くはphp 4で作成されており、php 5の多くの優れた機能がありません。そのため、phpでtry-catchを使用した経験はあまりありません。

58
Travis

考慮すべきことの1つは、例外がスローされないtryブロックのコストは、実際に例外をスローしてキャッチするコストとは異なる問題であることです。

例外が失敗した場合にのみスローされる場合、プログラムの実行ごとに何度も失敗しないので、パフォーマンスはほとんど問題になりません。タイトなループで失敗している場合(別名:レンガの壁に頭をぶつけている場合)、アプリケーションの速度が遅くなるよりも問題が発生する可能性があります。したがって、通常の制御フローに何らかの方法でそれらを使用せざるを得ない場合を除いて、例外をスローするコストを心配する必要はありません。

誰かが、例外をスローするコードのプロファイリングについて話している回答を投稿しました。私自身はテストしたことがありませんが、何もスローせずにtryブロックに出入りするよりもはるかに大きなパフォーマンスヒットを示すと確信しています。

考慮すべきもう1つのことは、多くのレベルの呼び出しをネストする場合、戻り値をチェックしてすべての呼び出しでエラーを伝播するよりも、1回試行してすぐにキャッチする方が高速になる可能性があることです。

その逆の状況では、すべての呼び出しを独自のtry ... catchブロックでラップしていることがわかり、コードは遅くなります。そして醜い。

67
Steve Jessop

私は退屈し、次のプロファイルを作成しました(タイミングコードは省略しました)。

function no_except($a, $b) { 
    $a += $b;
    return $a;
}
function except($a, $b) { 
    try {
        $a += $b;
    } catch (Exception $e) {}
    return $a;
}

2つの異なるループを使用する:

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

これらの結果は一貫しており、テストを実行した順序に関係なく、同様の比率で維持されました。

結論:まれな例外を処理するコードを追加することは、無視する例外をコード化することより遅くはありません。

52
jmucchiello

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
19
Brilliand

一般に、例外を使用して予期しないエラーから保護し、通常のプログラム状態の一部であるエラーに対してコードでエラーチェックを使用します。説明する:

  1. レコードがデータベースに見つかりません-有効な状態です。クエリ結果を確認し、ユーザーに適切にメッセージを送る必要があります。

  2. レコードをフェッチしようとしたときのSQLエラー-予期しないエラー、レコードがあるかどうかは不明ですが、プログラムエラーがあります-これは例外の良い場所です-エラーログにエラーを記録し、管理者にスタックトレースを電子メールで送信して表示します何か問題が発生し、作業中であることをユーザーに通知する丁寧なエラーメッセージ。

例外はコストがかかりますが、例外を使用してプログラムフロー全体を処理しない限り、パフォーマンスの違いは人間にはわかりません。

8
Aeon

GoogleのTry/Catchパフォーマンスについては何も見つかりませんでしたが、IFステートメントの代わりにループスローエラーを使用した簡単なテストでは、5000のループで329ミリ秒と6ミリ秒が生成されます。

5

非常に古いメッセージに投稿して申し訳ありませんが、コメントを読んだところ多少不満があります。単純なコードの一部では違いが最小限か、コードの特定の部分に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を含めるよりも高価です

2
Fabrizio

それはとても良い質問です!

私は何度もテストしましたが、パフォーマンスの問題はまったく見られませんでした;-) 10年前のC++の場合は真実でしたが、今日は非常に便利でクリーンなので、大幅に改善されたと思います。

しかし、私はそれでも私の最初のエントリポイントを囲むことを恐れています。

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

たくさんの関数呼び出しと大きなインクルードを使ってテストしませんでした...すでに誰かが完全にテストしましたか?

1
Tom