首页
下载
文档
社区
视频
捐赠
源代码
赞助商
AOT 编译器
AI 助理
商业产品
PHP AOT 原生编译器
Swoole-Compiler 代码加密器
CRMEB 新零售社交电商系统
登录
注册
全部
提问
分享
讨论
建议
公告
开发框架
CodeGalaxy
发表新帖
Swoole 4.5.6 支持零拷贝 JSON 或 PHP 反序列化
在最新的`4.5.6`开发分支中,底层增加了`2`个特殊的函数: - `swoole_substr_json_decode` - `swoole_substr_unserialize` 这里为什么要增加这两个函数呢?有这样一种场景。使用`Swoole\Server`实现`RPC`服务,在`EOF`协议或长度协议通信方式下,一个包可能有`3`部分组成。 ```php $packet = $header + $body + $footer ``` 通常`$header`和`$footer`比较小,而`$body`比较大,`$body`可能会使用`JSON`或`PHP`序列化格式。如果要解析`$body`数据,那么就需要先进行`substr`得到`$body`的字符串格式数据,再进行`json_decode`和`unserialize`操作。 这会引起一次内存拷贝,`$body_str = substr($packet, $header_length)`的过程会创建一个临时字符串变量,在反序列化操作`$body = json_decode($body_str)`之后,这个变量就会被释放。 ```php // 先进行 substr,这时会产生内存拷贝,从 $packet 复制数据到 $body_str $body_str = substr($packet, 4, strlen($packet) - 4 - 2); // 反序列化之后 $body_str 这块内存不再使用,会在函数退出时释放 $body = json_decode($body_str, true); ``` 使用新增的两个函数就可以将`substr`和`反序列化`操作合二为一。减少一次内存拷贝,从而提高性能。 ```php $body = swoole_substr_json_decode($packet, $header_length); $body = swoole_substr_unserialize($packet, $header_length); ``` 压测 ---- ```php <?php error_reporting(E_ALL); $a['hello'] = base64_encode(random_bytes(1000)); $a['world'] = 'hello'; $a['int'] = rand(1, 999999); $a['list'] = ['a,', 'b', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx']; $val = serialize($a); $str = pack('N', strlen($val)).$val."\r\n"; $n = 1000000; $s = microtime(true); while($n--) { $l = strlen($str) - 6; $data = unserialize(substr($str, 4, $l)); } echo "unserialize: ".(microtime(true)-$s)."\n"; $s = microtime(true); while($n--) { $l = strlen($str) - 6; $data = swoole_substr_unserialize($str, 4, $l); } echo "swoole_substr_unserialize: ".(microtime(true)-$s)."\n"; ``` 使用`swoole_substr_unserialize`与`substr + unserialize`相比,性能提升了`12%`。 > 测试中使用的数据尺寸只有`1K`多,在实际项目中经常会出现几百`K`的大数据包,性能提升会更显著 ```shell htf@htf-ThinkPad-T470p:~/workspace/debug$ php s.php unserialize: 2.2559139728546 swoole_substr_unserialize: 1.9821600914001 ```
发布于5年前 · 130 次浏览 · 来自
分享
Rango
在最新的`4.5.6`开发分支中,底层增加了`2`个特殊的函数: - `swoole_substr_json_decode` - `swoole_substr_unserialize` 这里为什么要增加这两个函数呢?有这样一种场景。使用`Swoole\Server`实现`RPC`服务,在`EOF`协议或长度协议通信方式下,一个包可能有`3`部分组成。 ```php $packet = $header + $body + $footer ``` 通常`$header`和`$footer`比较小,而`$body`比较大,`$body`可能会使用`JSON`或`PHP`序列化格式。如果要解析`$body`数据,那么就需要先进行`substr`得到`$body`的字符串格式数据,再进行`json_decode`和`unserialize`操作。 这会引起一次内存拷贝,`$body_str = substr($packet, $header_length)`的过程会创建一个临时字符串变量,在反序列化操作`$body = json_decode($body_str)`之后,这个变量就会被释放。 ```php // 先进行 substr,这时会产生内存拷贝,从 $packet 复制数据到 $body_str $body_str = substr($packet, 4, strlen($packet) - 4 - 2); // 反序列化之后 $body_str 这块内存不再使用,会在函数退出时释放 $body = json_decode($body_str, true); ``` 使用新增的两个函数就可以将`substr`和`反序列化`操作合二为一。减少一次内存拷贝,从而提高性能。 ```php $body = swoole_substr_json_decode($packet, $header_length); $body = swoole_substr_unserialize($packet, $header_length); ``` 压测 ---- ```php <?php error_reporting(E_ALL); $a['hello'] = base64_encode(random_bytes(1000)); $a['world'] = 'hello'; $a['int'] = rand(1, 999999); $a['list'] = ['a,', 'b', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx']; $val = serialize($a); $str = pack('N', strlen($val)).$val."\r\n"; $n = 1000000; $s = microtime(true); while($n--) { $l = strlen($str) - 6; $data = unserialize(substr($str, 4, $l)); } echo "unserialize: ".(microtime(true)-$s)."\n"; $s = microtime(true); while($n--) { $l = strlen($str) - 6; $data = swoole_substr_unserialize($str, 4, $l); } echo "swoole_substr_unserialize: ".(microtime(true)-$s)."\n"; ``` 使用`swoole_substr_unserialize`与`substr + unserialize`相比,性能提升了`12%`。 > 测试中使用的数据尺寸只有`1K`多,在实际项目中经常会出现几百`K`的大数据包,性能提升会更显著 ```shell htf@htf-ThinkPad-T470p:~/workspace/debug$ php s.php unserialize: 2.2559139728546 swoole_substr_unserialize: 1.9821600914001 ```
赞
1
收藏
提问
分享
讨论
建议
公告
开发框架
CodeGalaxy
登录
后参与评论
评论
2020-10-23
郭新华
火钳刘明
赞
4
回复
2020-10-27
Reasno
坐等protobuf版
赞
2
回复
2020-10-23
w
weizeng
求零拷贝在底层的实现方案,引用同一个字符数组吗?
赞
0
回复
2020-10-23
Rango
回复
weizeng
核心的原理是 `PHP` 底层提供的 `ZENDAPI` 可以传入`char *`指针,在`$packet`之上做指针偏移即可,你可以直接看代码。
赞
2
回复
2020-10-23
15093******
好厉害的样子
赞
0
回复