Производительность кодирования и декодирования serialize и json

    Возникла мысль сравнить два способа хранения абстрактных данных в виде строк, реализованных в PHP: старая добрая сериализация и относительно недавно поддерживающий в языке формат json.

    Для сравнения небольшой скрипт генерирует массив из 1000 элементов, две трети элементов которого — строки, длинной 15 символов (напополам — только буквы и только цифры), а оставшаяся треть — массивы по 50 элементов (также — буквенные и цифровые элементы пополам).

    Итог весьма неоднозначен, подробности во второй части.



    Заодно я убедился, что serialize/unserialize и json_encode/json_decode — очень быстрые функции и вряд-ли при разумных объемах данных способны стать «бутылочным горлышком».

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

    Наглядный график (заодно освоил азы Google Chart API):

    serialize vs json

    Результат работы скрипта:

    Data length (print_r): 610Kb
    Serialize: 0.22sec. (472Kb)
    Unserialize: 0.17sec.
    JSON encode: 0.19sec. (303Kb)
    JSON decode: 0.26sec.


    Код скрипта, используемого для тестирования:

    <?
    $data = array ();

    //циферки
    for ($i = 0; $i < 500; $i ++)
    {
       $data[] = getRandomString (true, 15);
    }

    //буковки
    for ($i = 0; $i < 500; $i ++)
    {
       $data[] = getRandomString (false, 15);
    }

    //вложенность треть элементов имеют вложенность
    for ($i = 0; $i < 334; $i ++)
    {
       $index = intval(rand(0, 1000));
       $data[$index] = array ();

       //вложенность по 50 элементов
       for ($j = 0; $j < 25; $j ++)
       {
          $data[$index][] = getRandomString (false, 15);
          $data[$index][] = getRandomString (true, 15);
       }

       //перемешиваем
       shuffle ($data[$index]);
    }

    //перемешиваем
    shuffle ($data);

    $rounds = 10;

    //serialize

    echo 'Data length (print_r): '. round (strlen (print_r($data, true)) / 1024). 'Kb<br/>';

    //кодирование
    $a = new pgt;
    for ($i = 0; $i < $rounds; $i ++)
    {
       $serialize = serialize ($data);
    }
    $d[0] = round ($a->getValue (), 2);
    echo 'Serialize: '. $d[0]. 'sec. ('. round (strlen ($serialize) / 1024). 'Kb)<br/>';

    //декодирование
    $a = new pgt;
    for ($i = 0; $i < $rounds; $i ++)
    {
       $serialize1 = unserialize ($serialize);
    }
    $d[1] = round ($a->getValue (), 2);
    echo 'Unserialize: '. $d[1]. 'sec.<br/>';

    //json

    //кодирование
    $a = new pgt;
    for ($i = 0; $i < $rounds; $i ++)
    {
       $json = json_encode ($data);
    }
    $d[2] = round ($a->getValue (), 2);
    echo 'JSON encode: '. $d[2]. 'sec. ('. round (strlen ($json) / 1024). 'Kb)<br/>';

    //декодирование
    $a = new pgt;
    for ($i = 0; $i < $rounds; $i ++)
    {
       $json1 = json_decode ($json);
    }
    $d[3] = round ($a->getValue (), 2);
    echo 'JSON decode: '. $d[3]. 'sec.<br/>';

    foreach ($d as $index => $value)
    {
       $d[$index] = $d[$index] * 100 * 3.7;
    }

    //$val = 'http://chart.apis.google.com/chart?cht=bvg&chd=t:'. $d[0]. ','. $d[2]. '|'. $d[1]. ','. $d[3]. '&chs=260x300&chl=serialize|json&chco=4d89f9,c6d9fd&chdl=encoding|decoding&chxt=y&chxl=0:|0 sec|0.027 sec';
    //echo '<br/><img src="'. $val. '"/>';

    class pgt {

       function __construct () {
          $this->time = $this->GetTime ();
       }

       public function getValue () {
          $value = $this->GetTime() — $this->time;
          return $value;
       }

       //служебная внутрення часть
       private function getTime () {
          list($usec, $sec) = explode(" ",microtime());
          return ((float)$usec + (float)$sec);
       }

    }

    function getRandomString ($letters, $length)
    {
       if ($letters === true)
       {
          $a = 'ABCDEFZHIJKLMKOPQRSTUVWXYZabcdefzhijklmkopqrstuvwxyz';
       }
       else
       {
          $a = '0123456789';
       }

       $result = '';
       
       for ($i = 0; $i < $length; $i ++)
       {
          $result .= substr ($a, intval(rand(0, strlen ($a) — 1)), 1);
       }

       return $result;
    }
    ?>
    * This source code was highlighted with Source Code Highlighter.


    PS Мой первый пост на Хабре :-)

    Обновление: спасибо за конструктивные комментарии, они послужили толчком для второй части материала.
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 39

      0
      ну разница мизерная, +(un)serialize встроенная ф-я,.. так что на этом тоже думаю не стоит заморачиваться =)
        0
        JSON функции также встроены в PHP начиная с версии 5.2.0. Но разница действительно мизерная.
        +1
        спасибо, было интересно почитать. Лично я предпочитаю JSON так как удобней хранить данные и можно сразу в AJAX выдавать.
          +2
          Итог: serialize/unserialize быстрее всегда.

          как так? у вас же на диаграмме serialize медленнее, чем json_encode.
            0
            Имелись в виду парные операции - т.е. кодирование сравнивалось с кодированием, декодирование - с декодированием.
              +2
              ну, и я о том же: на диаграмме serialize медленне, чем json_encode, но unserialize быстрее, чем json_decode. почему ж тогда «serialize/unserialize быстрее всегда»? unserizlize быстрее всегда, да. а serialize, наоборот, медленнее всегда, получается.
                0
                Упс... Конечно же вы правы. Исправляю топик.
            0
            Вы не использовали русский UTF-8 у нас JSON почему-то каждый не латинский символ кодировал в четырех сивольный код, так что размер как раз раздувался нереально
              0
              так правильно, яваскрипт получил escape последовательность.
                0
                ну насколько я знаю - у JS нет проблем с юникодом, в таком случае зачем искейпить русские символы?
                  0
                  это вы спросите у своего броузера
                    0
                    причем тут мой браузер? когда это продукт php-функций?
                      0
                      вы про какие символы спрашиваете? %uXXXили \uXXX в пхп?
                      http://pastie.org/242412 - вот все классно работает, в чем проблема то?
              –2
              ооо, сэкономлю еще 0.001 секунды. спасибо, мой сайт будет летать.
                +1
                микрооптимизация это потеря времени
                  0
                  Всё зависит от масштабов. Если это часто выполняемая операция, я думаю не будет лишним потратить час на тесты.
                    +3
                    Микрооптимизации должны сидеть в подкорке и делаться автоматически.
                    Руки сами не должны писать for (i = 0; i < count(array); i++).
                      0
                      Насчёт пост-инкремента согласен, но если этот цикл работает не очень много времени я всегда напишу i < count(array) чем перед циклом array_size = count(array) и i < array_size, потому что экономия несущесвенна, а читабельность и время страдает.
                        +1
                        А я как раз с вами и согласен, Я пытался сказать, что у человека, профессионально пишущего код решение вопроса надо ли писать i < count(array) или count = count(array); i < count не должно занимать больше 0,001 секунды. :) И не в коем случае не являться предметом обсуждения.
                          –1
                          Я с вами полностью согласен. Я хотел сказать, что у человека, пишущего код профессионально, решение вопроса надо ли писать i < count(array) или array_size = count(array); i < array_size не должно занимать больше 0,001 секунды и ни в коем случае не являться предметом жаркой дискуссии.
                          0
                          жаль что тема уже не обсуждается…

                          по поводу for (i = 0; i < count(array); i++) — мы как-то заинтересовались этим вопросом, решили проверить. вообще-то спор был, как джава и пхп это обрабатывают. в джаве, ясное дело, каждый раз вычисляться значение count, если массив не меняется, не будет. самое интересное — в пхп тоже не вычисляется каждый раз. скорее всего оптимизация интерпретатор…

                          возможно, опубликую данные теста
                            0
                            С точки зрения логики count — обычная функция, которая принимает параметром массив, а поэтому не совсем понятно почему она не должна исполняться каждый раз при вызове. Хотя я не претендую на знания приемов оптимизации PHP-интерпретатора.
                              0
                              потому я и написал «самое интересное». неожиданный приятный сюрприз ;)
                        +1
                        У меня на других тестовых данных получилось что json_encode занимает в 3 раза меньше времени чем serialize. Выигрыш у json по компактности данных раза в 2.

                        Также для себя посчитал плюсом что json легко читается и позволяет искать что-либо в сжатом поле SQL запросом через LIKE, иногда приходится такое делать.
                          0
                          Действительно, если в указанном примере вместо цифр и латинских символов использовать UTF-8 кириллицу, то json_encode быстрее в три раза. Спасибо за замечание, дополню топик.
                          +2
                          На самом деле на таких маленьких цифрах не очень показательно... Запустили бы хоть на 1000 раундов... Ну или на 100 раундов и по 10К элементов...
                            –2
                            Вижу, что вы не забыли использовать исключительно одинарные кавычки и echo вместо print’а. Это хорошо. Продолжайте в том же духе.
                              0
                              Это тенденция большинства популярных языков программирования — к их «облегчению», этим грешат все, но мало кто из создателей говорит об этом (а если точнее никто), не будете же вы признавать, что новые возможности имеют недостатки.
                              Для новичков это оправдано, и база изучающих язык ввиду его «лёгкости» возростает, да и потом — как правило, новички не станут делать что-то громоздкое, т.е. в большинстве своём различия не так уж заметны.
                                0
                                Давно интересуюсь этим вопросом. Подкину пару мыслей:

                                1. Эксперимент был бы интереснее, и результаты сильнее бы отличались в те или иные стороны, если бы данные были разной длины и разного размера. Попробуйте сериализовать/десериализовать: очень короткие строки, очень длинные строки (главы из книг), целые числа и вещественные числа, массивы, и так далее.

                                2. Реализация serialize/unserialize в PHP имеет ряд особенностей (и мне это не очень нравится). К примеру, попробуйте сериализовать текст, содержащий перевод строки, а потом посмотрите, как выглядит уже сериализованная строка. То есть, используя serialize(), нельзя рассчитывать гарантированно получить однострочную строку, например, для передачи скрипту параметров методом GET. Поэтому часто после serialize() приходится использовать дополнительные функции для кодирования полученного результата, например base64 или url encode.
                                  0
                                  Прошу прощения, опечатка. В первом пункте имелось в виду: если бы данные были разного типа* (строки, числа, массивы), и разного размера.
                                  +3
                                  Спасибо за проделанную работу.

                                  list($usec, $sec) = explode(" ",microtime());
                                  return ((float)$usec + (float)$sec);
                                  я обычно заменяю на microtime(true) для наглядности ;), как не странно многие не знают о такой возможности

                                  Ещё интересный факт:

                                  class MyTest extends StdClass{
                                  protected $test = 24;
                                  public function test(){
                                  echo $this->test, PHP_EOL;
                                  }
                                  }

                                  $a = new MyTest();
                                  $a->test1 = 'string';

                                  $b = unserialize( serialize($a));
                                  var_dump($b);
                                  $b->test();

                                  $c = json_decode(json_encode($a));
                                  var_dump($c);
                                  $c->test();
                                  /*/ // вывод скрипта
                                  object(MyTest)#2 (2) {
                                  ["test:protected"]=>
                                  int(24)
                                  ["test1"]=>
                                  string(6) "string"
                                  }
                                  24
                                  object(stdClass)#3 (1) {
                                  ["test1"]=>
                                  string(6) "string"
                                  }

                                  Fatal error: Call to undefined method stdClass::test() in /home/kandy/workspace/TelmeWeb/Telme/test.php on line 31

                                  Call Stack:
                                  0.0003 96944 1. {main}() /home/kandy/workspace/TelmeWeb/Telme/test.php:0
                                  /**/


                                  Вывод прост: json предназначен для другого
                                    0
                                    Замечательная пометка про microtime(true), очень показательный пример с классом. Блеск.
                                      0
                                      В "спеке" по JSON'у говориться только об упаковке свойств объекта.
                                      Про упаковку методов - ни слова.
                                        0
                                        я не совсем понимаю зачем вообще упаковывать методы
                                          0
                                          В том то и дело что незачем.
                                      0
                                      А по-моему вы занимаетесь бредом... так можно начать писать про сравнение strtr и str_replace и вообще всех схожих функций... ещё и в мультибайтовом режиме отдельно... бестолковая статья - такие манёвры при желании можно провести у себя на компе за 3 минуты, запихав то что надо в цикл на 10000 итераций и сравнить результаты... а то что вы треть массива строк тоже сделали массивами абсолютно ничего не значит т.к. и серриализованная и JSON-структуры хранятся в виде строк.
                                        0
                                        экономия на спичках, имхо.
                                          0
                                          Для полноты картины сравните с XML и Bencode.

                                          Bencode - это формат, обеспечивающий максимально возможную скорость десериализации за счет отсутствия накладных расходов на парсинг и определение типов значений. Используется для кодирования torrent файлов.
                                          Реализаций на PHP хватает. Например: http://www.pear.php.net/package/File_Bittorrent2/
                                          • НЛО прилетело и опубликовало эту надпись здесь

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

                                            Самое читаемое