Какие последствия для производительности следует учитывать при использовании утверждений try-catch в php 5?
Раньше я читал какую-то старую и, казалось бы, противоречивую информацию по этому вопросу в Интернете. Большая часть структуры, с которой я в настоящее время работаю, была создана на php 4 и не имеет многих тонкостей php 5. Итак, у меня нет большого опыта использования try-catch с php.
Одна вещь, которую следует учитывать, заключается в том, что стоимость блока try, где не генерируется исключение, – это другой вопрос из стоимости фактического броска и исключения исключения.
Если исключения случаются только в случаях сбоя, вы почти наверняка не заботитесь о производительности, так как вы не будете терпеть неудачу очень много раз за выполнение вашей программы. Если вы терпите неудачу в петле (иначе: стучите головой о кирпичную стену), ваше приложение, вероятно, имеет более серьезные проблемы, чем медленное. Поэтому не беспокойтесь о стоимости выброса исключения, если вы каким-то образом не вынуждены использовать их для регулярного потока управления.
Кто-то отправил ответ, говорящий о профилировании кода, который выдает исключение. Я никогда не тестировал его сам, но я уверенно предсказываю, что это будет показывать гораздо больший успех, чем просто входить и выходить из блока try, не бросая ничего.
Еще одна вещь, которую следует учитывать, заключается в том, что там, где вы гнездо называет много уровней, может быть даже быстрее иметь одну попытку … поймать вверху справа, чем проверять возвращаемые значения и распространять ошибки при каждом вызове.
В противоположность этой ситуации, когда вы обнаружите, что вы завершаете каждый вызов в своем собственном блоке 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) {} }
С 1000000 запусков в моем окне WinXP запустите 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 при попытке получить запись – неожиданный сбой, запись может быть или не быть, но у вас есть программная ошибка – это хорошее место для ошибки журнала ошибок в журнале ошибок, отправки администратору трассировки стека и отображения вежливое сообщение об ошибке, сообщающее пользователю, что что-то пошло не так, и вы работаете над этим.
Исключения стоят дорого, но если вы не будете обрабатывать весь ваш поток программ, используя их, любая разница в производительности не должна быть заметной для человека.
Я ничего не нашел в работе Try / Catch в Google, но простой тест с ошибкой металирования цикла вместо утверждения IF выражает 329ms против 6ms в петле 5000.
Извините, что вы отправляете сообщение в очень старом сообщении, но я прочитал комментарии, и я несколько не согласен, разница может быть минимальной с простой частью кода или может быть пренебрежимой, если 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 в коде
Это очень хороший вопрос!
Я тестировал его много раз и никогда не видел никаких проблем с производительностью 😉 Это было правдой 10 лет назад на C ++, но я думаю, что сегодня они значительно улучшили его с момента его столь полезного и чистого.
Но я все еще боюсь окружить свою первую точку входа:
try {Controller::run();}catch(...)
Я не тестировал с большим количеством вызовов функций и большого включения …. Кто-нибудь уже полностью его тестировал?
Вообще говоря, они дороги и не стоят в PHP.
Поскольку это проверенный язык выражений, вы ДОЛЖНЫ улавливать все, что выдает исключение.
Когда вы имеете дело с устаревшим кодом, который не выбрасывает, и новый код, который это делает, это только приводит к путанице.
Удачи!