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

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

Например, если в прошлом примере с массивами 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') {}
Наверняка есть и ещё случаи, которые я не смог сходу вспомнить, буду благодарен, если кто-то из читателей пополнит список или что-то уточнит.
9 июня 2012 10:38

SunChaser (sunchaser.info)
9 июня 2012, 14:05

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

bolk (bolknote.ru)
9 июня 2012, 15:45, ответ предназначен SunChaser (sunchaser.info):

В документации по isset написано:
Determine if a variable is set and is not NULL.
Вероятно, история вопроса такова, что NULL и «переменная не определена» какое-то время не различалось вообще никак. Да и сейчас, если вывести значение переменной, которой нет, то будет NULL и notice (или warning, не помню точно).

SunChaser (sunchaser.info)
9 июня 2012, 17:01, ответ предназначен bolk (bolknote.ru):

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

bolk (bolknote.ru)
9 июня 2012, 17:18, ответ предназначен SunChaser (sunchaser.info):

Если очень хочется, то можно проверить — NULL там или неопределённая переменная:
ob_start();
debug_zval_dump($var);
var_dump(preg_match('/refcount.(\d+)/', ob_get_clean(), $m) && $m[1] <> 1);
Если переменная глобальная, то можно проще — проверить наличие ключа в массиве $GLOBALS

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

alex (инкогнито)
9 июня 2012, 18:29

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

bolk (bolknote.ru)
9 июня 2012, 18:47, ответ предназначен alex

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

Koc (инкогнито)
9 июня 2012, 18:48

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

bolk (bolknote.ru)
9 июня 2012, 19:07, ответ предназначен Koc

Нет.

bolk (bolknote.ru)
9 июня 2012, 19:16, ответ предназначен Koc

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

SiMM (mr-simm.livejournal.com)
10 июня 2012, 09:18, ответ предназначен bolk (bolknote.ru):

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

bolk (bolknote.ru)
10 июня 2012, 19:00, ответ предназначен SiMM (mr-simm.livejournal.com):

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

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

Bagir (инкогнито)
10 июня 2012, 19:47, ответ предназначен SiMM (mr-simm.livejournal.com):

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

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

SiMM (mr-simm.livejournal.com)
13 июня 2012, 09:05, ответ предназначен Bagir

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

bolk (bolknote.ru)
13 июня 2012, 14:37, ответ предназначен SiMM (mr-simm.livejournal.com):

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

SiMM (mr-simm.livejournal.com)
13 июня 2012, 15:57

Десяток трёхгрошовых оптимизаций
Ну ведь не о замене функций phpversion, php_sapi_name и php_uname речь?

bolk (bolknote.ru)
13 июня 2012, 16:24, ответ предназначен SiMM (mr-simm.livejournal.com):

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

wapplay (инкогнито)
24 июня 2012, 07:17

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

bolk (bolknote.ru)
24 июня 2012, 08:40, ответ предназначен wapplay

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

bolk (bolknote.ru)
26 июня 2012, 16:35

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

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

bolk (bolknote.ru)
11 июля 2012, 20:31

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

karudo (karudo.ya.ru)
12 июля 2012, 07:41

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

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

bolk (bolknote.ru)
12 июля 2012, 12:41, ответ предназначен karudo (karudo.ya.ru):

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

Ваше имя или адрес блога (можно OpenID):

Текст вашего комментария, не HTML:

Кому бы вы хотели ответить (или кликните на его аватару)