Стоимость вызова функции в ПХП
В комментариях к предыдущей заметке мне напомнили о немаловажной подробности, которую я упустил из вида в прошлый раз — в ПХП вызов функции дорогое удовольствие. Как пишут на демотиваторах «по возможности избегайте этого».
Например, если в прошлом примере с массивами array_push заменить на добавление элемента в массив через „[]“, то производительность сразу возрастёт на 0,04 секунды.
В самом деле, есть масса ситуаций, когда вызов функции можно заменить конструкцией или получением данных из константы, так же существуют частные случаи, когда такая замена возможна:
# занесение элемента в массив:
array_push($stack, $element);
$stack[] = $element;
# получение версии ПХП, есть ещё
# PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION, PHP_VERSION_ID и PHP_EXTRA_VERSION
if (phpversion() === "5.0.0") {}
if (PHP_VERSION === "5.0.0") {}
# способ подключения ПХП
if (php_sapi_name() === 'cli') {}
if (PHP_SAPI === 'cli') {}
# проверка существования ключа, но, увы, это не одно и тоже:
# isset не покажет true, если ключ существует, но значение null
if (array_key_exists('key1', $arr) && array_key_exists('key2', $arr)) {}
if (isset($arr['key'], $arr['key2'])) {}
# для массивов, ключи которых не перекрываются (операция «+» добавит только те ключи,
# которые отсутствуют в первом массиве)
$arr = array_merge($arr1, $arr2);
$arr = $arr1 + $arr2;
# операционная система
if (php_uname('s') === 'Darwin') {}
if (PHP_OS === 'Darwin') {}
# получение переменной окружения. Есть отличие: массив $_ENV может быть перезаписан другим кодом,
# при этом реальное значение переменной окружения не изменится, кроме того,
# getevn не сгенерирует предупреждение, если значения с таким ключом нет
if (getenv('VARNAME') === 'value') {}
if ($_ENV['VARNAME'] === 'value') {}
Наверняка есть и ещё случаи, которые я не смог сходу вспомнить, буду благодарен, если кто-то из читателей пополнит список или что-то уточнит.
какое-то странное поведение для isset
Комментарий для sunchaser.info:
В документации по isset написано:
Вероятно, история вопроса такова, что NULL и «переменная не определена» какое-то время не различалось вообще никак. Да и сейчас, если вывести значение переменной, которой нет, то будет NULL и notice (или warning, не помню точно).
Комментарий для Евгения Степанищева:
нотис, да. да я понимаю, что это совместимость, просто она иногда к совершенно неадекватному поведению приводит. тут к слегка неадекватному
Комментарий для sunchaser.info:
Если очень хочется, то можно проверить — NULL там или неопределённая переменная:
Если переменная глобальная, то можно проще — проверить наличие ключа в массиве $GLOBALS
Но на мой взгляд, в коде не должна создаваться ситуация, где эти две вещи нужно различать.
Вместо (ob_start + var_dump) можно использовать print_r($expr, true) или var_export($expr, true)
Комментарий для alex:
Вместо ob_start + var_dump — можно. У меня используется ob_start + debug_zval_dump.
Это все экономия на спичках в стиле «что быстрее: одинарные кавычки или двойные».
Комментарий для Koc:
Нет.
Комментарий для Koc:
И чтобы не быть в ваших глазах голословным невеждой, бросающимся ничем не подкреплённым мнением, я сделал замер. На моём PHP 5.3.10 разница между вызовом функции и использованием константы в три раза.
Комментарий для Евгения Степанищева:
Приведённые примеры — экономия на спичках — ну в каком реальном коде будут многократно вызывать, к примеру, phpversion, чтобы это значительно сказывалось на производительности всего кода?
Комментарий для mr-simm.livejournal.com:
Какие-то странные вопросы. Ответ очевиден же.
ПХП сильно меняется со временем. Быстрее проверить версию ПХП константой, чем вызвать function_exists. Иногда нужно проверить есть ли у какой-то функции параметр, который появился в какой-то версии.
Комментарий для mr-simm.livejournal.com:
Представте себе веб проект с миллионной посещаемостью. Даже если в коде вызывается функция всего один раз, то это не значит что сам код будет вызываться всего один раз. При миллионных ежедневных посещениях это уже не экономия на спичках!
При этом возможно этот проект будет находитmся не на одном сервере, а может быть распределенным по тысячам серверов и на разных серверах может быть разная версия PHP.
Комментарий для Bagir:
Даже при миллионных посещениях это как было ноль целых хрен десятых, так и останется. Нет, Вы действительно думаете, что это будет самым узким местом в коде?
Комментарий для mr-simm.livejournal.com:
Вы знаете, я много веб-приложений написал, которые работают под хорошей нагрузкой. Десяток трёхгрошовых оптимизаций, которые можно в коде назаменять за пять минут, иногда дают такой же прирост, как и сложная оптимизация некого куска кода, который надо не один день переписывать.
Ну ведь не о замене функций phpversion, php_sapi_name и php_uname речь?
Комментарий для mr-simm.livejournal.com:
В том числе, почему нет-то? Как правильно заметил товарищ Багир, в распределённом проекте часто работает один код на куче конфигураций и совсем нередка ситуация, когда в коде есть проверки. У меня часто встречалось такое, что сервера вообще были неподконтрольны, приходилось всё время проверять что на них.
Функция time() и переменная $_SERVER[’REQUEST_TIME’]
Комментарий для wapplay:
Не совсем одно и то же, но пример очень хороший, спасибо! Кстати говоря, в ПХП 5.4 появилось значение REQUEST_TIME_FLOAT, с микросекундами.
Вот ещё вспомнил:
if (count($array)) {}
лучше заменить на
if ((bool) $array) {}
if (is_null($var)) {}
лучше заменить на
if ($var === null) {}
А обязательно писать
if ((bool) $array) {}
не прокатит просто
if ($array) {}
?
Комментарий для karudo.ya.ru:
Прокатит. Это моя собственная вкусовщина, которой я всех учу :)