Preferred method to store PHP arrays (json_encode vs serialize)

Viewed 221317

I need to store a multi-dimensional associative array of data in a flat file for caching purposes. I might occasionally come across the need to convert it to JSON for use in my web app but the vast majority of the time I will be using the array directly in PHP.

Would it be more efficient to store the array as JSON or as a PHP serialized array in this text file? I've looked around and it seems that in the newest versions of PHP (5.3), json_decode is actually faster than unserialize.

I'm currently leaning towards storing the array as JSON as I feel its easier to read by a human if necessary, it can be used in both PHP and JavaScript with very little effort, and from what I've read, it might even be faster to decode (not sure about encoding, though).

Does anyone know of any pitfalls? Anyone have good benchmarks to show the performance benefits of either method?

20 Answers

Depends on your priorities.

If performance is your absolute driving characteristic, then by all means use the fastest one. Just make sure you have a full understanding of the differences before you make a choice

  • Unlike serialize() you need to add extra parameter to keep UTF-8 characters untouched: json_encode($array, JSON_UNESCAPED_UNICODE) (otherwise it converts UTF-8 characters to Unicode escape sequences).
  • JSON will have no memory of what the object's original class was (they are always restored as instances of stdClass).
  • You can't leverage __sleep() and __wakeup() with JSON
  • By default, only public properties are serialized with JSON. (in PHP>=5.4 you can implement JsonSerializable to change this behavior).
  • JSON is more portable

And there's probably a few other differences I can't think of at the moment.

A simple speed test to compare the two

<?php

ini_set('display_errors', 1);
error_reporting(E_ALL);

// Make a big, honkin test array
// You may need to adjust this depth to avoid memory limit errors
$testArray = fillArray(0, 5);

// Time json encoding
$start = microtime(true);
json_encode($testArray);
$jsonTime = microtime(true) - $start;
echo "JSON encoded in $jsonTime seconds\n";

// Time serialization
$start = microtime(true);
serialize($testArray);
$serializeTime = microtime(true) - $start;
echo "PHP serialized in $serializeTime seconds\n";

// Compare them
if ($jsonTime < $serializeTime) {
    printf("json_encode() was roughly %01.2f%% faster than serialize()\n", ($serializeTime / $jsonTime - 1) * 100);
}
else if ($serializeTime < $jsonTime ) {
    printf("serialize() was roughly %01.2f%% faster than json_encode()\n", ($jsonTime / $serializeTime - 1) * 100);
} else {
    echo "Impossible!\n";
}

function fillArray( $depth, $max ) {
    static $seed;
    if (is_null($seed)) {
        $seed = array('a', 2, 'c', 4, 'e', 6, 'g', 8, 'i', 10);
    }
    if ($depth < $max) {
        $node = array();
        foreach ($seed as $key) {
            $node[$key] = fillArray($depth + 1, $max);
        }
        return $node;
    }
    return 'empty';
}

JSON is simpler and faster than PHP's serialization format and should be used unless:

  • You're storing deeply nested arrays: json_decode(): "This function will return false if the JSON encoded data is deeper than 127 elements."
  • You're storing objects that need to be unserialized as the correct class
  • You're interacting with old PHP versions that don't support json_decode

You might also be interested in https://github.com/phadej/igbinary - which provides a different serialization 'engine' for PHP.

My random/arbitrary 'performance' figures, using PHP 5.3.5 on a 64bit platform show :

JSON :

  • JSON encoded in 2.180496931076 seconds
  • JSON decoded in 9.8368630409241 seconds
  • serialized "String" size : 13993

Native PHP :

  • PHP serialized in 2.9125759601593 seconds
  • PHP unserialized in 6.4348418712616 seconds
  • serialized "String" size : 20769

Igbinary :

  • WIN igbinary serialized in 1.6099879741669 seconds
  • WIN igbinrary unserialized in 4.7737920284271 seconds
  • WIN serialized "String" Size : 4467

So, it's quicker to igbinary_serialize() and igbinary_unserialize() and uses less disk space.

I used the fillArray(0, 3) code as above, but made the array keys longer strings.

igbinary can store the same data types as PHP's native serialize can (So no problem with objects etc) and you can tell PHP5.3 to use it for session handling if you so wish.

See also http://ilia.ws/files/zendcon_2010_hidden_features.pdf - specifically slides 14/15/16

If you are caching information that you will ultimately want to "include" at a later point in time, you may want to try using var_export. That way you only take the hit in the "serialize" and not in the "unserialize".

I know this is late but the answers are pretty old, I thought my benchmarks might help as I have just tested in PHP 7.4

Serialize/Unserialize is much faster than JSON, takes less memory and space, and wins outright in PHP 7.4 but I am not sure my test is the most efficient or the best,

I have basically created a PHP file which returns an array which I encoded, serialised, then decoded and unserialised.

$array = include __DIR__.'/../tests/data/dao/testfiles/testArray.php';

//JSON ENCODE
$json_encode_memory_start = memory_get_usage();
$json_encode_time_start = microtime(true);

for ($i=0; $i < 20000; $i++) { 
    $encoded = json_encode($array);
}

$json_encode_time_end = microtime(true);
$json_encode_memory_end = memory_get_usage();
$json_encode_time = $json_encode_time_end - $json_encode_time_start;
$json_encode_memory = 
$json_encode_memory_end - $json_encode_memory_start;


//SERIALIZE
$serialize_memory_start = memory_get_usage();
$serialize_time_start = microtime(true);

for ($i=0; $i < 20000; $i++) { 
    $serialized = serialize($array);
}

$serialize_time_end = microtime(true);
$serialize_memory_end = memory_get_usage();
$serialize_time = $serialize_time_end - $serialize_time_start;
$serialize_memory = $serialize_memory_end - $serialize_memory_start;


//Write to file time:
$fpc_memory_start = memory_get_usage();
$fpc_time_start = microtime(true);

for ($i=0; $i < 20000; $i++) { 
    $fpc_bytes = 
    file_put_contents(
        __DIR__.'/../tests/data/dao/testOneBigFile',
        '<?php return '.var_export($array,true).' ?>;'
    );
}

$fpc_time_end = microtime(true);
$fpc_memory_end = memory_get_usage();
$fpc_time = $fpc_time_end - $fpc_time_start;
$fpc_memory = $fpc_memory_end - $fpc_memory_start;


//JSON DECODE
$json_decode_memory_start = memory_get_usage();
$json_decode_time_start = microtime(true);

for ($i=0; $i < 20000; $i++) { 
    $decoded = json_encode($encoded);
}

$json_decode_time_end = microtime(true);
$json_decode_memory_end = memory_get_usage();
$json_decode_time = $json_decode_time_end - $json_decode_time_start;
$json_decode_memory = 
$json_decode_memory_end - $json_decode_memory_start;


//UNSERIALIZE
$unserialize_memory_start = memory_get_usage();
$unserialize_time_start = microtime(true);

for ($i=0; $i < 20000; $i++) { 
    $unserialized = unserialize($serialized);
}

$unserialize_time_end = microtime(true);
$unserialize_memory_end = memory_get_usage();
$unserialize_time = $unserialize_time_end - $unserialize_time_start;
$unserialize_memory = 
$unserialize_memory_end - $unserialize_memory_start;


//GET FROM VAR EXPORT:
$var_export_memory_start = memory_get_usage();
$var_export_time_start = microtime(true);

for ($i=0; $i < 20000; $i++) { 
    $array = include __DIR__.'/../tests/data/dao/testOneBigFile';
}

$var_export_time_end = microtime(true);
$var_export_memory_end = memory_get_usage();
$var_export_time = $var_export_time_end - $var_export_time_start;
$var_export_memory = $var_export_memory_end - $var_export_memory_start;

Results:

Var Export length: 11447 Serialized length: 11541 Json encoded length: 11895 file put contents Bytes: 11464

Json Encode Time: 1.9197590351105 Serialize Time: 0.160325050354 FPC Time: 6.2793469429016

Json Encode Memory: 12288 Serialize Memory: 12288 FPC Memory: 0

JSON Decoded time: 1.7493588924408 UnSerialize Time: 0.19309520721436 Var Export and Include: 3.1974139213562

JSON Decoded memory: 16384 UnSerialize Memory: 14360 Var Export and Include: 192

I would suggest you to use Super Cache, which is a file cache mechanism which won't use json_encode or serialize. It is simple to use and really fast compared to other PHP Cache mechanism.

https://packagist.org/packages/smart-php/super-cache

Ex:

<?php
require __DIR__.'/vendor/autoload.php';
use SuperCache\SuperCache as sCache;

//Saving cache value with a key
// sCache::cache('<key>')->set('<value>');
sCache::cache('myKey')->set('Key_value');

//Retrieving cache value with a key
echo sCache::cache('myKey')->get();
?>
Related