Хватит использовать устаревшие методы оптимизации в PHP

Источник: «Stop Using These 5 Outdated Optimization Tricks in PHP»
Прекратите использовать приёмы микрооптимизации в PHP, жертвуя читабельностью кода ради мнимого повышения производительности. Более того, перестаньте учить этим трюкам новичков. В этом нет никакого смысла.

Преждевременная оптимизация — корень всех зол

—Дональд Кнут.

Мы все слышали эту цитату. Возможно, десятки раз. Но многие разработчики продолжают использовать старые добрые методы оптимизации.

Мне кажется, что любовь к "трюкам производительности" часто возникает у разработчиков с 10+ летним стажем. Они знают множество трюков, и у них есть мысли вроде "хуже не будет" и "производительность никогда не бывает лишней". Другой случай — это разработчики, которые хотят показать своё мастерство. "Ха-ха — посмотрите, какой трюк я знаю".

В результате мы получаем вот такой странный код:

<?php

$users = getUsers();

$usrsAmt = sizeof($users);

for ($i = 0; $i < $usrsAmt; $i++>) {
if (isset($users[$i]['name'][17])) {
$users[$i]['name'] = substr($users[$i]['name']) . '...(truncated)';
echo 'user ', $i, ' name was truncated';
}
}

Почему sizeof вместо count? Почему не foreach? Что за чертовщина с echo? isset индекса строки???

Всё это — ради "производительности". По крайней мере, мы так думали десять лет назад. А на самом деле и десять лет назад не было смысла использовать такие трюки. Да, некоторые странные вещи могли дать небольшой выигрыш в производительности, но…:

  1. Если действительно важны наносекунды — вы используете не тот инструмент (PHP)
  2. С вероятностью 99,99% узким местом вашего кода является не синтаксис цикла или двойные кавычки вместо одинарных

Всё ещё не убедились? А что, если я скажу, что некоторые старомодные советы больше не имеют смысла?

Я выбрал 5 самых распространённых приёмов, известных каждому разработчику, работающему с PHP 5.6 и старше. Все они не имеют смысла в PHP 8.x+

1. Использование isset() вместо strlen()

Это отличный пример идиоматического кода. Вместо того чтобы проверять фактическую длину строки, нам предложили просто проверить наличие N-го символа в строке.

<?php

$string = "some kind of string";

// нам предлагают заменить это
$stringIsLongerThan5 = strlen($string) > 5;

// на это
$stringIsLongerThen5 = isset($string[6]);

И в PHP 5.6 действительно есть разница в производительности. Мои локальные тесты показали прирост около 40%.

Но… это потеряло смысл после выхода PHP 7.0. Обработка строк была значительно улучшена, и теперь скорость осталась прежней.

Мне не удалось измерить разницу даже на наборе из 500K строк.

2. Использование цикла for вместо foreach

Всё просто — мы знаем, что цикл for быстрее цикла foreach.

<?php

$array = getHugeArray();

// нам предлагают заменить это
foreach ($array as &$item) {
$item = process($item);
}

// на это
$size = count($array);

for ($i = 0; $i < $size; $i++) {
$array[$i] = process($array[$i]);
}

Ладно, технически это всё ещё имеет смысл. Да, цикл for всё равно быстрее.

Но:

  1. Цикл foreach был значительно улучшен, и накладные расходы стали намного меньше, чем годы назад.
  2. Нет смысла использовать этот трюк, пока в вашем массиве не будет сотни тысяч элементов.

Так что да, эта штука работает, но не стоит использовать этот трюк до тех пор, пока не понадобится просмотреть огромный объём данных. В противном случае вы просто сделаете свой код менее читабельным без веской причины.

3. Использование одинарных кавычек вместо двойных

Логика была простой — PHP обрабатывает строки с одинарными кавычками как простые строки, а строки с двойными кавычками — как шаблонные строки, которые могут содержать переменные. И идея была проста — используйте строки с двойными кавычками только в том случае, если вы хотите передать в них переменную.

<?php

// нам предлагают заменить это
$userName = "John";
$userAge = "29";
$userNickname = "Super{$userName}{$userAge}";

// на это
$userName = 'John';
$userAge = '29';
$userNickname = "Super{$userName}{$userAge}";

Честно говоря, даже в те времена я сомневался в этом трюке. Серьёзно, я понятия не имею, что, черт возьми, нужно закодировать, чтобы получить реальный эффект от этой штуки.

Но теперь это официально не имеет смысла. Обработка строк была значительно улучшена в релизе PHP 7.0, и теперь они невероятно быстры. Не стесняйтесь использовать двойные кавычки везде. Серьёзно, перестаньте заморачиваться с этими кавычками.

4. Использование конкатенации вместо шаблонов строк

Идея была очень похожа на предыдущую. Шаблоны строк работали медленнее, чем простые, поэтому разработчики решили, что быстрее будет использовать конкатенацию строк, а не шаблоны (почему?). Даже если это выглядит уродливее, чем шаблоны строки.

<?php

// нам предлагают заменить это
$userName = "John";
$userAge = "29";
$userNickname = "Super{$userName}{$userAge}";

// на это
$userName = 'John';
$userAge = '29';
$userNickname = 'Super' . $userName . $userAge;

Суть та же — делать это после PHP 7+ не имеет смысла. Скажу больше — автор движка PHP утверждает, что в современном PHP конкатенация может быть даже медленнее, чем шаблоны строк.

5. Использование префиксного инкремента вместо постфиксного

<?php

// нам предлагают заменить это
$i++;

// на это
$++i;

По-моему, это выглядит глупо. Серьёзно, не может быть, чтобы эта штука дала какое-то значительное улучшение производительности. Но я слишком часто видел это в реальном коде, чтобы сомневаться, что люди верят в это.

Да, 10 лет назад разница в скорости была крошечной, но Zend Engine постоянно совершенствовался, и теперь разница меньше, чем погрешность измерений, даже когда выполняются миллионы операций (хотел бы я посмотреть на PHP-код, которому действительно нужно делать инкрементацию миллионы раз).

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

Часть этих трюков просто не работает, а часть даст минус 0,001 микросекунды во времени выполнения скрипта. Это бессмысленно. Один SQL-запрос, не попавший в индекс, уничтожит все эти "улучшения". И наоборот, правильно используемый кэш может дать секунды разницы вместо микросекунд.

Вы знаете другие бессмысленные "оптимизации", которые только портят читабельность кода? Поделитесь в комментариях.

Дополнительные материалы

Предыдущая Статья

Отключение сообщения packages are looking for funding в NPM

Следующая Статья

Как работают дженерики в TypeScript