2009-12-07

MessagePack for PHP のベンチマーク測定

php_msgpackのベンチマーク測定を行いました。
比較対象は、PHP serializeとJSONです。

ベンチマークには以下のようなコードを用いました。

class MyBench
{
  private $packer;
  private $unpacker;
  private $name;
  private $time = 0;
  private $size = 0;
  private $debug = 0;

  function __construct($name, $packer, $unpacker)
  {
    $this->name = $name;
    $this->packer = $packer;
    $this->unpacker = $unpacker;
  }

  public function init()
  {
    $this->size = $this->time = 0;
  }

  public function exec($var)
  {
    $packed = $this->exec_pack($var);
    $unpacked = $this->exec_unpack($packed);
    if ($this->debug) {
      var_dump(bin2hex($packed));
      var_dump($unpacked);
    }
  }

  public function exec_pack($var)
  {
    $func = $this->packer;
    $t = microtime(true);
    $packed = $func($var);
    $this->time += microtime(true) - $t;
    $this->size += strlen($packed);
    return $packed;
  }

  public function exec_unpack($var)
  {
    $func = $this->unpacker;
    $t = microtime(true);
    $unpacked = $func($var);
    $this->time += microtime(true) - $t;
    return $unpacked;
  }

  public function dump_result($msg = "")
  {
    printf("% 25s ", $msg);
    print("$this->time sec, $this->size byte\n");
  }

  public function bench_int($num = 10000)
  {
    $this->init();
    for ($i = 0; $i < $num; $i++) {
      $this->exec($i);
    }
    $this->dump_result("$this->name (int):");
  }

  public function bench_float($num = 10000)
  {
    $this->init();
    for ($i = 0; $i < $num; $i++) {
      $this->exec((float)($i / 23));
    }
    $this->dump_result("$this->name (float):");
  }

  public function bench_string($num = 10000)
  {
    $this->init();
    $str = "";
    for ($i = 0; $i < $num; $i++) {
      $this->exec($str);
      $str .= "$i";
    }
    $this->dump_result("$this->name (string):");
  }

  public function bench_array($num = 1000)
  {
    $this->init();
    $arr = array();
    for ($i = 0; $i < $num; $i++) {
      $this->exec($arr);
      $arr []= $i;
    }
    $this->dump_result("$this->name (array):");
  }

  public function bench_map($num = 1000)
  {
    $this->init();
    $arr = array();
    for ($i = 0; $i < $num; $i++) {
      $this->exec($arr);
      $arr["k$i"] = "v$i";
    }
    $this->dump_result("$this->name (map):");
  }

  public function bench_all()
  {
    $this->bench_int();
    $this->bench_float();
    $this->bench_string();
    $this->bench_array();
    $this->bench_map();
  }
}

$packers = array(array("MessagePack", "msgpack_pack", "msgpack_unpack"),
                 array("PHP serialize", "serialize", "unserialize"),
                 array("JSON", "json_encode", "json_decode")
                 );


foreach ($packers as $p) {
  $obj = new MyBench($p[0], $p[1], $p[2]);
  $obj->bench_all();
}

実行結果は若干見づらかったので、結果を表にまとめると以下のようになりました。


 (注1.数字の単位は秒です。シリアライズ後のサイズは省略しました。
注2.便宜上、キーが文字列の配列のことをmapと書いてます。)

全体的に見るとMessagePackが優秀ですが、型によってはJSONやPHP Serializeの方が速いものもありました。
特に、文字列型ではJSONが異常に遅い一方で、PHP Serializeは健闘しています。


ちなみにMessagePackのシリアライズだけの時間を見てみると、 以下のようになりました。
       MessagePack (int): 0.0383882522583 sec, 29616 byte
     MessagePack (float): 0.0395138263702 sec, 90000 byte
    MessagePack (string): 0.187726020813 sec, 189415563 byte
     MessagePack (array): 0.0688591003418 sec, 2314272 byte
       MessagePack (map): 0.0919380187988 sec, 5287678 byte

int, float, stringに関しては大体半分くらいの時間ですが、どうやらarray のデシリアライズに異常に時間がかかっていることがわかりました。
時間があれば詳しく調べてみようと思いますが、たぶん、ひとつの配列要素を読むごとにzendオブジェクトをallocしてしまっているのが原因だと思われます。

また、takei-hさんのバージョンでも同じようにしてシリアライズの時間を測定してみました。
        MessagePack (int): 0.0359139442444 sec, 29616 byte
     MessagePack (float): 0.0392725467682 sec, 90000 byte
    MessagePack (string): 0.229011297226 sec, 189415563 byte
     MessagePack (array): 0.0804646015167 sec, 1158620 byte
       MessagePack (map): 0.0922095775604 sec, 4788178 byte

結果としては微妙に遅くなっていますがあまり大きな差はありませんでした。
配列をあらかじめ調べてmapかarrayか判断する、という処理は意外と軽いということがわかりました。
配列を長くすれば少し遅くなるのですが、すべてmap型でシリアライズしてしまった場合でもデータサイズが大きくなってその分オーバーヘッドが生じるので、デシリアライズも含めて考えると結果的にはあらかじめ調べておいた方が得になりそうです。

0 件のコメント:

コメントを投稿

ZenBackWidget