Комментарии 74
Больная тема. Протестируйте на 5.3.3 — будете удивлены результатом.
0
Длина массива 2*10^6? Неслабо
+8
В комментариях к статье «Производительность кодирования и декодирования serialize и json» (http://habrahabr.ru/blogs/php/30210/) рекомендуют для кодирования больших объемов данных использовать реализацию на PHP формата Bencode.
+3
Для больших массивов попробуйте использовать var_dump($var, true); и пишите в файл. По идее в данном случае файловая операция будет быстрее чем 30 секунд.
0
Вы же имели ввиду var_export?
+6
Это serialize. А unserialize в таком случае как сделать?
0
eval (для var_export)
0
Интерестно а что будет если подменить файлик c массивом ( или запись в базе ) на произвольный PHP-код?
0
А вы время засекали? Сериализация ведь занимает мало времени, основная проблема десериализация.
0
Собственно:
ini_set('memory_limit', '512M');
$file = '/tmp/1';
$data = range(1,2000000);
echo «Test serialize\n»;
$time0 = microtime(1);
file_put_contents($file, var_export($data, true));
unset($data);
$time1 = microtime(1);
eval( '$data='.file_get_contents($file).';');
$time2 = microtime(1);
$timeset = $time1-$time0;
$timeget = $time2-$time1;
echo «Serialize set time $timeset get time $timeget\n»;
Результат:
Serialize set time 3.1096308231354 get time 2.9017460346222
ini_set('memory_limit', '512M');
$file = '/tmp/1';
$data = range(1,2000000);
echo «Test serialize\n»;
$time0 = microtime(1);
file_put_contents($file, var_export($data, true));
unset($data);
$time1 = microtime(1);
eval( '$data='.file_get_contents($file).';');
$time2 = microtime(1);
$timeset = $time1-$time0;
$timeget = $time2-$time1;
echo «Serialize set time $timeset get time $timeget\n»;
Результат:
Serialize set time 3.1096308231354 get time 2.9017460346222
+2
Хороший результат =) в таком случае он имеет право на жизнь, мне такая вариация больше понравилась: habrahabr.ru/blogs/php/104069/#comment_3243873
0
еще можно eval( '$data='.file_get_contents($file).';'); заменить на include $file;
0
Можно сделать лучше:
file_put_contents($file, '<?php return'.var_export($data,1).'; ?>');
$data = include $file;
+6
Можно при экспорте запихнуть в теги <?php ?> и потом просто делать include или require, так по крайней мере закешируется опкод. Это если писать в файл. Если в сессию, то такое не пойдет :(
0
Интересно другое. Зачем сериализовать массивы, содержащие 2млн элементов?
+5
Например, есть исследовательский скрипт, который производит анализ определённых данных. Для его работы первоначально нужно составить справочник на 2млн элементов. Справочник составляется долго. Хочется ускорить скрипт. Чтобы не составлять справочник лишний раз, если исходные данные не изменились, можно этот справочник закешировать.
0
почему бы не использовать любое key:value хранилище?
+1
Да, я понимаю, что можно сделать быстрее, лучше и прочнее. Дело в том, что цели — исследовательские (причем исследование не возможностей PHP, а исследование данных), потому очень часто приходится менять и формат хранения и способы использования.
Этот словарик в одних опытах используется как инструмент частотного анализа, в других как множество и надо проверять элемент на вхождение в это множество. Впереди опыты, где надо будет пересекать словарики-множества построенные на разных исходных данных.
Вот чтобы не отвлекаться на изменение формата хранения я и использую обычные массивы. Исходная проблема с кешированием этого словарика уже решена — сейчас используется json.
Осталась проблема с квадратичным временем выполнения unserialize. Об этом и написана статья.
Этот словарик в одних опытах используется как инструмент частотного анализа, в других как множество и надо проверять элемент на вхождение в это множество. Впереди опыты, где надо будет пересекать словарики-множества построенные на разных исходных данных.
Вот чтобы не отвлекаться на изменение формата хранения я и использую обычные массивы. Исходная проблема с кешированием этого словарика уже решена — сейчас используется json.
Осталась проблема с квадратичным временем выполнения unserialize. Об этом и написана статья.
+1
А вы не пробовали эксперементировать с ленивой загрузкой или подобными методами?
0
Небольшой оффтоп: Я что-то не понимаю, но почему здесь нельзя использовать БД?
0
Попробуйте IG Binary
+3
Попробуйте Data::Dumper и eval.
Или Storable.
Или Storable.
-1
хоть и ужасно несекурно, но если нужен результат «на скорую руку»:
function cache_store($data, $key) {
file_put_contents(«cache/».$key.".php", "
function cache_store($data, $key) {
file_put_contents(«cache/».$key.".php", "
+2
парсер — кака
pastebin.com/xgbL3NUs
pastebin.com/xgbL3NUs
+1
в обычной жизни евал (или инклюд, как в примере) гораздо тормознее сериалайза
0
… и значительно шустрее unserialize'а. проверено на собственной шкуре. даже массивчика в 2К элементов достаточно, чтобы заставить тупить сайт, использующий unserialize. при тех же условиях include работает сильно быстрее.
0
ну может сферический unserialize в вакууме и обгоняет сферический include… в боевых условиях, когда данные — это не просто range(1, 2000), а многомерный массив и обращение к этим данным происходит очень часто include выигрывает (наверное, за счёт кеша фс). по крайней мере, когда в DLE был дефолтный кеш на serialize+unserialize страница под нагрузкой генерилась 20 секунд. когда я сделал var_export+include она стала генериться 400 мсек.
0
В моем примере не использовался range(1, 2000), а условия генерации очень близки к реальной работе. Давайте подумаем вместе: чтобы загрузить serialize-данные необходимо просто разместить их в памяти, а для инклюд-данных нужно сначала интерпретировать код, что уже само собой отнимает время. Поэтому врядли кеш фс играет тут какую-то роль. Может быть в ваших экспериментах не учтен ускоритель пхп (xcache, eAccelerator)?
0
Давайте подумаем :)
Чтобы десериализировать данные нужно:
1. вычитать их из хранилища (файл, база, етс.)
2. разместить их в памяти
3. распарсить их в пхп-структуру, проверив корректность
Чтобы сделать инклюд нужно:
1. вычитать код из хранилища
2. разместить его в памяти
3. выполнить его, проверив на корректность
По-вашему выходит так, что распарсить строку в пхп-структуру быстрее, чем сделать eval. Вполне возможно, что в идеальных условиях так и есть, но в реальности, я повторюсь, мой метод выиграл у unserialize'а по производительности с огромным опережением. Речь идёт о проекте с ~200k хитов в сутки. Без нагрузки unserialize справлялся отлично, но как только нагрузку дали страница стала генериться по полминуты. Заменил unserialize на include — вышло 200-400 мсек без каких-либо акселераторов.
Чтобы десериализировать данные нужно:
1. вычитать их из хранилища (файл, база, етс.)
2. разместить их в памяти
3. распарсить их в пхп-структуру, проверив корректность
Чтобы сделать инклюд нужно:
1. вычитать код из хранилища
2. разместить его в памяти
3. выполнить его, проверив на корректность
По-вашему выходит так, что распарсить строку в пхп-структуру быстрее, чем сделать eval. Вполне возможно, что в идеальных условиях так и есть, но в реальности, я повторюсь, мой метод выиграл у unserialize'а по производительности с огромным опережением. Речь идёт о проекте с ~200k хитов в сутки. Без нагрузки unserialize справлялся отлично, но как только нагрузку дали страница стала генериться по полминуты. Заменил unserialize на include — вышло 200-400 мсек без каких-либо акселераторов.
0
Может быть дело в том, что размер массива, записанного в сериализованном виде был 8 мегабайт? :-D
0
1. Таки что же вы подразумеваете под «выполнить его» в 3-ем пункте инклюда?
2. Сравнима ли интерпретация пхп-кода и простая проверка на длину данных + размещение данных в памяти в serialize?
3. Что значит «нагрузку дали»? Закончилась RAM?
4. Используется ли кешатор пхп-кода в RAM (xcache, eAccelerator и т.п.)?
2. Сравнима ли интерпретация пхп-кода и простая проверка на длину данных + размещение данных в памяти в serialize?
3. Что значит «нагрузку дали»? Закончилась RAM?
4. Используется ли кешатор пхп-кода в RAM (xcache, eAccelerator и т.п.)?
0
1 — Ну таки код РНР нужно перегнать в Zend-структуру? Или нет? :)
2 — Вы считаете, что unserialize — это только проверить длину строки и разместить в памяти? А то, что из строки нужно получить массив с правильными типами каждого элемента? Скорее всего внутри РНР идёт посимвольная обработка строки.
3 — Значит, запустили ab :)
4 — Не используется до сих пор. Всё, что изменили — переехали с винды на генту и спрятали апач за джинкс, но всё это вряд ли критично для конкретно обсуждаемого куска кеша.
2 — Вы считаете, что unserialize — это только проверить длину строки и разместить в памяти? А то, что из строки нужно получить массив с правильными типами каждого элемента? Скорее всего внутри РНР идёт посимвольная обработка строки.
3 — Значит, запустили ab :)
4 — Не используется до сих пор. Всё, что изменили — переехали с винды на генту и спрятали апач за джинкс, но всё это вряд ли критично для конкретно обсуждаемого куска кеша.
0
> Ну таки код РНР нужно перегнать в Zend-структуру? Или нет? :)
то есть распарсить, проверить на синтаксис и интерпретировать, в процессе чего будет перегонка данных в структуру?
> Вы считаете, что unserialize — это только проверить длину строки и разместить в памяти
в том-то и дело, был даже такой баг когда при неверных значениях длины данных, пхп выдавал куски таблицы памяти
> Значит, запустили ab :)
при таких размерах данных, видимо, закончилась RAM и их негде было размещать. Или там были одни и те же данные? Других размеров?
то есть распарсить, проверить на синтаксис и интерпретировать, в процессе чего будет перегонка данных в структуру?
> Вы считаете, что unserialize — это только проверить длину строки и разместить в памяти
в том-то и дело, был даже такой баг когда при неверных значениях длины данных, пхп выдавал куски таблицы памяти
> Значит, запустили ab :)
при таких размерах данных, видимо, закончилась RAM и их негде было размещать. Или там были одни и те же данные? Других размеров?
0
1 — в процессе интерпретации ;-)
2 — ещё раз, я не говорю про длину и всё такое. кроме проверки длины нужно РАЗОБРАТЬ строку и СФОРМИРОВАТЬ структуру.
3 — это был список категорий :-) он практически не менялся. память не закончилась, памяти было несколько гигов свободно. сильно загружался процессор.
2 — ещё раз, я не говорю про длину и всё такое. кроме проверки длины нужно РАЗОБРАТЬ строку и СФОРМИРОВАТЬ структуру.
3 — это был список категорий :-) он практически не менялся. память не закончилась, памяти было несколько гигов свободно. сильно загружался процессор.
0
> ещё раз, я не говорю про длину и всё такое. кроме проверки длины нужно РАЗОБРАТЬ строку и СФОРМИРОВАТЬ структуру.
я правильно понимаю, что затраты на разбор строки сериализованных данных несравнимы с затратами на распарсивание кода?
> это был список категорий :-) он практически не менялся. память не закончилась, памяти было несколько гигов свободно. сильно загружался процессор.
похоже, тут дело в кешировании исполняемого кода системой и получается прирост в производительности если данные одни и те же
я правильно понимаю, что затраты на разбор строки сериализованных данных несравнимы с затратами на распарсивание кода?
> это был список категорий :-) он практически не менялся. память не закончилась, памяти было несколько гигов свободно. сильно загружался процессор.
похоже, тут дело в кешировании исполняемого кода системой и получается прирост в производительности если данные одни и те же
0
> я правильно понимаю, что затраты на разбор строки сериализованных данных несравнимы с затратами на распарсивание кода?
зависит от реализации одного и другого. я считаю, что однозначно это утверждать нельзя. всё-таки разбор пхп-кода — это основная задача интерпретатора и оптимизация скорости этого процесса есть одна из первостепенных задач разработчиков. чего нельзя сказать про десериализацию.
> похоже, тут дело в кешировании исполняемого кода системой и получается прирост в производительности если данные одни и те же
стоял (и стоит) голый интерпретатор, без оптимайзеров, без кешеров. не могу утверждать на 100%, код не читал, но мне кажется, что сам по себе интерпретатор не кеширует код между запросами.
зы: php 5.2.13 win32, apache 2.2, windows 2k3 32bit, 8 процессоров по 2 ядра, 16 гиг оперативки.
зависит от реализации одного и другого. я считаю, что однозначно это утверждать нельзя. всё-таки разбор пхп-кода — это основная задача интерпретатора и оптимизация скорости этого процесса есть одна из первостепенных задач разработчиков. чего нельзя сказать про десериализацию.
> похоже, тут дело в кешировании исполняемого кода системой и получается прирост в производительности если данные одни и те же
стоял (и стоит) голый интерпретатор, без оптимайзеров, без кешеров. не могу утверждать на 100%, код не читал, но мне кажется, что сам по себе интерпретатор не кеширует код между запросами.
зы: php 5.2.13 win32, apache 2.2, windows 2k3 32bit, 8 процессоров по 2 ядра, 16 гиг оперативки.
0
Думаю, из моих тестов можно сделать выводы по скорости разбора (4-5 раз). Наивно предполагать, что десеаризация, скажем, объекта будет занимать дольше его же распарсивания в коде.
С некоторой погрешностью можно представить мою мысль наглядно:
// unserialize
$data = unserialize("...");
// упрощенный процесс include
eval('$data = unserialize("...")');
С некоторой погрешностью можно представить мою мысль наглядно:
// unserialize
$data = unserialize("...");
// упрощенный процесс include
eval('$data = unserialize("...")');
0
кстати, на графике в посте не видно кривой инклюда
0
>В PHP есть две замечательные функции serialize и unserialize.
никакие они не замечательные, встроенные объекты абсолютно не переваривают. Причем без всяких предупреждений, просто на выходе пустой объект. И разработчики даже не считают это багом :(
никакие они не замечательные, встроенные объекты абсолютно не переваривают. Причем без всяких предупреждений, просто на выходе пустой объект. И разработчики даже не считают это багом :(
-2
То есть на наборе, на котором serialize отрабативает за 30 сек., json_encode — за 0.27..? Я правильно понял?
0
на P4 3.0Ghz, ubuntu выдало: «Test serialize Serialize set time 9.42804217339 get time 65.6582648754» о_О
0
Test serialize
Serialize set time 1.63469910622 get time 2.52785992622
Intel® Core(TM)2 Quad CPU Q9300 @ 2.50GHz
PHP 5.3.2-1ubuntu4.2
Serialize set time 1.63469910622 get time 2.52785992622
Intel® Core(TM)2 Quad CPU Q9300 @ 2.50GHz
PHP 5.3.2-1ubuntu4.2
+1
Есть такая засада. В общем, они там при десериализации с целью поддержки ссылок каждую переменную кладут еще и в отдельный список. Список этот — обычный односвязный, по 1024 переменные на элемент списка. Правда, чтобы положить очередную переменную, они проходятся по списку, начиная с головы, так что сложность алгоритма получается порядка N2*log(N)/1024.
0
bugs.php.net/bug.php?id=52832
Вину за собой признают, правда патчить 5.3 отказываются, видимо, пойдет в 6.0 (5.4?).
Вину за собой признают, правда патчить 5.3 отказываются, видимо, пойдет в 6.0 (5.4?).
0
Я обнаружил, что время работы функции unserialize может оказаться неожиданно большим.к бабке гадалке далеко ходить не надо, ясен пень, что большим. Все что связано с деревьями — это тяжелые операции. Все почему-то не любят по этому XML, но забывают про другие структуры данных. Но если использовать простые структуры данных (типа одномерного массивчика), то это на фоне общей производительность будет не заметно.
0
Я сначала тоже подумал, что здесь слишком большое время построения двоичного дерева по ключам, и особенность принципиальная. Потому я и проверил эту гипотезу на JSON. Как видим, можно сделать и быстрее, но почему-то не сделано.
0
Все из-за кривой реализации поддержки ссылок, в транке уже поправили, см. мою ссылку выше.
0
Спасибо, что обратили моё внимание на комментарий habrahabr.ru/blogs/php/104069/#comment_3250274 Я его как-то проглядел.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Производительность функции unserialize