programing

php에서 try-catch의 성능

coolbiz 2023. 9. 7. 22:35
반응형

php에서 try-catch의 성능

php 5에서 try-catch 문을 사용할 때 고려해야 할 성능적인 의미는 무엇입니까?

저는 이전에 웹에서 이 주제에 대한 오래되고 모순되어 보이는 정보를 읽은 적이 있습니다.제가 현재 작업해야 할 많은 프레임워크가 php 4에서 만들어졌고 php 5의 장점이 많이 부족합니다.그래서 저는 php로 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 box에서 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

일반적으로 예기치 않은 실패를 방지하기 위해 예외를 사용하고 정상적인 프로그램 상태의 일부인 실패에 대해 코드에 오류 검사를 사용합니다.설명하기:

  1. 데이터베이스에서 레코드를 찾을 수 없습니다. 유효한 상태이므로 쿼리 결과를 확인하고 사용자에게 메시지를 적절하게 보내야 합니다.

  2. 레코드를 가져오려고 할 때 SQL 오류 - 예기치 않은 실패, 레코드가 있을 수도 있고 없을 수도 있지만 프로그램 오류가 있습니다 - 예외적으로 사용할 수 있는 위치입니다 - 오류 로그에 오류를 기록하고 관리자에게 스택 트레이스를 이메일로 전송한 후 사용자가 작업 중임을 알리는 정중한 오류 메시지를 표시합니다.

예외는 비용이 많이 들지만, 이 예외를 사용하여 전체 프로그램 흐름을 처리하지 않는 한 성능 차이가 사람의 눈에 띄지 않아야 합니다.

나는 구글에서 Try/Catch 성능에 대해 아무것도 발견하지 못했지만 IF 문 대신 루프 던지기 오류가 있는 간단한 테스트는 5000 루프에서 329ms 대 6ms를 생성합니다.

아주 오래된 메시지에 글을 올리게 되어 죄송합니다만, 댓글을 읽었는데 다소 동의하지 않습니다. 간단한 코드 조각으로 차이가 최소화되거나 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가 있는 것보다 더 비쌉니다.

저는 Brilliand의 테스트 코드를 업데이트하여 보고서를 더 이해하기 쉽고 통계적으로 진실되게 만들기 위해 무작위성을 더했습니다.좀 더 공정하게 하기 위해 일부 테스트를 바꿨기 때문에 결과가 다를 것이기 때문에 다른 답변으로 씁니다.

실행한 테스트: PHP 7.4.4 (cli) (작성: 2020년 3월 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

결론은 다음과 같습니다.

  • 추가 시도 횟수를 추가하면 1000000회 반복당 ~0.01마이크로초가 추가됩니다.
  • 예외 던지기 및 잡기는 ~0.12마이크로초를 추가합니다(아무것도 던지지 않고 아무것도 잡지 않은 이전과 비교했을 때 x12).
  • 예외 생성으로 ~0.91마이크로초가 추가됨(이전 줄에서 계산된 시행 횟수 메커니즘과 비교하여 x7.6)

따라서 가장 비용이 많이 드는 부분은 예외 생성입니다. - 송구 캐치 메커니즘이 아니라 후자는 do_nothing 시나리오에 비해 이 간단한 루틴을 2배 느리게 만듭니다.

* 결론의 모든 측정값은 둥글고 과학적으로 정확한 것처럼 보이지 않습니다.

아주 좋은 질문입니다!

여러 번 테스트해봤는데 성능에 문제가 있는 것을 본 적이 없습니다;-) C++에서는 10년 전에도 그랬지만, 오늘은 너무 유용하고 깨끗해서 많이 개선된 것 같습니다.

하지만 저는 여전히 제 첫 진입 지점을 그것으로 둘러싸기가 두렵습니다.

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

나는 많은 기능 호출과 큰 포함을 가지고 테스트를 하지 않았습니다..이미 충분히 테스트해본 사람이 있습니까?

일반적으로 말해서, 그것들은 값이 비싸고 PHP의 가치가 없습니다.

확인된 표현 언어이기 때문에 예외가 있는 것은 반드시 잡아야 합니다.

던지지 않는 레거시 코드와 던지지 않는 새로운 코드를 다룰 때는 혼란만 초래합니다.

행운을 빕니다.

언급URL : https://stackoverflow.com/questions/104329/performance-of-try-catch-in-php

반응형