PHP null и copy-on-write

Предположим, что у меня есть две переменные и они равны null . (Более реалистично, я думаю о массиве, который содержит большое количество null значений s, но сценарий «двух переменных» достаточно для вопроса.) Очевидно, что я могу сделать это более чем одним способом. Я могу это сделать (метод 1):

 $a = null; $b = $a; 

По моему мнению, результатом этого является то, что есть один zval, на который указывают две записи в таблице символов: 'a' и 'b' . Но в качестве альтернативы можно сделать это (метод 2):

 $a = null; $b = null; 

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

Из этого следует, что если вы хотите иметь большой массив, и многие элементы массива будут null , более эффективно (с точки зрения использования zval / memory) создавать переменную $master_null со значением null , а затем напишите null элементы массива, назначив с помощью $master_null ?

Рассмотрим этот скрипт:

 $arr = array(); for ($i = 0; $i < 100000; $i++) $arr[] = null; echo memory_get_usage() . "\n"; 

который на моей машине выдает: 21687696, то есть 21 МБ используемой памяти. С другой стороны, используя это:

 $master_null = null; $arr = array(); for ($i = 0; $i < 100000; $i++) $arr[] = $master_null; echo memory_get_usage() . "\n"; 

выходов: 13686832, что составляет 13 МБ. Основываясь на этой информации, вы можете предположить, что, поскольку использование памяти является вашей проблемой, на самом деле лучше использовать переменную «главный нуль». Однако вам все равно нужно иметь все элементы в массиве, и каждая запись в HashTable (внутреннее представление массивов) также занимает некоторую память.

Если вы хотите углубиться в zvals и ссылки, я предлагаю использовать функцию debug_zval_dump . Используя его, вы можете видеть, какие переменные имеют один и тот же zval:

 $a = $b = $c = $d = "abc"; debug_zval_dump($a); $x = $y = $z = $w = null; debug_zval_dump($x); $q = null; debug_zval_dump($q); 

который выводит:

 string(3) "abc" refcount(5) NULL refcount(5) NULL refcount(2) 

И это означает, что хотя переменные $ x и $ q равны NULL, они не совпадают с zval. Но $ x и $ y имеют один и тот же zval, потому что они назначены друг другу. Я считаю, что вы знаете функцию debug_zval_dump , но если нет, убедитесь, что вы внимательно прочитали объяснение refcount по адресу http://php.net/manual/en/function.debug-zval-dump.php .

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

Из того, что я понимаю, PHP zval-контейнеры имеют логику подсчета ссылок. Таким образом, каково мое впечатление, если вы используете ссылки ie & $ master_null для инициализации всех значений NULL, я думаю, что это экономит ваше пространство, т.е. все NULL-элементы точек массива относятся к той же ссылке на контейнер zval.

Вот пример:

 # php -r '$var1 = NULL; $var2 = $var1; $var3 = $var1; debug_zval_dump(&$var1); debug_zval_dump(&$var2); debug_zval_dump(&$var3);' &NULL refcount(2) &NULL refcount(2) &NULL refcount(2) 

Вы можете больше узнать о базе ссылок ссылок PHP здесь:

что-то стоит прочитать по этой ссылке:

 PHP is smart enough not to copy the actual variable container when it is not necessary. Variable containers get destroyed when the "refcount" reaches zero. The "refcount" gets decreased by one when any symbol linked to the variable container leaves the scope (eg when the function ends) or when unset() is called on a symbol. 

Таким образом, каждый раз, когда вы используете & $ master_null, увеличивается «refcount», и когда «refcount» достигает нуля, контейнер переменной удаляется из памяти.


Из приведенного выше примера примера здесь используется использование памяти:

 # php -r '$arr = array(); for ($i = 0; $i < 100000; $i++) $arr[] = null; echo memory_get_usage() . "\n";' 11248372 # php -r '$master_null = null; $arr = array(); for ($i = 0; $i < 100000; $i++) $arr[] = &$master_null; echo memory_get_usage() . "\n";' 6848488 # php -r '$master_null = null; $arr = array(); for ($i = 0; $i < 100000; $i++) $arr[] = $master_null; echo memory_get_usage() . "\n";' 6848468 

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