
在PHP生态系统中,目前有两个用于Tarantool服务器的连接器:官方PECL扩展用C编写的
tarantool / tarantool-php和用
PHP编写的
tarantool-php / client 。 我是后者的作者。
在本文中,我想分享这两个库的性能测试结果,并展示如何在代码中进行最少的更改就可以实现3到5倍的性能改进(
在综合测试中! )。
我们要测试什么?
我们将测试异步,并行和异步异步启动的上述同步连接器。 另外,我们不希望更改连接器的源代码。 目前,有几个扩展可以完成这项工作:
- Swoole ,PHP的高性能异步框架。 已被阿里巴巴和百度等互联网巨头使用。 从版本4.1.0开始,惊人的运行时挂钩Swoole \ Runtime :: enableCoroutine()出现了,它允许“使用单行代码将同步PHP网络库转换为协同例程库”。
- 异步,直到最近,PHP才是异步工作中非常有前途的扩展。 为什么直到最近? 不幸的是,由于我不知道的原因,作者删除了存储库,该项目的前途令人怀疑。 我将使用其中一个叉子。 像Swoole一样,此扩展通过将PHP的默认流实现替换为它们的异步对应项,可以轻松激活异步模式。 这可以通过选项“ async.tcp = 1 ”完成。
- Parallel是著名的Joe Watkins的新扩展,Joe Watkins是phpdbg,apcu,pthreads,pcov,uopz等库的作者。 该扩展为PHP提供了一个多线程API,并且可以替代pthread。 该库的一个重要限制是它只能与PHP的ZTS(Zend线程安全)版本一起使用。
我们要如何测试?
我们将运行一个禁用了预写日志记录(
wal_mode = none )和扩展的网络缓冲区(
readahead = 1 * 1024 * 1024 )的Tarantool实例。 第一个选项将阻止对磁盘驱动器的IO操作,第二个选项将允许从操作系统缓冲区读取更多请求,从而最大程度地减少系统调用次数。
对于使用数据(插入,删除,读取等)的基准,将在基准开始之前(重新)创建memtx空间,并且该空间的初始索引值将由序列生成器创建。
DDL的空间如下:
space = box.schema.space.create(config.space_name, { id = config.space_id, temporary = true }) space:create_index('primary', { type = 'tree', parts = {1, 'unsigned'}, sequence = true }) space:format({ {name = 'id', type = 'unsigned'}, {name = 'name', type = 'string', is_nullable = false} })
如有必要,在启动基准测试之前,该空间将填充10,000个以下形式的元组:
{id, 'tuple_' .. id}
使用随机键值访问元组。
基准测试是对服务器的单个请求,执行了10,000次(转),然后又以迭代方式执行。 重复迭代,直到5次迭代中的所有时间偏差都在3%误差范围*之内。 之后,取平均结果。 在两次迭代之间,有1秒的暂停以防止CPU节流。 Lua垃圾收集器在每次迭代之前被禁用,并在迭代完成后被强制启动。 仅使用基准测试所需的扩展启动PHP进程,并启用输出缓冲和禁用垃圾收集器。
*转数,迭代次数和错误阈值可以在基准设置中更改。测试环境
以下发布的结果是在MacBookPro(2015年中)和Fedora 30(内核版本5.3.8-200.fc30.x86_64)上完成的。 Tarantool是在docker中使用“
--network host ”设置启动的。
套件版本:Tarantool:2.3.0-115-g5ba5ed37e
码头工人:03/19/3,建立a872fc2f86
PHP:7.3.11(CLI)(建立:2019年10月22日08:11:04)
tarantool /客户端:0.6.0
rybakit / msgpack:0.6.1
ext-tarantool:0.3.2(已修补)*
ext-msgpack:2.0.3
外部异步:0.3.0-8c1da46
ext-swoole:4.4.12
外部并行:1.1.3
*不幸的是,官方连接器不适用于PHP> 7.2。 要在PHP 7.3上编译和运行扩展,我必须使用patch 。结果
同步(默认)
Tarantool协议使用
MessagePack二进制格式来序列化消息。 在PECL连接器中,序列化隐藏在库的深处,因此
似乎不可能影响用户区代码的编码过程。 相反,通过扩展标准编码器之一或使用您自己的实现,纯PHP连接器提供了自定义编码过程的功能。 现成的编码器有两种:一种基于
msgpack / msgpack-php (官方MessagePack PECL扩展),另一种基于
rybakit / msgpack (纯PHP)。
在继续比较连接器之前,让我们测量一下PHP连接器的MessagePack编码器的性能,以便在测试中进一步使用性能最佳的连接器:
虽然PHP版本(Pure)的速度不如PECL扩展快,但我仍建议在实际项目中使用
rybakit / msgpack ,因为正式的PECL扩展仅部分实现了MessagePack规范(例如,不支持自定义数据类型,如果没有它,您将无法使用Decimal-Tarantool 2.3中引入的新数据类型),并且还有许多其他
问题 (包括与PHP 7.4的兼容性问题)。 而且该项目看起来总体上被放弃了。
因此,让我们在同步模式下测量连接器的性能:
从图中可以看出,PECL连接器(Tarantool)的性能优于PHP连接器(Client)。 考虑到后者除了以较慢的语言实现之外,实际上还可以做更多的工作,这不足为奇:每个请求都创建一个新的
Request and
Response对象(在Select的情况下,还有
Criteria ,在在Update / Upsert的情况下,有
Operations ),
Connection ,
Packer和
Handler也增加了一些开销。 不用说,更高的灵活性是有代价的。 但是,PHP解释器通常显示出良好的性能。 尽管有所不同,但微不足道,并且在PHP 7.4中使用预加载可能会变得更少,更不用说PHP 8中的JIT了。
现在继续前进。 Tarantool 2.0引入了SQL支持。 让我们尝试使用SQL协议执行Select,Insert,Update和Delete操作,并将结果与noSQL(二进制)等效项进行比较:
SQL结果并不是那么令人印象深刻(让我提醒您,我们仍在测试同步模式)。 但是,在此之前,我不会感到沮丧:SQL支持仍在积极开发中(例如,不久前就添加了对
预处理语句的支持),并且根据
问题列表,SQL引擎将在将来获得许多优化。
异步
好了,现在让我们看看异步扩展如何能够帮助我们改善上述结果。 对于异步编程,该扩展提供了一个基于协程的API,我们将在这里使用它。 首先,通过测试我们发现,针对我们的环境,协程的最佳数量为25:
然后,我们对25个协程进行了10,000次操作,并检查了结果:
对于PHP连接器,每秒的操作数量已增长了3倍以上! 可悲的是,PECL连接器无法通过ext-async启动。
那SQL呢?
如您所见,在异步模式下,二进制协议和SQL之间的差异落在误差范围之内。
旋风
再次,让我们确定针对Swoole的最佳协同程序数量:
让我们以25为例。现在,重复与Async扩展相同的技巧:在25个协程之间分配10,000个操作。 除此之外,让我们再添加一个测试,将整个过程分为两个过程(即,每个过程将在25个协程中执行5,000个操作)。 这些过程将在
Swoole \ Process的帮助下创建。
结果:
与Async相比,Swoole在一个进程中运行时的性能略低,但是在2个进程中,画面发生了巨大变化(不是偶然选择了2个,在我的机器上,此确切数目的进程显示了最佳结果)。
顺便说一句,异步扩展中也有一个用于处理进程的API,但是我注意到在单个进程或多个进程中启动基准测试之间没有区别(尽管我犯了一些错误)。
SQL与二进制协议:
与异步一样,在异步模式下,二进制操作和SQL操作之间的差异也被消除了。
平行的
由于Parallel扩展是关于线程而不是协程的,因此我们要测量并行线程的最佳数量:
我的机器上是16。 现在让我们对16个并行线程上的连接器进行基准测试:
如您所见,结果甚至比异步扩展更好(除了Swoole带有2个进程启动)。 请注意,对于PECL连接器,Update和Upsert操作没有任何限制。 这是因为这些操作因错误而崩溃,并且我不确定应该归咎于什么:ext-parallel或ext-tarantool或两者。
现在让我们将SQL性能添加到比较中:
您是否注意到同步启动的连接器与图表的相似之处?
多合一
最后,让我们将所有结果组合在一张图中,以查看测试中的扩展的整个图片。 我们将只向图中添加一个新测试,但尚未完成:使用Parallel *并行启动Async协程。 作者已经
讨论了集成上述扩展的想法,但尚未达成共识,因此我们必须自己做。
*我无法使用Parallel启动Swoole协程; 这些扩展似乎不兼容。现在,最终结果为:
结论
在我看来,结果是相当不错的,但是有些事情让我相信我们还没有到那! 如果您有关于如何提高基准的任何想法,我将很乐意审查您的请求请求。 所有带有启动说明和结果的代码都发布在专用的
存储库中 。
由您自己决定是否在实际项目中需要它,我只想说这是一个令人兴奋的实验,它使我能够以最小的努力估算出一个同步TCP连接器可以产生多少。