Php размер массива

PHP: как сократить количество итераций в цикле и уменьшить время выполнения скрипта?

Так как count() и sizeof php используются в циклах очень часто, то следует изучить их досконально.

$array = array(  	"fruit" =>array(  		"apple",   		"bananas",  		"orange",  	),  	"vegetables" =>array(  		"potatoes",   		"tomatoes",  	),  );    echo "По умолчанию = ".sizeof($array); //По умолчанию = 2  echo "Рекурсивно = ".sizeof($array, 1); //Рекурсивно = 7   

Тестирование больших массивов в цикле (более 65000 элементов) показало, что sizeof() быстрее, чем count(), поэтому имеет смысл применять ее на практике.

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

$test = array( 1, 2, 3, 4 );  $sizeof_test = sizeof( $test );  for ( $it=0; $it < $sizeof_test; $it++ ) {   	echo $test[$it];  }    

Если протестировать цикл на 1000 однобайтных значениях, то время его прохождения с предустановленной переменной меньше в 250 раз.

Рассмотрим массив с нулевыми значениями:

$test2 = array( '', null, false, 0 );  var_dump( sizeof( $test2 ) ); //выведет int(4)  var_dump( sizeof( array_filter( $test2 ) ) ); //выведет int(0)

Как видно из примера, sizeof php (и count тоже), считают количество элементов, в том числе и нулевые, поэтому нужно это учитывать и (если необходимо) удалять пустые значения с помощью array_filter или другой пользовательской функции.

Добавим массив в $test2:

$test2 = array( '',null,false,0, array() ); //выведет int(0)  $test2 = array( '',null,false,0, array( null ) ); //выведет int(1)  var_dump( sizeof( array_filter( $test2 ) ) );

В данном примере array_filter удалил все пустые значения одномерного массива, но эта функция не имеет рекурсивного действия, поэтому значение «null» «внутреннего» массива осталось, а значит sizeof php его учитывает.

Давайте посмотрим что будет, если убрать array_filter?

$test2 = array('', null, false, 0, array() ); //выведет int(5)  $test2 = array('', null, false, 0, array( null ) ); //выведет int(5)  $t_rec = array('', null, false, 0, array() ); //выведет int(5)  $t_rec = array('', null, false, 0, array( null ) ); //выведет int(6)  var_dump( sizeof( $test2 ) );  var_dump( sizeof( $t_rec, 1 ) );    

Если пустые значения не были введены в код намеренно, то лучше от них избавиться с помощью функций-фильтров прежде, чем запускать цикл. Это уберет лишние итерации цикла и сократит время на выполнение процесса.

Как посчитать объект StdClass созданный из json_decode с помощью sizeof php?

$json = '{ "foo": "bar", "number": 10 , "car": "BMW" }';  $stdInstance = json_decode( $json );  var_dump( sizeof( ( array )$stdInstance ) ); //выведет int(3)  var_dump( sizeof( get_object_vars( $stdInstance ) ) ); //выведет int(3)

Sizeof() определяет количество элементов массива или объекта Countable. StdClass не является ни тем, ни другим. Чтобы получить объект в виде массива, используйте функцию get_object_vars. Первый вариант с (array) тоже работает, но все же второй вариант кажется более надежным.

Get_object_vars применяют для получения нестатических свойств объекта, что вполне подходит для нашего примера.

fb.ru

count()

The syntax of PHP count() function is,

count(var array_variable, mode)

array_variable

The array variable is required for which the length will be calculated. If the value of this first argument is not set or having an empty array, then count() will return 0. And, if the variable specified is not an array, then, count method will return 1. To check the state of this variable whether it is not set or it is empty, we can use PHP predefined function, isset() and empty(), respectively.


mode

This argument is optional and has value to specify the mode of operation to be used in calculating array length. There are various possible options to this argument. These are,

  • COUNT_NORMAL – By specifying this predefined constant for the optional second argument of count() function, it will return the length of an array in depth 0.
  • COUNT_RECURSIVE – This value will be used for recursive count operation, till it reaches depth n. This recursive call will be continued infinitely, till it reaches depth n or the program execution time limit is elapsed.

Note:

  • The alternative values can be passed as the value of this optional argument is, 0 and 1 to create the same effect on returning array length with COUNT_NORMAL and COUNT_RECURSIVE, respectively.
  • COUNT_NORMAL is the default option, whereas, if no second argument is specified while invoking count().

php_array_length

Example: PHP count()

The following program is shown as an example for count() function to calculate a number of elements available in a two-dimensional array. Since the input array of this program contains more than one dimensions, we can differentiate the output values return by using various mode of operations.


Let us use the same two-dimensional array we have taken as an example for PHP inheritance article. That is,

$toys = array(array("name"=>"Mechanical Cars","category"=>"pull back"),  array("name"=>"Jigsaw","category"=>"puzzles"),  array("name"=>"HiTech Cars","category"=>"remote"),  array("name"=>"Teddy Bears","category"=>"soft"),  array("name"=>"Baby pillow","category"=>"soft"),  array("name"=>"Chinese Checker","category"=>"puzzles"),  array("name"=>"Jumbo Helicopter","category"=>"remote"));

Now, we can invoke count() function by passing different possible mode option, as follows,

$array_length["normal_count"] = count($toys,COUNT_NORMAL);  //(OR)   $array_length["default_count"] = count($toys);  $array_length["recursive_count"] = count($toys,COUNT_RECURSIVE);  print "<pre>";  print_r($array_length);  print "</pre>";

In the above PHP code, the count() is invoked with normal, default and recursive mode, and the returned values are stored into an array named, $array_length, by specifying the appropriate name indices. The integer value returned as the length of the given input array will be same for the normal and default mode. This will be clear with the following output of this program.

Array  (   [normal_count] => 7   [default_count] => 7   [recursive_count] => 21  )

sizeof()


This function is an alias of PHP count() function as we said at the beginning of this article, and accepts the same set of arguments as like as count().

We can replace the count() with sizeof() in the above program to compare the results of both functions which will be same for both functions calls.

Alias functions in PHP are preserved for providing backward compatibility, though it is not good programming practice to use alias functions. Why because, we need to maintain our code with an upgraded version since there is no guarantee for the alias functions will exist for a longer time with the future API clean up.

Particularly, sizeof() function performs differently depends on the programming languages. For example, in C, this function is used to calculate the size of the data type to be represented in bytes. So, the developer who is new for PHP, migrating from C-like languages, will be confused with this function. So, it is preferable to use master functions instead of aliases.

↑ Back to Top

phppot.com


считаем в PHP количество элементов в массиве

От автора: семьдесят пять, шесть, семь…. Извините, я сейчас уже заканчиваю! Семьдесят…. Сколько? Тьфу, опять сбился! В общем, создал массив, «накопипастил» в него элементов, а теперь не могут понять, сколько их. А чего я мучаюсь? Ведь с помощью PHP количество элементов в массиве подсчитать очень даже легко.

А индекс зачем?

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

Решение этой проблемы кажется простым лишь на первый взгляд. Но если длина массива «зашкаливает» за сотни или даже тысячи, тогда у любого пойдет кругом голова. И метод «раз ромашка, а я четвертую нашел» явно вам не поможет. Как не помог и мне :) .

Чтобы доказать выше сказанное, создадим самый непредсказуемый массив в мире. Имеется в виду, что длина, ключ и значение каждого из элементов такой структуры задается случайным числом из определенного диапазона. Для этого мы использовали функцию rand():

Узнаем ширину массива

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

считаем в PHP количество элементов в массиве

Еще один метод, как подсчитать количество элементов в массиве PHP, заключается в применении функции var_dump(). Но с ее помощью можно узнать «численность» элементов визуально. Данная функция выводит всю информацию о передаваемой ей в качестве параметра переменной: тип, длину (если это array) и значение:

считаем в PHP количество элементов в массиве

Другие варианты

Существуют и другие варианты, чтобы посчитать количество элементов в массивах PHP. Например, метод «засланного казачка». Для этого мы инициализируем внутри цикла for новую переменную. При каждой итерации цикла к ее значению будет прибавляться 1(с помощью инкремента). Затем под телом цикла мы выводим значение «засланного казачка» и узнаем длину массива.


считаем в PHP количество элементов в массиве

Чтобы узнать в PHP количество уникальных элементов массива, можно мудрить очень долго. Мой вариант заключается в использовании функции array_unique(), которая очищает входящий массив от повторяющихся значений. Затем просто возвращаем длину уже обработанного массива с помощью функции count() (число уникальных элементов):

считаем в PHP количество элементов в массиве

Вот так легко подсчитывается количество элементов в массиве PHP. А я мучился! Но благодаря этому материалу я воскресил в своей памяти нужные мне знания и посвятил в эту «магическую науку» вас. Хотя на деле магия оказалась обычной ловкостью ума и PHP :) .

Php размер массива

webformyself.com

Схват атриб&.
жение HTML, иначе ничего не показывать php создать метод класса во время выполнения Как проверить, является ли сеанс PHP пустым? Разбивка страницы и использование в PHP как испо&#x.
#x435;тр ATTR_TIMEOUT для MySQL, когда сервер не может быть достигнут Как настроить $ docroot, чтобы дать мне путь, который я хочу Не проверять, не сделан ли пользователь Преобразование строки из 1s и 0s в двоичное значение, затем сжимать потом, PHP проблема понимания отношения отображения в доктрине 2 Ошибка дескриптора файловой системы PHP

ruphp.com

Criada para contar quantos níveis um array multidimensional possui.

function count_multiLevel($matrix, $_LEVEL = 0){
       
        /* Variáveis de recursão */
        $_COUNT = $_LEVEL;
       
        /* Verifica se o ARRAY foi instanciado */
        if (is_setVar($matrix)){
           
            /* Verifica se a variável é um ARRAY */
            if(is_array($matrix)){
               
                /* Loop de elementos da matriz*/
                foreach ($matrix as $elements => $value) {
                   
                    /* Auxiliar para verificação posterior */
                    $_AUX = $_COUNT;
                   
                    /* Verifiando os Nós */
                    if (is_array($matrix[$elements])) {
                        $_COUNT = count_multiLevel($matrix[$elements], $_LEVEL+1);   
                    }
                    /* Cereja do bolo */
                    if($_AUX > $_COUNT)
                        $_COUNT = $_AUX;
                }
                /* Retorn do resultado da operação */
                return $_COUNT;
               
            }else{
                /* Em casos que o valor passado não seja uma matriz/array */
                return -1;
            }
        }
    }

php.net

Создание массива

Массив можно создать с помощью функции array(), параметры которой и составляют массив. Параметры могут задаваться парами «ключ=>значение». Если при создании массива ключ не указывается, то индекс определяется положением элемента в массиве (начиная с 0). Например:

 $рост = array (174, 181, 166); //Массив с индексацией, начинающейся с нуля $цена = array ("помидоры" => 15, "огурцы" => 12); //Ассоциативный массив $данные = array (  "Иванов" => array ("рост" => 174, "вес" => 68),  "Петров" => array ("рост" => 181, "вес" => 90),  "Сидоров" => array ("рост" => 166, "вес" => 73)); //Двухмерный массив 

Массивы можно создать и другим способом — непосредственно. Например:

 $фрукты[] = "яблоко"; $фрукты[] = "груша"; $фрукты[] = "слива"; $цена["помидор"] = 15; $цена["огурец"] = 12; 

Индексы элементов неассоциативного массива можно не указывать. PHP автоматически вычислит их. Если же указать индексы таким образом:

 $фрукты[0] = "груша"; $фрукты[5] = "яблоко"; 

то в массиве будет два элемента, причем последний с индексом 5. Элементы 1 — 4 не инициализируются.

Можно создать массив с помощью функции array(), а затем добавить к нему новый элемент:

 $фрукты = array("яблоко","груша","слива"); ... $фрукты[] = "персик"; 

Подсчет количества элементов

Количество элементов в массиве можно определить с помощью функций count() или sizeof().

Пример 1

 <html> <head> <title>Размер массива</title> </head> <body> <?php $фрукты = array("яблоко", "груша", "слива", "персик"); echo "Размер массива $фрукты равен ".count($фрукты)."<br>"; echo "Последний элемент массива $фрукты - ".$фрукты[count($фрукты)-1]."<br>"; ?> </body> </html> 

Для доступа к последнему элементу надо вычесть 1 из размера массива, так как индексация массива начинается с нуля. Для вывода зарезервированного символа «$» перед знаком доллара стоит символ обратной косой черты «».

Частоту вхождения элементов в массив можно определить с помощью функции array_count_values(). Эта функция возвращает массив, в котором ключами являются элементы исследуемого массива, а значениями — частоты их вхождения в исследуемый массив.

Пример 2

 <html> <head> <title>Размер массива</title> </head> <body> <?php $фрукты = array("яблоко", "груша", "слива", "персик", "груша"); print_r (array_count_values($фрукты)); ?> </body> </html> 

Функция print_r() отображает ключи и значения массива, указанного в аргументе.

Просмотр массива в цикле

Для итерационного просмотра содержимого массива служит функция foreach. С ее помощью можно просмотреть и простой (проиндексированный числами) массив, и ассоциативный, и многомерный.

Пример 3

 <html> <head> <title>Просмотр массива</title> </head> <body> <?php $фрукты = array("яблоко", "груша", "слива", "персик", "груша"); foreach ($фрукты as $фрукт)  {  echo "$фрукт<br>";  } ?> </body> </html> 

Пример 4

 <html> <head> <title>Просмотр ассоциативного массива</title> </head> <body> <?php $цена = array ("помидоры" => 15, "огурцы" => 12); foreach ($цена as $овощи => $руб)  {  echo "$овощи стоят $руб руб.<br>";  } ?> </body> </html> 

Пример 5

 <html> <head> <title>Просмотр двухмерного массива</title> </head> <body> <?php $данные = array (  "Иванов" => array ("рост" => 174, "вес" => 68),  "Петров" => array ("рост" => 181, "вес" => 90),  "Сидоров" => array ("рост" => 166, "вес" => 73)); foreach ($данные as $фамилия => $данные1)  {  echo "<br>$фамилия:<br>";  foreach ($данные1 as $параметр => $pp)  {  echo "$параметр = $pp<br>";  }  } ?> </body> </html> 

print_r — Вывод всех эллементов массива. Удобная функция для отладки.

 <?php $a = array ('a' => 'apple', 'b' => 'banana', 'c' => array ('x','y','z')); print_r ($a); ?> 

Управление массивами

Для управление массивами в PHP существует целый ряд специализированных функций. Наиболее употребимыми являются:

  • + — объединение массивов. $ar=$ar1+$ar2
  • == — сравнение массивов. Оператор вернёт true если все пары (ключ:значение) из первого массива совпадут с содержанием второго массива.
  • === — идентичность массивов. Оператор вернёт true если:
    • содержат одно и тоже количество элементов;
    • ключи и значения элементов одинаковы в обоих массивах;
    • порядок элементов;
    • абсолютное совпадение по типам данных у значений элементов.

    Если ключи одного массива типа string, а второго integer и все значения совпадают, то оператор вернёт true. Если же вместо integer воспользоваться float, то результат будет false.

  • array_merge() — объединение (слияние) массивов. Последующее значение элемента перекрывает предыдущее, если ключи строковые и одинаковые.

    Пример 6

     <html> <head> <title>Слияние массивов</title> </head> <body> <?php $мнение1 = array (1, "Катя" => "умная", 2, "Женя" => "красивая", 3); $мнение2 = array (4, "Женя" => "глупая", 5, "Катя" => "милая"); echo "1 + 2:<br>"; print_r (array_merge ($мнение1, $мнение2)); echo "<br>2 + 1:<br>"; print_r (array_merge ($мнение2, $мнение1)); ?> </body> </html> 

  • array_shift() — удаление первого элемента.

    Пример 7

     <html> <head> <title>Удаление первого элемента массива</title> </head> <body> <?php $фрукты = array("яблоко", "груша", "слива", "персик"); echo "В вазе лежали:"; foreach ($фрукты as $tmp)  {  echo " $tmp";  } echo "<br>Первым съели ".array_shift($фрукты)."<br>"; echo "В вазе остались:"; foreach ($фрукты as $tmp)  {  echo " $tmp";  } ?> </body> </html> 

  • array_pop() — удаление последнего элемента.

    Пример 8

     <html> <head> <title>Удаление последнего элемента массива</title> </head> <body> <?php $фрукты = array("яблоко", "груша", "слива", "персик"); echo "В вазе лежали:"; foreach ($фрукты as $tmp)  {  echo " $tmp";  } echo "<br>Съели ".array_pop($фрукты)."<br>"; echo "В вазе остались:"; foreach ($фрукты as $tmp)  {  echo " $tmp";  } ?> </body> </html> 

  • array_push() — добавление элементов в конец массива.

    Пример 9

     <html> <head> <title>Добавление элементов в конец массива</title> </head> <body> <?php $a = array(1, 2, 3); echo "Массив $a:"; foreach ($a as $tmp) echo " $tmp"; $k = array_push($a, 4, 5); echo "<br>Массив $a после добавления:"; foreach ($a as $tmp) echo " $tmp"; echo "<br>Его длина = $k"; ?> </body> </html> 

    Имейте в виду, что если в качестве второго аргумента функции array_push() передать массив, то этот массив будет добавлен как элемент, т.е. будет создан двумерный массив.

  • array_unshift() — добавление элементов в начало массива.

    Пример 10

     <html> <head> <title>Добавление элементов в начало массива</title> </head> <body> <?php $a = array(1, 2, 3); echo "Массив $a:"; foreach ($a as $tmp) echo " $tmp"; $k = array_unshift($a, 4, 5); echo "<br>Массив $a после добавления:"; foreach ($a as $tmp) echo " $tmp"; echo "<br>Его длина = $k"; ?> </body> </html> 

  • array_slice() — выделение фрагмента массива.

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

    Пример 11

     <html> <head> <title>Выделение фрагмента массива</title> </head> <body> <?php $a = array("a", "b", "c", "d", "e", "f"); echo "$a:"; foreach ($a as $t) echo " $t"; $b = array_slice($a, 2); echo "<br>array_slice($a, 2):"; foreach ($b as $t) echo " $t"; $b = array_slice($a, 2, -1); echo "<br>array_slice($a, 2, -1):"; foreach ($b as $t) echo " $t"; $b = array_slice($a, -2, 1); echo "<br>array_slice($a, -2, 1):"; foreach ($b as $t) echo " $t"; $b = array_slice($a, 0, 3); echo "<br>array_slice($a, 0, 3):"; foreach ($b as $t) echo " $t"; ?> </body> </html> 

Сортировки

Сортировать можно как простые, так и ассоциативные массивы. Для сортировки массивов в PHP существуют определенные функции:

  • sort() — сортирует массив в алфавитном порядке, если хотя бы один из его элементов является строкой, и в числовом порядке, если все его элементы — числа.
  • rsort() — работает как sort( ), но в обратном порядке.
  • asort() — сортирует ассоциативный массив; работает как sort( ), но сохраняет имена элементов.
  • arsort() — работает как asort( ), но в обратном порядке.
  • ksort() — сортирует ассоциативный массив по именам элементов.
  • krsort() — работает как ksort( ), но в обратном порядке.

    Пример 12

     <html> <head> <title>Сортировка ассоциативного массива</title> </head> <body> <?php $a = array("первый" => 6, "второй" => 2, "третий" => 1); echo "$a:<br>" asort ($a); echo "asort ($a):<br>" foreach ($a as $k => $t) echo " $k = $t<br>"; ksort ($a); echo "ksort ($a):<br>" foreach ($a as $k => $t) echo "$k = $t<br>"; ?> </body> </html> 

Поиск элемента

Для проверки наличия элемента в массиве существуют функции:

  • in_array() — если элемент найден, возвращает true, иначе — false.
  • array_search() — если элемент найден, возвращает его ключ, иначе — false.

Пример 13

 <html> <head> <title>Поиск элемента в массиве</title> </head> <body> <?php $a = array("первый" => 6, "второй" => 2, "третий" => 1); if (in_array (2, $a)) echo "2 нашли!<br>"; echo "ключ найденного элемента - ".array_search(2, $a); ?> </body> </html> 

htmlweb.ru

Краткое изложение

Для тех, кто не хочет разбираться со всем этим, вот краткий обзор вовлечённых компонент.

 | 64 bit | 32 bit --------------------------------------------------- zval | 24 bytes | 16 bytes + cyclic GC info | 8 bytes | 4 bytes + allocation header | 16 bytes | 8 bytes =================================================== zval (value) total | 48 bytes | 28 bytes =================================================== bucket | 72 bytes | 36 bytes + allocation header | 16 bytes | 8 bytes + pointer | 8 bytes | 4 bytes =================================================== bucket (array element) total | 96 bytes | 48 bytes =================================================== total total | 144 bytes | 76 bytes 

Приведённые выше числа могут меняться в зависимости от вашей операционной системы, компилятора и опций компилирования. Например, если вы компилируете PHP с debug или thread-safety, то получите различные значения. Но я думаю, что приведённые размеры вы увидите на рядовой сборке PHP 5.3 на 64 разрядном Линуксе.

Если умножить эти 144 байта на наши 100 000 чисел, то получится 14 400 000 байт, что составляет 13,73 Мб. Довольно близко к реальному результату, остальное — это в основном указатели для неинициализированных блоков(buckets), но я расскажу об этом позже.

Теперь, если вы хотите иметь более детальный анализ значений, которые указаны выше, то читайте дальше :).

Объединение zvalue_value

Сначала давайте обратим внимание на то, как PHP хранит значения. Как вы знаете PHP является слаботипизированным языком, поэтому ему нужен способ быстрого переключения между значениями. PHP использует объединение (union), который определён следующим образом zend.h#307 (комментарии мои):

typedef union _zvalue_value {  long lval; // Для целых и булей  double dval; // Для чисел с плавающей точкой  struct { // Для строк  char *val; // состоит из строки как таковой  int len; // и её длины  } str;  HashTable *ht; // Для массивов (хеш-таблицы)  zend_object_value obj; // Для объектов } zvalue_value; 

Если вы не знаете C, то это не проблема — код очень прост: объединение означает, что значение может выступать в роли различных типов. Например, если вы используете zvalue_value->lval, то значение будет интерпретировано как целое число. С другой стороны, если используете zvalue_value->ht, то значение будет интерпретировано как указатель на хеш-таблицу (aka массив).

Не будем на этом задерживаться. Важным для нас только то, что размер объединения равен размеру его крупнейшего компонента. Самый большой компонент — это строка (на самом деле структура zend_object_value имеет тоже размер, но этот момент я опущу для простоты). Структура состоит из указателя (8 байт) и целого числа (4 байта). Итого 12 байт. Благодаря выравниванию памяти (структуры в 12 байт — это не круто, потому что они не являются произведением 64 бит/8 байт) конечный размер структуры будет 16 байт и, соответственно, всего объединения в целом.

Итак, теперь мы знаем, что нам нужно не 8 байт для каждого значения, а 16 — за счёт динамической типизации PHP. Умножив на 100 000 получим 1 600 000 байт, т.е. 1,53 Мб. Но реальный объём 13,97 Мб, поэтому мы не достигли пока цели.

Структура zval

Вполне логично, что union хранит только значение, а PHP, очевидно, нужно хранить так же его тип и некоторую информацию для сборки мусора. Структура, которая содержит эту информацию, называется zval и вы, наверное, уже слышали о ней. Для получения дополнительной информации о том, зачем это PHP, я рекомендую прочитать статью Sara Golemon. Как бы то ни было эта структура определяется следующим образом:

struct _zval_struct {  zvalue_value value; // Значение  zend_uint refcount__gc; // Количество ссылок на значение (для GC)  zend_uchar type; // Тип  zend_uchar is_ref__gc; // Является ли это значение ссылкой (&) }; 

Размер структуры определяется суммой размеров всех её компонент: zvalue_value — 16 байт (расчёт выше), zend_uint — 4 байта, zend_uchar — 1 байт каждый. В общей сложности 22 байта. Опять же из-за выравнивания памяти реальный размер будет 24 байта.

Так что, если мы храним 100 000 значений по 24 байта, то это будет 2 400 000 байт или 2,29 Мб. Разрыв сокращается, но реальное значение ещё более чем в шесть раз больше.

Сборщик мусора для циклических ссылок (PHP 5.3)

PHP 5.3 представила новый сборщик мусора для циклических ссылок. Для этого PHP хранит некоторую дополнительную информацию. Я не хочу здесь объяснять как это работает, вы можете почерпнуть необходимую информацию из мануала. Для наших расчётов размеров важно, что каждый zval оборачивается zval_gc_info:

typedef struct _zval_gc_info {  zval z;  union {  gc_root_buffer *buffered;  struct _zval_gc_info *next;  } u; } zval_gc_info; 

Как вы видите Zend только добавляет объединение, которое содержит два указателя. Как вы помните размер объединения определяется самым большим компонентом. Оба компонента — это указатели по 8 байт. Соответственно, размер объединения тоже 8 байт.

Если мы добавим полученные выше 24 байта, то мы получим 32 байта. Умножаем это на 100 000 и полуаем 3,05 Мб.

Менеджер памяти ZEND

Си, в отличие от PHP, не управляет памятью за вас. Вы должны самостоятельно следить за распределением памяти. Для этого PHP использует оптимизированный для своих нужд собственный менеджер памяти: The Zend Memory Manager. MM Zend основан на malloc от Doug Lea и всяческих дополнительных специфических для PHP особенностей и оптимизаций (таких как ограничение памяти, очистка после каждого запроса и тому подобное).

Что важного для нас в этом так это то, что MM добавляет заголовок для каждого выделения памяти, которое проходит через него. И определяется следующим образом:

typedef struct _zend_mm_block {  zend_mm_block_info info; #if ZEND_DEBUG  unsigned int magic; # ifdef ZTS  THREAD_T thread_id; # endif  zend_mm_debug_info debug; #elif ZEND_MM_HEAP_PROTECTION  zend_mm_debug_info debug; #endif } zend_mm_block;  typedef struct _zend_mm_block_info { #if ZEND_MM_COOKIES  size_t _cookie; #endif  size_t _size; // выделенный размер  size_t _prev; // предыдущий блок (не уверен что это) } zend_mm_block_info; 

Как вы видите определение включает в себя множество проверок опций компилирования. Если хотя бы одна из эти опций будет включена заголовок для выделенной памяти будет больше, и будет он самым большим, если вы скомпилируете PHP с защитой кучи(heap protection), защитой потоков(thread safety), отладкой и MM cookies.

Для примера мы будем считать, что все эти опции отключены. В этом случае остается только две компоненты size_t _size и _prev. size_t занимет 8 байт (64 бита), так что заголовок имеет размер в 16 байт — и этот заголовок добавляется для каждого выделения памяти.

Так что мы должны скорректировать размер zval снова. На самом деле это будет не 32 байта, а 48, из-за этого заголовка. Умножаем на наши 100 000 элементов и получаем 4,58 Мб. Реальный размер 13,97 Мб, так что мы уже покрыли примерно треть.

Блоки

До сих пор мы рассматривали значения по отдельности. Но структура массива в PHP забирает много места. На самом деле термин «Массив» здесь подобран неудачно. В PHP массив — это на самом деле хеш таблицы/словари. Так как же хэш-таблицы работают? В основном для каждого ключа генерируется хэш, и этот хэш используется для перехода в «реальный» C массив. Хэши могут конфликтовать, все элементы, которые имеют одинаковые хэши хранятся в связанном списке. При обращении к элементу PHP сначала вычисляет хэш, ищет нужный блок(bucket), и проходит по списку в поисках точного совпадения элемент за элементом. Блок определяется следующим образом (zend_hash.h#54):

typedef struct bucket {  ulong h; // Хэш (или ключ для целочисленных ключей)  uint nKeyLength; // Длина ключа (для строковых ключей)  void *pData; // Данные  void *pDataPtr; // ??? Что это ???  struct bucket *pListNext; // PHP массивы упорядочены. Указатель на следующий элемент  struct bucket *pListLast; // и на предыдущий  struct bucket *pNext; // Следущий элемент в этом (двойном) связном списке  struct bucket *pLast; // Предыдущий элемент в этом (двойном) связном списке  const char *arKey; // Ключ (для строковых ключей) } Bucket; 

Как вы видите необходимо хранить «груз» данных, чтобы получить абстрактный массив данных вроде такого, какой используется в PHP (массивы PHP являются массивами, словарями и связными списками в одно и тоже время, что, конечно, требует много данных). Размер отдельных компонент это: 8 байт для типа ulong, 4 байта для uint и 7 раз по 8 байт для указателей. В результате получается 68. Добавляем выравнивание и получаем 72 байта.

Для блоков как и для zval должны быть добавлены заголовки в 16 байт, что даёт нам 88 байт. Так же нам нужно хранить указатели на эти блоки в «настоящем» массиве C (Bucket **arBuckets;), я упомнил об этом выше, что добавляет ещё 8 байт на элемент. Так что в целом каждый блок расходует в 96 байтах памяти.

И так, если нам нужен блок для каждого значения — это будет 96 байт для bucket и 48 байт для zval, что составляет 144 байта в общей сложности. Для 100 000 элементов это будет 14 400 000 байт или 13,73 Мб.

Загадка решена.

Подождите, осталось ещё 0,24 Мб!

Эти последние 0,24 Мб обусловлены неинициализированными блоками: размер «реального» массива C в идеале должен быть равен количеству элементов. Таким образом мы получаем наименьшее количество коллизий (если вы не хотите тратить много памяти). Но PHP, очевидно, не может перераспределять весь массив каждый раз когда добавляется новый элемент — это было бы ооочень медленно. Вместо этого PHP всегда удваивает размер внутреннего массива блоков, если оно попадает в предел. Таким образом, размер массива всегда является степенью двойки.

В нашем случае это 2 ^ 17 = 131 072. Но нам нужно только 100 000 из этих блоков, поэтому мы оставляем 31 072 блока неиспользованными. Те, память под эти блоки выделена не будет (поэтому нам не надо тратить полные 96 байт), но память под указатель(который хранится в внутреннем массиве блоков) на блок должна быть использована. Поэтому мы дополнительно используем 8 байт (на указатель) * 31 072 элементов. Это 248 576 байт или 0,23 Мб. Что соответствует недостающей памяти. (Конечно, отсутствуют ещё несколько байт, но я не хочу полностью покрыть всё. Это такие вещи как сама структура хэш-таблицы, переменные и т.д.)

Загадка действительно решена.

О чём нам это говорит?

PHP не C. И это говорит нам только об этом. Вы не можете ожидать от супер-динамического языка PHP эффективного использования памяти как в C. Не можете и всё.

Но, если вы хотите сохранить память вы можете рассмотреть использование SplFixedArray для больших статических массивов.

Посмотрим на модифицированный скрипт:

<?php $startMemory = memory_get_usage(); $array = new SplFixedArray(100000); for ($i = 0; $i < 100000; ++$i) {  $array[$i] = $i; } echo memory_get_usage() - $startMemory, ' bytes'; 

В основном он делает то же самое, но, если вы его запустите, то вы заметите, что он использует «всего лишь» 5 600 640 байт. Что составляет 56 байт на элемент, а это намного меньше, чем 144 байта на элемент обычного массива. Это происходит потому, что фиксированный массив не нуждается в bucket структуре: так что требуется только один zval (48 байт) и один указатель (8 байт) для каждого элемента, что даст нам наблюдаемые 56 байт.

P.S. Все замечания по поводу перевода прошу писать в ЛС, а я постараюсь оперативно их исправить.

habr.com


You May Also Like

About the Author: admind

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.

Adblock
detector