Oc-windows.ru

IT Новости из мира ПК
1 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

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

Насколько большие массивы (и значения) в PHP? (Подсказка: ОЧЕНЬ БОЛЬШИЕ)

В этой статье я хочу исследовать расход памяти у массивов (и значений в целом) в PHP используя следующий скрипт в качестве примера, который создаёт 100 000 уникальных целочисленных элементов массива и в конце измеряет количество использованной памяти.

Это перевод (для таких как я, которые этого часто не замечают).

В начале я хочу поблагодарить Johannes и Tyrael за их помощь в поисках укромных мест расхода памяти.

Как вы думаете сколько получится? Если целое число это 8 байт (на 64 архитектурах и используя тип long) и есть 100 000 целых чисел, то, очевидно, потребуется 800 000 байт. Это около 0,76 Мб.

Теперь попробуйте запустить код. Это можно сделать on-line. В результате получится 14 649 024 байт. Да, вы не ослышались, это 13,97 Мб — в 18 раз больше, чем мы прикинули.

Итак, откуда появилось это 18 кратное увеличение?

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

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

Приведённые выше числа могут меняться в зависимости от вашей операционной системы, компилятора и опций компилирования. Например, если вы компилируете 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 (комментарии мои):

Если вы не знаете 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. Как бы то ни было эта структура определяется следующим образом:

Размер структуры определяется суммой размеров всех её компонент: 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:

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

Читать еще:  Почему когда устанавливаю игру пишет ошибку

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

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

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

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

Как вы видите определение включает в себя множество проверок опций компилирования. Если хотя бы одна из эти опций будет включена заголовок для выделенной памяти будет больше, и будет он самым большим, если вы скомпилируете 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):

Как вы видите необходимо хранить «груз» данных, чтобы получить абстрактный массив данных вроде такого, какой используется в 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. Не можете и всё.

Читать еще:  Ошибка d3dx11 43 dll что делать

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

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

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

Использование массивов

Основное назначение массивов в PHP — организация групп связанных значений. Каждый элемент массива имеет индекс (ключ) и значение. Индекс элемента массива указывается в квадратных скобках после имени массива. Для того, чтобы обратиться к пятому элементу массива $array1, надо написать:

Помните, что по умолчанию массив начинается не с первого элемента, а с нулевого.

Индекс может быть как числом, так и текстовой строкой. Массив со строковыми индексами называют ассоциативным, а сами индексы — именами элементов. Например, цены на товары хранятся в ассоциативном массиве $prices, индексами которого являются наименования товаров. Чтобы получить значение цены на конкретный товар, надо написать:

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

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

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

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

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

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

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

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

Пример 1

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

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

Пример 2

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

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

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

Пример 3


Пример 4


Пример 5


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

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

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

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

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

    Пример 6


    Пример 7


    Пример 8


    Пример 9

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

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

    Пример 10

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

    Пример 11


    Сортировки

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

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

    Пример 12


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

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

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

    count

    (PHP 4, PHP 5, PHP 7)

    count — Подсчитывает количество элементов массива или чего-либо в объекте

    Описание

    Подсчитывает количество элементов массива или чего-то в объекте.

    Для объектов, если у вас включена поддержка SPL, вы можете перехватить count() , реализуя интерфейс Countable. Этот интерфейс имеет ровно один метод, Countable::count() , который возвращает значение функции count() .

    Смотрите раздел Массивы в этом руководстве для более детального представления о реализации и использовании массивов в PHP.

    Список параметров

    Массив или объект, реализующий Countable.

    Если необязательный параметр mode установлен в COUNT_RECURSIVE (или 1), count() будет рекурсивно подсчитывать количество элементов массива. Это особенно полезно для подсчёта всех элементов многомерных массивов.

    count() умеет определять рекурсию для избежания бесконечного цикла, но при каждом обнаружении выводит ошибку уровня E_WARNING (в случае, если массив содержит себя более одного раза) и возвращает большее количество, чем могло бы ожидаться.

    Возвращаемые значения

    Возвращает количество элементов в array_or_countable . Если параметр не является массивом или объектом, реализующим интерфейс Countable, будет возвращена 1. За одним исключением: если array_or_countable — NULL , то будет возвращён .

    Примеры

    Пример #1 Пример использования count()

    [ 0 ] = 1 ;
    $a [ 1 ] = 3 ;
    $a [ 2 ] = 5 ;
    var_dump ( count ( $a ));

    $b [ 0 ] = 7 ;
    $b [ 5 ] = 9 ;
    $b [ 10 ] = 11 ;
    var_dump ( count ( $b ));

    var_dump ( count ( null ));

    var_dump ( count ( false ));
    ?>

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

    Пример #2 Пример рекурсивного использования count()

    = array( ‘fruits’ => array( ‘orange’ , ‘banana’ , ‘apple’ ),
    ‘veggie’ => array( ‘carrot’ , ‘collard’ , ‘pea’ ));

    // рекурсивный подсчет
    echo count ( $food , COUNT_RECURSIVE ); // выводит 8

    // обычный подсчет
    echo count ( $food ); // выводит 2

    Список изменений

    ВерсияОписание
    7.2.0count() теперь будет выдавать предупреждение о некорректных исчисляемых типов, переданных в параметр array_or_countable .

    Смотрите также

    • is_array() — Определяет, является ли переменная массивом
    • isset() — Определяет, была ли установлена переменная значением, отличным от NULL
    • empty() — Проверяет, пуста ли переменная
    • strlen() — Возвращает длину строки
    • is_countable() — Проверить, что содержимое переменной является счетным значением

    длина подмассива многомерного массива

    Подскажите пожалуйста, как можно узнать длину подмассива многомерного массива?
    Т.е. у меня есть массив
    arr[0][1]
    arr[1][1]
    arr[0][2]
    arr[1][2]
    arr[2][2]
    arr[3][2]
    arr[0][3]

    а мне нужно узнать длину массива arr[][2] ?

    PS: count(arr[][2]) выдает ошибку, а count(arr[2]) выдает количество элементов arr[2][].

    02.04.2010, 10:10

    Выбор массива из многомерного массива по ключу
    $array = array( array( ‘id’=>’1’, ‘value’=>’Значение 1’, ), array( .

    Генерация многомерного массива
    Задача сгенерировать многомерный массив, вот такого вида: Array ( => Array (.

    Сортировка многомерного массива.
    Сортирую массив, только теперь мне не понятно как результаты сортировки вывести в браузер.

    Перебор Многомерного массива
    Уважаемые помогите перебрать многомерный массив if($qrand1)< .

    02.04.2010, 10:362

    arr[][2]
    такого массива (либо элемента массива) не может быть

    я так понимаю тебе нужно узнать кол-во элементов с индексом [2] во всех массивах arr[0], arr[1], arr[2] и т.д. ? или нет?

    02.04.2010, 10:39302.04.2010, 11:19403.04.2010, 08:15526.02.2016, 12:286

    Тема хоть и устарела, но все-равно оставлю свое решение для тех, кто сейчас сталкивается с этим вопросом.

    count($array, COUNT_RECURSIVE) — count($array))/count($array)

    26.02.2016, 12:32726.02.2016, 18:068

    Kerry_Jr, А вообще, если исходить, что нулевой элемент как минимум всегда есть в любом подмассиве, то можно обычным count все спокойно узнать. Например у нас есть пятимерный массив с разной длиной подмассивов — $array[k1][k2][k3][k4][k5]. Получается —
    count($array) — длина k1
    count($array[0]) — длина k2
    count($array[0][0]) — длина k3
    count($array[0][0][0]) — длина k4
    count($array[0][0][0][0]) — длина k5

    Если в чем-то не прав, то поправь меня.

    26.02.2016, 18:12926.02.2016, 19:1110
    26.02.2016, 19:11
    26.02.2016, 19:11

    Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь.

    Сортировка многомерного массива
    Здравствуйте, есть массив, который выглядит так: $arr= 1; $arr= 24567; $arr= 35679; // $arr=.

    сотировка многомерного массива
    есть массив $arResult — содержащий товары. выглядит так => Array ( =>.

    сортировка многомерного массива
    подскажите пожалуйста как отсортировать многомерный массив по одному из критериев что бы все.

    Парсинг многомерного массива
    Здравствуйте. Не подскажите как обработать многомерный массив. Имеем: stdClass Object ( .

    Ссылка на основную публикацию
    Adblock
    detector
    ×
    ×