Protocol Buffers 是一种应用广泛的数据交换格式,内部服务间通信时常常用它做为数据传输格式,主要是因为较之 Json 它具有更好的性能(体现在空间占用更少、编解码更快)、数据结构定义更规范。 然而在 PHP 中使用 Protocol Buffers 却是一场灾难。
Protocol Buffers 初始化是很慢的
通过 protoc
编译出来的骨架代码本质上只是将 proto
文件编译成方便解析的中间文件格式,建立 PHP 层对应的类型系统,绝大部分的工作放到了运行时。
参考 php performance problem · Issue #4227 · protocolbuffers/protobuf
使用 XHProf
分析实际的请求,发现 40%
的时间(133 毫秒)花在了初始化上
使用的是 Protocol Buffers 的 PHP 实现,耗时的调用栈如下
PaymentEvent::__construct – GPBMetadata\Xllivensq::initOnce -— Google\Protobuf\Internal\DescriptorPool::internalAddGeneratedFile
C 扩展性能是 PHP 实现的 10
倍以上,主要是因为使用 C 扩展后底层对 Protocol
Buffers 字节流的编、解码更快了。
Protocol Buffers 的 C 扩展不稳定
线上系统使用中发现内存和 CPU 占用过高。
Protocol Buffers 性能问题对 PHP 应用的影响是巨大的
其它语言(如:Go、C++)程序是常驻内存运行的,初始化 Protocol Buffers 的开销只付出一次,所以往往意识不到这个问题。
PHP 采用 FastCGI
模型,每次请求都要承担 Protocol Buffers 的开销,很快就会发现采用 Protocol Buffers 后接口响应时间变长,系统的吞吐率下降。
PHP 实现与 C 扩展的性能对比
-
Protocol Buffers C 扩展的安装
sudo pecl install protobuf-3.7.0
-
Protocol Buffers C 扩展的启用与禁用
在
php.ini
加入以下配置项启用 C 扩展extension=protobuf.so
注释掉上面的配置项禁用 C 扩展。
-
创建性能测试 PHP 项目
进入项目目录
创建
composer.json
文件,内容如下{ "require": { "google/protobuf": "3.7.0" } }
创建
index.php
文件,内容如下<?php $startTime = microtime(true); require __DIR__ . '/vendor/autoload.php'; require __DIR__ . '/generated/GPBMetadata/Addressbook.php'; require __DIR__ . '/generated/Tutorial/AddressBook.php'; require __DIR__ . '/generated/Tutorial/Person.php'; require __DIR__ . '/generated/Tutorial/Person/PhoneType.php'; require __DIR__ . '/generated/Tutorial/Person/PhoneNumber.php'; $addressBook = new Tutorial\AddressBook(); $person = new Tutorial\Person(); $person->setId(1); $person->setName("user1"); $person->setEmail("[email protected]"); $phone = new Tutorial\Person\PhoneNumber(); $phone->setType(Tutorial\Person\PhoneType::HOME); $phone->setNumber("1000001"); $person->getPhones()[] = $phone; $addressBook->getPeople()[] = $person; $person = new Tutorial\Person(); $person->setId(2); $person->setName("user2"); $person->setEmail("[email protected]"); $phone = new Tutorial\Person\PhoneNumber(); $phone->setType(Tutorial\Person\PhoneType::HOME); $phone->setNumber("2000002"); $person->getPhones()[] = $phone; $addressBook->getPeople()[] = $person; $person = new Tutorial\Person(); $person->setId(3); $person->setName("user3"); $person->setEmail("[email protected]"); $phone = new Tutorial\Person\PhoneNumber(); $phone->setType(Tutorial\Person\PhoneType::HOME); $phone->setNumber("3000003"); $person->getPhones()[] = $phone; $addressBook->getPeople()[] = $person; $data = $addressBook->serializeToString(); $endTime = microtime(true); $runTime = ($endTime-$startTime)*1000 . ' ms'; $class = new ReflectionClass('Google\Protobuf\Internal\DescriptorPool'); if ($class->getFileName()) { echo "protobuf-php $runTime\n"; } else { echo "protobuf-c $runTime\n"; }
创建
Makefile
内容如下all: install generate benchmark install: vendor vendor: composer.json composer install generate: generated/GPBMetadata generated/GPBMetadata: addressbook.proto -mkdir generated protoc --php_out=generated addressbook.proto benchmark: php index.php | tee -a ./benchmark.log addressbook.proto: wget https://raw.githubusercontent.com/protocolbuffers/protobuf/master/examples/addressbook.proto -O ${CURDIR}/addressbook.proto clean: -rm -rf ${CURDIR}/vendor ${CURDIR}/generated
-
运行性能测试
make
交替启用禁用 C 扩展的运行结果
protobuf-c 2.335 ms protobuf-php 29.14 ms protobuf-c 2.443 ms protobuf-php 27.962 ms protobuf-c 2.238 ms protobuf-php 14.957 ms protobuf-c 2.221 ms protobuf-php 29.814 ms protobuf-c 2.295 ms protobuf-php 29.304 ms protobuf-c 2.435 ms protobuf-php 37.493 ms
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 如何使用 WebAssembly 提升性能
- 使用延迟加载提升SPA性能
- 使用ab压力命令测试网站性能
- 使用 Traefik 提高 WebSocket 应用性能
- 使用 Traefik 提高 WebSocket 应用性能
- 使用多线程提高rest服务性能
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。