PHP ноль и копирование при записи
Предположим, я хочу иметь две переменные и они обе равны null
, (Более реалистично, я имею в виду массив, который содержит большое количество null
с, но сценарий "двух переменных" достаточно для вопроса.) Очевидно, я могу сделать это более чем одним способом. Я могу сделать это (метод 1):
$a = null;
$b = $a;
Насколько я понимаю, результатом этого является то, что есть один zval, на который указывают две записи в таблице символов: 'a'
а также 'b'
, Но в качестве альтернативы можно сделать это (метод 2):
$a = null;
$b = null;
Наивно можно ожидать, что это должно привести к двум различным zval, на каждый из которых указывает одна запись в таблице символов.
Из этого следует, что если вы хотите иметь большой массив, и многие элементы массива будут null
более эффективно (с точки зрения использования zval/ памяти) создать $master_null
переменная со значением null
, а затем напишите null
элементы массива путем назначения с помощью $master_null
?
3 ответа
Рассмотрим этот скрипт:
$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 МБ. Основываясь на этой информации, вы можете предположить, что, хотя использование памяти является вашей задачей, на самом деле лучше использовать переменную "master null". Однако вам все еще нужно иметь все элементы в массиве, и каждая запись в 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 контейнеры имеют логику подсчета ссылок. Таким образом, у меня сложилось впечатление, что если вы используете ссылки, например & & 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 (e.g. 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
это то же самое.