Подтвердить что ты не робот

PHP и миллион детей

Представьте, что у вас есть следующий массив целых чисел:

array(1, 2, 1, 0, 0, 1, 2, 4, 3, 2, [...] );

Целые числа идут до одного миллиона записей; только вместо жесткого кодирования они были предварительно сгенерированы и сохранены в форматированном файле JSON (размером приблизительно 2 МБ). Порядок этих целых чисел имеет значение, я не могу случайным образом генерировать его каждый раз, потому что он должен быть последовательным и всегда иметь одинаковые значения в тех же индексах.

Если этот файл снова читается на PHP (например, используя file_get_contents + json_decode), он принимает от 700 до 900 мс только для того, чтобы вернуть массив - "Хорошо", - подумал я, "вероятно, разумно, поскольку json_decode должен разбирать около 2 миллионов символов, пусть кэширует его". APC кэширует его в записи, которая занимает около 68 МБ, возможно, нормальная, zvals большие. Извлечение, однако, этот массив обратно с APC также занимает около 600 мс, что слишком сильно в моих глазах.

Изменить: APC выполняет сериализацию /unserialize для хранения и извлечения контента, который с миллионом массива элементов является длительным и тяжелым процессом.

Итак, вопросы:

  • Должен ли я ожидать эту задержку, если я намереваюсь загрузить миллионный массив записей, независимо от хранилища данных или метода, в PHP? Насколько я понимаю, APC хранит сам zval, поэтому теоретически получение его из APC должно быть таким же быстрым, как возможно (без синтаксического анализа, без преобразования и без доступа к диску)

  • Почему APC настолько медленна для чего-то настолько простого?

  • Есть ли эффективный способ загрузить миллион записей массива, полностью в памяти с помощью PHP? предполагая, что использование ОЗУ не является проблемой.

  • Если бы мне приходилось обращаться к только срезам этого массива на основе индексов (например, загружать кусок из индекса 15 в индекс 76) и никогда не иметь весь массив в памяти (да, я понимаю, что это разумный способ делать это, но я хотел знать все стороны), что было бы самой эффективной системой хранения данных для полного массива? Очевидно, что не RDBM; Я думаю redis, но я был бы рад услышать другие идеи.

4b9b3361

Ответ 1

Скажем, что целые числа равны 0-15. Затем вы можете сохранить 2 байта:

<?php
$data = '';
for ($i = 0; $i < 500000; ++$i)
  $data .= chr(mt_rand(0, 255));

echo serialize($data);

Для запуска: php ints.php > ints.ser

Теперь у вас есть файл с 500000 байтовой строкой, содержащей 1 000 000 случайных чисел от 0 до 15.

Для загрузки:

<?php
$data = unserialize(file_get_contents('ints.ser'));

function get_data_at($data, $i)
{
  $data = ord($data[$i >> 1]);

  return ($i & 1) ? $data & 0xf : $data >> 4;
}

for ($i = 0; $i < 1000; ++$i)
  echo get_data_at($data, $i), "\n";

Время загрузки на моей машине составляет около 0,002 секунды.

Конечно, это может быть не напрямую применимо к вашей ситуации, но это будет намного быстрее, чем раздутый массив PHP из миллиона записей. Честно говоря, наличие массива, большого в PHP, никогда не является правильным решением.

Я не говорю, что это правильное решение, но оно, безусловно, работоспособно, если оно соответствует вашим параметрам.

Обратите внимание, что если ваш массив имел целые числа в диапазоне 0-255, вы могли бы избавиться от упаковки и просто получить доступ к данным как ord($data[$i]). В этом случае ваша строка будет длиной 1 Мбайт.

Наконец, согласно документации file_get_contents(), php будет хранить карту памяти. Если это так, ваша лучшая производительность будет заключаться в том, чтобы сбрасывать необработанные байты в файл и использовать его так:

$ints = file_get_contents('ints.raw');
echo ord($ints[25]);

Это предполагает, что ints.raw составляет ровно миллион байтов.

Ответ 2

APC хранит данные, сериализованные, поэтому он должен быть неэтериализован, поскольку он загружается из APC. Это где ваши накладные расходы.

Наиболее эффективным способом загрузки является запись в файл как PHP и include(), но вы никогда не будете иметь какой-либо уровень эффективности с массивом, содержащим миллион элементов... он принимает огромное количество памяти, и для загрузки требуется время. Вот почему были изобретены базы данных, поэтому в чем проблема с базой данных?

ИЗМЕНИТЬ

Если вы хотите ускорить сериализацию/десериализацию, посмотрите igbinary

Ответ 3

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

Вы когда-нибудь читали о псевдослучайных числах? Там эта маленькая вещь называется семенем, которая решает эту проблему.

Также сравнивайте свои варианты и формулы. Вы приурочили файл_get_contents к json_decode? Между запасами и ресурсами доступа существует компромисс. Например. если ваши номера равны 0..9 (или 0..255), тогда их может быть проще хранить в строке 2 Мб и использовать для этого функцию доступа. 2Mb будет загружаться быстрее, будь то из FS или APC.

Ответ 4

Как отметил Марк, именно поэтому были созданы базы данных - чтобы вы могли эффективно выполнять поиск (и манипулировать, но может быть, не нуждаться) в этих данных, основываясь на ваших обычных шаблонах использования. Это также может быть быстрее, чем реализация собственного поиска с использованием массива. Я предполагаю, что мы говорим о где-то близком к 2-300 МБ данных (до сериализации), которые сериализуются и несериализуются каждый раз, когда вы обращаетесь к массиву.

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

Я предполагаю, почему вы не можете напрямую хранить zvals, потому что они содержат внутреннее состояние, и вы просто не можете просто указать таблицу символов переменных в предыдущую таблицу.