23

Swoole 4.5.6 支持零拷贝 JSON 或 PHP 反序列化

 3 years ago
source link: https://segmentfault.com/a/1190000037570031
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在最新的 4.5.6 开发分支中,底层增加了 2 个特殊的函数:

swoole_substr_json_decode
swoole_substr_unserialize

这里为什么要增加这两个函数呢?有这样一种场景。使用 Swoole\Server 实现 RPC 服务,在 EOF 协议或长度协议通信方式下,一个包可能有 3 部分组成。

$packet = $header + $body + $footer

通常 $header$footer 比较小,而 $body 比较大, $body 可能会使用 JSONPHP 序列化格式。如果要解析 $body 数据,那么就需要先进行 substr 得到 $body 的字符串格式数据,再进行 json_decodeunserialize 操作。

这会引起一次内存拷贝, $body_str = substr($packet, $header_length) 的过程会创建一个临时字符串变量,再反序列化操作 $body = json_decode($body_str) 之后,这个变量就会被释放。

// 先进行 substr,这时会产生内存拷贝,从 $packet 复制数据到 $body_str
$body_str = substr($packet, 4, strlen($packet) - 4 - 2);
// 反序列化之后 $body_str 这块内存不再使用,会在函数退出时释放
$body = json_decode($body_str, true);

使用新增的两个函数就可以将 substr反序列化 操作合二为一。减少一次内存拷贝,从而提高性能。

$body = swoole_substr_json_decode($packet, $header_length);
$body = swoole_substr_unserialize($packet, $header_length);

压测

<?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."rn";

$n = 100000;

$s = microtime(true);
while($n--) {
    $l = strlen($str) - 6;
    // var_dump(unserialize(substr($str, 4, $l)));
    var_dump(swoole_substr_unserialize($str, 4, $l));
}
echo "cost: ".(microtime(true)-$s)."n";

使用 swoole_substr_unserializesubstr + unserialize 相比,性能提升了 12%

htf@htf-ThinkPad-T470p:~/workspace/debug$ php s.php
cost: 2.2559139728546
htf@htf-ThinkPad-T470p:~/workspace/debug$ php s.php
cost: 1.9821600914001

Vn6ZFfz.jpg!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK