Пишу, по большей части, про историю, свою жизнь и немного про программирование.

Стоимость вызова функции в ПХП

В комментариях к предыдущей заметке мне напомнили о немаловажной подробности, которую я упустил из вида в прошлый раз — в ПХП вызов функции дорогое удовольствие. Как пишут на демотиваторах «по возможности избегайте этого».

Например, если в прошлом примере с массивами 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') {}

Наверняка есть и ещё случаи, которые я не смог сходу вспомнить, буду благодарен, если кто-то из читателей пополнит список или что-то уточнит.

22 комментария
SunChaser (sunchaser.info) 2012

isset не покажет true, если ключ существует, но значение null

какое-то странное поведение для isset

Евгений Степанищев (bolknote.ru) 2012

Комментарий для sunchaser.info:

В документации по isset написано:

Determine if a variable is set and is not NULL.

Вероятно, история вопроса такова, что NULL и «переменная не определена» какое-то время не различалось вообще никак. Да и сейчас, если вывести значение переменной, которой нет, то будет NULL и notice (или warning, не помню точно).

SunChaser (sunchaser.info) 2012

Комментарий для Евгения Степанищева:

нотис, да. да я понимаю, что это совместимость, просто она иногда к совершенно неадекватному поведению приводит. тут к слегка неадекватному

Евгений Степанищев (bolknote.ru) 2012

Комментарий для sunchaser.info:

Если очень хочется, то можно проверить — NULL там или неопределённая переменная:

ob_start();
debug_zval_dump($var);
var_dump(preg_match(’/refcount.(\d+)/’, ob_get_clean(), $m) && $m[1] <> 1);

Если переменная глобальная, то можно проще — проверить наличие ключа в массиве $GLOBALS

Но на мой взгляд, в коде не должна создаваться ситуация, где эти две вещи нужно различать.

alex 2012

Вместо (ob_start + var_dump) можно использовать print_r($expr, true) или var_export($expr, true)

Евгений Степанищев (bolknote.ru) 2012

Комментарий для alex:

Вместо ob_start + var_dump — можно. У меня используется ob_start + debug_zval_dump.

Koc 2012

Это все экономия на спичках в стиле «что быстрее: одинарные кавычки или двойные».

Евгений Степанищев (bolknote.ru) 2012

Комментарий для Koc:

Нет.

Евгений Степанищев (bolknote.ru) 2012

Комментарий для Koc:

И чтобы не быть в ваших глазах голословным невеждой, бросающимся ничем не подкреплённым мнением, я сделал замер. На моём PHP 5.3.10 разница между вызовом функции и использованием константы в три раза.

SiMM (mr-simm.livejournal.com) 2012

Комментарий для Евгения Степанищева:

Приведённые примеры — экономия на спичках — ну в каком реальном коде будут многократно вызывать, к примеру, phpversion, чтобы это значительно сказывалось на производительности всего кода?

Евгений Степанищев (bolknote.ru) 2012

Комментарий для mr-simm.livejournal.com:

Какие-то странные вопросы. Ответ очевиден же.

ПХП сильно меняется со временем. Быстрее проверить версию ПХП константой, чем вызвать function_exists. Иногда нужно проверить есть ли у какой-то функции параметр, который появился в какой-то версии.

Bagir 2012

Комментарий для mr-simm.livejournal.com:

Представте себе веб проект с миллионной посещаемостью. Даже если в коде вызывается функция всего один раз, то это не значит что сам код будет вызываться всего один раз. При миллионных ежедневных посещениях это уже не экономия на спичках!

При этом возможно этот проект будет находитmся не на одном сервере, а может быть распределенным по тысячам серверов и на разных серверах может быть разная версия PHP.

SiMM (mr-simm.livejournal.com) 2012

Комментарий для Bagir:

Даже если в коде вызывается функция всего один раз, то это не значит что сам код будет вызываться всего один раз. При миллионных ежедневных посещениях это уже не экономия на спичках!

Даже при миллионных посещениях это как было ноль целых хрен десятых, так и останется. Нет, Вы действительно думаете, что это будет самым узким местом в коде?

Евгений Степанищев (bolknote.ru) 2012

Комментарий для mr-simm.livejournal.com:

Вы знаете, я много веб-приложений написал, которые работают под хорошей нагрузкой. Десяток трёхгрошовых оптимизаций, которые можно в коде назаменять за пять минут, иногда дают такой же прирост, как и сложная оптимизация некого куска кода, который надо не один день переписывать.

SiMM (mr-simm.livejournal.com) 2012

Десяток трёхгрошовых оптимизаций

Ну ведь не о замене функций phpversion, php_sapi_name и php_uname речь?

Евгений Степанищев (bolknote.ru) 2012

Комментарий для mr-simm.livejournal.com:

В том числе, почему нет-то? Как правильно заметил товарищ Багир, в распределённом проекте часто работает один код на куче конфигураций и совсем нередка ситуация, когда в коде есть проверки. У меня часто встречалось такое, что сервера вообще были неподконтрольны, приходилось всё время проверять что на них.

wapplay 2012

Функция time() и переменная $_SERVER[’REQUEST_TIME’]

Евгений Степанищев (bolknote.ru) 2012

Комментарий для wapplay:

Не совсем одно и то же, но пример очень хороший, спасибо! Кстати говоря, в ПХП 5.4 появилось значение REQUEST_TIME_FLOAT, с микросекундами.

Евгений Степанищев (bolknote.ru) 2012

Вот ещё вспомнил:

if (count($array)) {}
лучше заменить на
if ((bool) $array) {}

Евгений Степанищев (bolknote.ru) 2012

if (is_null($var)) {}
лучше заменить на
if ($var === null) {}

karudo (karudo.ya.ru) 2012

А обязательно писать
if ((bool) $array) {}

не прокатит просто
if ($array) {}
?

Евгений Степанищев (bolknote.ru) 2012

Комментарий для karudo.ya.ru:

Прокатит. Это моя собственная вкусовщина, которой я всех учу :)