Написать пост

Особенности PHP 7+: строки, массивы и Copy on write

Аватарка пользователя Izenx

Рассказываем об особенностях языка программирования PHP от 7 версии. Рассматриваем строки, массивы, Interned variables и Copy on write.

Все популярные языки программирования похожи, они основываются на общих фундаментальных концепциях. Были разработаны и развиваются с учетом лучших практик. Несмотря на сходства, каждый язык программирования также имеет свои уникальные особенности, в этой статье поговорим о некоторых особенностях языка PHP (7+).

Принцип работы PHP

Стандартный путь выполнения PHP скрипта следующий: поступает запрос на выполнение PHP скрипта, создается процесс выполнения, где PHP код компилируется в байт-код. Далее байт-код оптимизируется и исполняется виртуальной машиной (ZEND). После выполнения, процесс уничтожается со всей сопутствующей информацией, и при повторном вызове процесс полностью создается заново. Поэтому существует такое выражение: “PHP рождён, чтобы умирать”.

Ситуацию меняют OPcache и JIT. OPcache (Opcode Cache) – это расширение для PHP, которое выполняет кэширование скомпилированного байт-кода PHP. OPcache сохраняет этот скомпилированный байт-код в памяти, чтобы избежать повторной компиляции при каждом запросе. JIT (Just-In-Time) генерирует и кэширует машинный код из оптимизированного байт-кода, который без изменений может вызывать интерпретатор. JIT работает только с теми частями кода, которые активно используются во время выполнения.

Copy on write

Механизм Copy on write используется для оптимизации использования памяти. В PHP, когда передается значение в функцию или одна переменная присваивается другой (и во многих других случаях), то будет создана отдельная копия значения:

			$a = 1;
$b = $a;
$a++; // Увеличится только $a
function addZero($n) {
    $n[]=0;
}
$arr = [1,2,3,4,5,6,7,8,9,10];
addZero($arr); // Массив $arr не будет изменен вне функции addZero
var_dump(memory_get_usage()); // Потребление памяти int(405808)
		

Очевидно, что если передать большой массив в функцию, то выделение ресурсов на его копирование не сулит ничем хорошим. Чтобы избежать лишнего копирования, PHP использует copy on write: копирование значения происходит только при его модификации:

			$a = 1;
$b = $a;
$a++; // Увеличится только $a
function addZero($n) {
    //$n[]=0; Убрали модификацию массива
}
$arr = [1,2,3,4,5,6,7,8,9,10];
addZero($arr);
var_dump(memory_get_usage()); // Потребление памяти стало меньше int(405280)
		

Стоит заметить, что копирование это не совсем копирование в привычном понимании. Это означает, что если вы “копируете” значение, вы на самом деле повторно используете старое значение и увеличиваете его счетчик ссылок (refcount). Только после того, как вы выполните какую-либо модификацию, будет сделана реальная копия (дубликат).

Строки

Строка (тип string) – это набор символов, где символ – это то же самое, что и байт. Это значит, что PHP поддерживает ровно 256 различных символов, а также то, что в PHP нет встроенной поддержки многобайтовых кодировок. Для работы с ними есть расширение mbstring, которое предоставляет функции для работы с многобайтовыми строками:

			$str = 'Привет'; // Многобайтовая строка
var_dump(strlen($str)); // int(12)
var_dump(mb_strlen($str)); // int(6)
		

Кавычки

Строки в разных кавычках обрабатываются по разному. В двойных кавычках PHP будет искать и обрабатывать переменные, следовательно, будет тратить больше ресурсов на обработку. Это также касается и функций вывода строк: echo и print будут оптимальнее чем printf.

Interned variables

В PHP хранение переменных осуществляется в variable containers, это значит, что можно повторно использовать переменные, память при это не выделяется.

На примере строк: Interned strings относится к процессу оптимизации памяти, при котором один экземпляр строки хранится в памяти и используется повторно, вместо создания дубликатов этой строки.

Когда вы объявляете строковую переменную в PHP, например:

			$str1 = "Hello";
$str2 = "Hello";
		

Обычно каждая строка будет иметь свой собственный экземпляр в памяти. Однако, PHP оптимизирует использование памяти и делает так, чтобы обе переменные ссылались на один и тот же экземпляр строки “Hello”. Но, если строки создаются динамически или изменяются во время выполнения программы, они не будут интернированы.

Массивы

Массивы в PHP отличаются от классических массивов в других языках. В PHP массивы это упорядоченные словари, но в зависимости от ключей, интерпретатор выбирает наиболее оптимальное внутреннее представление массива. Выделить можно 3 типа массивов: неизменяемые, packed array и обычные массивы.

Неизменяемый массив заполнен неизменяемыми элементами, чьи значения не требуют вычислений и известны во время компиляции.

Packed array получается в случае, когда ключи массива это целые числа в порядке возрастания. Он занимает меньше памяти (чем обычный массив) и прост для итерации.

Packed array:

			$arr = [];
for($i=0;$i<10000;$i++) 
{
  $arr []= $i;
 }
 $arr [] = 1;
 var_dump(memory_get_usage()); // int(917272)
		

Обычный массив (добавим ключ с типом string):

			$arr = [];
for($i=0;$i<10000;$i++) 
{
  $arr []= $i;
 }
 $arr['some_str_key'] = 'some_str_val';
 var_dump(memory_get_usage()); // int(1044456)
		

Потребление памяти заметно стало больше.

Обход массивов

Обход также будет отличатся для packed и обычных массивов.

При обходе packed array, функции использующие callback (map, filter и другие) и foreach будут менее эффективны по памяти и производительности чем циклы for и while:

			$arr = [];
for($i=0;$i<10000;$i++) {$arr []= $i;}
foreach($arr as $k => &$v) 
{
  $v += 1;
}
unset($v);
var_dump(memory_get_usage(false)); // int(1237592)
		

Через цикл for:

			$arr = [];
for($i=0;$i<10000;$i++) {$arr []= $i;}
$arrLength = count($arr);
for($i=0;$i<$arrLength;$i++) 
{
  $arr [$i]= $arr[$i] + 1;
 }
var_dump(memory_get_usage(false)); // int(917960)
		

При обходе массивов со смешанными ключами, все циклы плюс минус одинаковы по производительности.

Заключение

Мы рассмотрели несколько особенностей PHP. Язык постоянно развивается, оптимизируется и становится лучше. Если было полезно, ставьте классы. Всем добра!

Следите за новыми постами
Следите за новыми постами по любимым темам
854 открытий1К показов