概念
测试脚本
- protobuf schema (test.proto)
message Test {
optional int32 aaa = 1;
repeated string bbb = 2;
}
- 编译test.proto
<?php
/**
* Test message
*/
class Test extends ProtobufMessage
{
/* Field index constants */
const AAA = 1;
const BBB = 2;
/* @var array Field descriptors */
protected static $fields = array(
self::AAA => array(
'name' => 'aaa',
'required' => false,
'type' => 5,
),
self::BBB => array(
'name' => 'bbb',
'repeated' => true,
'type' => 7,
),
);
/**
* Constructs new message container and clears its internal state
*
* @return null
*/
public function __construct()
{
$this->reset();
}
/**
* Clears message values and sets default ones
*
* @return null
*/
public function reset()
{
$this->values[self::AAA] = null;
$this->values[self::BBB] = array();
}
/**
* Returns field descriptors
*
* @return array
*/
public function fields()
{
return self::$fields;
}
/**
* Sets value of 'aaa' property
*
* @param int $value Property value
*
* @return null
*/
public function setAaa($value)
{
return $this->set(self::AAA, $value);
}
/**
* Returns value of 'aaa' property
*
* @return int
*/
public function getAaa()
{
return $this->get(self::AAA);
}
/**
* Appends value to 'bbb' list
*
* @param string $value Value to append
*
* @return null
*/
public function appendBbb($value)
{
return $this->append(self::BBB, $value);
}
/**
* Clears 'bbb' list
*
* @return null
*/
public function clearBbb()
{
return $this->clear(self::BBB);
}
/**
* Returns 'bbb' list
*
* @return string[]
*/
public function getBbb()
{
return $this->get(self::BBB);
}
/**
* Returns 'bbb' iterator
*
* @return ArrayIterator
*/
public function getBbbIterator()
{
return new ArrayIterator($this->get(self::BBB));
}
/**
* Returns element from 'bbb' list at given offset
*
* @param int $offset Position in list
*
* @return string
*/
public function getBbbAt($offset)
{
return $this->get(self::BBB, $offset);
}
/**
* Returns count of 'bbb' list
*
* @return int
*/
public function getBbbCount()
{
return $this->count(self::BBB);
}
}
- php测试脚本
<?php
/**
* @name 序列化调研
* @author Rohn(253133755@qq.com)
* @date 2017/7/6
*/
include 'pb_proto_test.php';
class SerializeTest
{
private $test_data = array();
private $data_json = array();
private $data_pb = array();
public $report = array();
static $str = array(
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
'A',
'B',
'C',
'D',
'E',
'F',
'G',
'H',
'I',
'J',
'K',
'L',
'M',
'N',
'O',
'P',
'Q',
'R',
'S',
'T',
'U',
'V',
'W',
'X',
'Y',
'Z',
1,
2,
3,
4,
5,
6,
7,
8,
9,
0,
'_',
'!',
'@',
'#',
'$',
'%',
'^',
'&',
'*',
'(',
')',
'-',
'+',
'=',
'阿',
'萨',
'德',
'额',
'人',
'哦',
'吗',
'从',
'是',
'去',
'吧',
'你',
'放',
'如',
);
/**
* 运行入口
*/
public function run(){
$list = array(
1000,
3000,
30000,
100000,
300000,
);
foreach($list as $test){
$this->_do_run($test);
}
print_r($this->report);
}
/**
* 运行测试数据
* @param $count
*/
private function _do_run($count){
$this->_get_data($count);
$encode_report = $this->_encode();
$decode_report = $this->_decode();
$this->report[] = array_merge($encode_report, $decode_report);
}
/**
* 序列化
* @return array
*/
private function _encode(){
//json
$this->data_json = null;
$s_mem = memory_get_usage();
$s_time = microtime(true);
$this->data_json = json_encode($this->test_data);
$e_time = microtime(true);
$e_mem = memory_get_usage();
$length_json = number_format(mb_strlen($this->data_json) / 1024, 7, '.', '');
$encode_meme_json = $e_mem - $s_mem;
$encode_time_json = $e_time - $s_time;
//protobuf
$test_obj = new Test();
$this->data_pb = null;
$test_obj->setAaa($this->test_data['aaa']);
if(isset($this->test_data['bbb']) && count($this->test_data['bbb']) > 0){
foreach($this->test_data['bbb'] as $b){
$test_obj->appendBbb($b);
}
}
$s_mem = memory_get_usage();
$s_time = microtime(true);
$this->data_pb = $test_obj->serializeToString();
$e_time = microtime(true);
$e_mem = memory_get_usage();
$length_pb = number_format(mb_strlen($this->data_pb) / 1024, 7, '.', '');
$encode_meme_pb = $e_mem - $s_mem;
$encode_time_pb = $e_time - $s_time;
$test_obj = null;
return array(
'length_js' => $length_json,
'length_pb' => $length_pb,
'length_p' => ($length_json - $length_pb) * 100 / $length_pb,
'encode_mem_js' => $encode_meme_json,
'encode_mem_pb' => $encode_meme_pb,
'encode_mem_p' => ($encode_meme_json - $encode_meme_pb) * 100 / $encode_meme_pb,
'encode_time_js' => $encode_time_json,
'encode_time_pb' => $encode_time_pb,
'encode_time_p' => ($encode_time_json - $encode_time_pb) * 100 / $encode_time_pb,
);
}
/**
* 解序列化
* @return array
*/
private function _decode(){
//json
$s_mem = memory_get_usage();
$s_time = microtime(true);
$d = json_decode($this->data_json, 1);
$e_time = microtime(true);
$e_mem = memory_get_usage();
$decode_meme_json = $e_mem - $s_mem;
$decode_time_json = $e_time - $s_time;
$d = null;
//protobuf
$test_obj = new Test();
$s_mem = memory_get_usage();
$s_time = microtime(true);
$test_obj->parseFromString($this->data_pb);
$e_time = microtime(true);
$e_mem = memory_get_usage();
$decode_meme_pb = $e_mem - $s_mem;
$decode_time_pb = $e_time - $s_time;
$test_obj = null;
return array(
'decode_mem_js' => $decode_meme_json,
'decode_mem_pb' => $decode_meme_pb,
'decode_mem_p' => ($decode_meme_json - $decode_meme_pb) * 100 / $decode_meme_pb,
'decode_time_js' => $decode_time_json,
'decode_time_pb' => $decode_time_pb,
'decode_time_p' => ($decode_time_json - $decode_time_pb) * 100 / $decode_time_pb,
);
}
/**
* 构造数据
* @param $count
*/
private function _get_data($count){
$this->test_data['aaa'] = mt_rand(0, $count);
for($i = 0; $i < $count; $i++){
$this->test_data['bbb'][] = $this->get_str(100);
}
}
/**
* 获得随机字符串
* @param $length
* @return string
*/
private function get_str($length){
$data = '';
$count = count(self::$str);
for($i = 0; $i < $length; $i++){
$data .= self::$str[mt_rand(0, $count - 1)];
}
return $data;
}
}
$s = new SerializeTest();
$s->run();
执行测试脚本
php index.php
输出结果
Array
(
[0] => Array
(
[length_js] => 176.8730469
[length_pb] => 130.8339844
[length_p] => 35.188917245877
[encode_mem_js] => 181280
[encode_mem_pb] => 134024
[encode_mem_p] => 35.259356533158
[encode_time_js] => 0.0026960372924805
[encode_time_pb] => 0.00014805793762207
[encode_time_p] => 1720.9339774557
[decode_mem_js] => 297256
[decode_mem_pb] => 296552
[decode_mem_p] => 0.23739512800453
[decode_time_js] => 0.0033869743347168
[decode_time_pb] => 0.00016999244689941
[decode_time_p] => 1892.4263674614
)
[1] => Array
(
[length_js] => 706.4111328
[length_pb] => 522.8535156
[length_p] => 35.106891648105
[encode_mem_js] => 723528
[encode_mem_pb] => 535440
[encode_mem_p] => 35.127745405648
[encode_time_js] => 0.010690927505493
[encode_time_pb] => 0.00066089630126953
[encode_time_p] => 1517.6406926407
[decode_mem_js] => 1186200
[decode_mem_pb] => 1185464
[decode_mem_p] => 0.062085394410965
[decode_time_js] => 0.013932943344116
[decode_time_pb] => 0.00093817710876465
[decode_time_p] => 1385.1080050826
)
[2] => Array
(
[length_js] => 6002.6904297
[length_pb] => 4443.3632813
[length_p] => 35.093397718851
[encode_mem_js] => 6146920
[encode_mem_pb] => 4550064
[encode_mem_p] => 35.095242616368
[encode_time_js] => 0.089380025863647
[encode_time_pb] => 0.0054750442504883
[encode_time_p] => 1532.4986936074
[decode_mem_js] => 10321456
[decode_mem_pb] => 10320736
[decode_mem_p] => 0.0069762466552773
[decode_time_js] => 0.11988496780396
[decode_time_pb] => 0.0090479850769043
[decode_time_p] => 1224.9907773386
)
[3] => Array
(
[length_js] => 23659.4199219
[length_pb] => 17512.7265625
[length_p] => 35.098437341915
[encode_mem_js] => 24227408
[encode_mem_pb] => 17933072
[encode_mem_p] => 35.099039361466
[encode_time_js] => 0.37850999832153
[encode_time_pb] => 0.025068044662476
[encode_time_p] => 1409.9302854208
[decode_mem_js] => 40707576
[decode_mem_pb] => 40706992
[decode_mem_p] => 0.0014346429723916
[decode_time_js] => 0.47448205947876
[decode_time_pb] => 0.051760911941528
[decode_time_p] => 816.68025481228
)
[4] => Array
(
[length_js] => 76635.7978516
[length_pb] => 56723.3193359
[length_p] => 35.104572068118
[encode_mem_js] => 78475224
[encode_mem_pb] => 58084816
[encode_mem_p] => 35.104540918232
[encode_time_js] => 1.1786558628082
[encode_time_pb] => 0.0988929271698
[encode_time_p] => 1091.850516048
[decode_mem_js] => 129246120
[decode_mem_pb] => 129245304
[decode_mem_p] => 0.00063135756174166
[decode_time_js] => 1.509269952774
[decode_time_pb] => 0.13635492324829
[decode_time_p] => 1006.8686900478
)
)
制作报表 https://docs.google.com/spreadsheets/d/1kU0HpUvcG-C6JK8GU-zKSxjKCewYt94lsRqNftaEfME/edit?usp=sharing
-
报表
结论
protobuf比json在执行时间上是最大的收益,至少一个数量级;
其次是数据包的大小,protobuf比json少三分之二;
序列化的内存消耗protobuf是json的三分之一;
protobuf比json略麻烦一些,可以通过自动化脚本优化。
深入了解原理请访问
http://www.jianshu.com/p/ec39f79c0412