使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器

在 PHP 生态系统中,目前有两个连接器可与 Tarantool 服务器配合使用 - 这是官方的 PECL 扩展 tarantool/tarantool-php,用 C 语言编写,并且 tarantool-php/客户端,用 PHP 编写。 我是后者的作者。

在本文中,我想分享这两个库的性能测试结果,并展示如何通过对代码进行最小的更改,实现 3-5 的性能提升(在综合测试中!).

我们将测试什么?

我们将测试上面提到的那些 同步的 异步、并行和异步并行运行的连接器。 🙂 我们也不想触及连接器本身的代码。 目前有几个扩展可以实现您想要的:

  • 斯沃勒 ― PHP 的高性能异步框架。 被阿里巴巴、百度等互联网巨头使用。 从4.1.0版本开始出现了一个神奇的方法 SwooleRuntime::enableCoroutine(),它允许您“用一行代码将同步 PHP 网络库转换为异步网络库”。
  • 直到最近,Async 仍然是 PHP 中异步工作的一个非常有前途的扩展。 为什么直到最近? 不幸的是,由于我不知道的原因,作者删除了存储库,并且该项目的未来命运尚不清楚。 我得用它 来自叉子。 与 Swoole 一样,此扩展允许您轻弹手腕即可轻松打开裤子,通过用异步版本替换 TCP 和 TLS 流的标准实现来实现异步。 这是通过选项“异步 TCP = 1«。
  • 并行 ——来自著名的 Joe Watkins 的一个相当新的扩展,他是 phpdbg、apcu、pthreads、pcov、uopz 等库的作者。 该扩展为 PHP 中的多线程提供了 API,并定位为 pthread 的替代品。 该库的一个重要限制是它仅适用于 ZTS(Zend Thread Safe)版本的 PHP。

我们将如何测试?

让我们启动一个禁用预写日志记录的 Tarantool 实例(wal_mode = 无)和增加的网络缓冲区(预读 = 1 * 1024 * 1024)。 第一个选项将消除对磁盘的工作,第二个选项将可以从操作系统缓冲区读取更多请求,从而最大限度地减少系统调用的数量。

对于处理数据(插入、删除、读取等)的基准测试,在启动基准测试之前,将(重新)创建一个 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, "tuplе_<id>"}

使用随机键值访问元组。

基准测试本身是对服务器的单个请求,该请求被执行 10,000 次(循环),而这些请求又以迭代方式执行。 重复迭代,直到 5 次迭代之间的所有时间偏差都在 3%* 的可接受误差范围内。 此后,取平均结果。 迭代之间有 1 秒的暂停,以防止处理器限流。 Lua 的垃圾收集器在每次迭代之前被禁用,并在完成后强制启动。 仅使用基准测试所需的扩展启动 PHP 进程,并启用输出缓冲并禁用垃圾收集器。

* 转数、迭代次数和误差阈值可以在基准设置中更改。

测试环境

下面发布的结果是在 MacBookPro (2015)、操作系统 - Fedora 30(内核版本 5.3.8-200.fc30.x86_64)上得出的。 Tarantool 在 docker 中启动,参数为“--network host".

封装版本:

Tarantool: 2.3.0-115-g5ba5ed37e
Docker:19.03.3,构建 a872fc2f86
PHP:7.3.11(cli)(构建时间:22 年 2019 月 08 日 11:04:XNUMX)
塔兰工具/客户端:0.6.0
rybakit/msgpack:0.6.1
ext-tarantool:0.3.2(+ 7.3 的补丁)*
外部消息包:2.0.3
外部异步:0.3.0-8c1da46
ext-swoole:4.4.12
外部并行:1.1.3

* 不幸的是,官方连接器不适用于 PHP 版本 > 7.2。 要在 PHP 7.3 上编译并运行扩展,我必须使用 修补.

结果

同步模式

Tarantool协议使用二进制格式 消息包 序列化消息。 在PECL连接器中,序列化隐藏在库的深处,并影响用户级代码的编码过程 不可能。 相反,纯 PHP 连接器提供了通过扩展标准编码器或使用您自己的实现来自定义编码过程的能力。 有两种开箱即用的编码器,一种基于 msgpack/msgpack-php (官方MessagePack PECL扩展),另一个是on rybakit/msgpack (纯 PHP)。

在比较连接器之前,我们将测量 PHP 连接器的 MessagePack 编码器的性能,在进一步的测试中,我们将使用显示最佳结果的编码器:

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器
虽然PHP版本(Pure)在速度上不如PECL扩展,但在实际项目中我仍然推荐使用它 rybakit/msgpack,因为在官方 MessagePack 扩展中,格式规范仅部分实现(例如,不支持自定义数据类型,否则您将无法使用 Decimal - Tarantool 2.3 中引入的新数据类型)并且具有其他人数 问题 (包括与 PHP 7.4 的兼容性问题)。 嗯,总的来说,这个项目看起来被遗弃了。

那么,让我们在同步模式下测量连接器的性能:

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器
从图中可以看出,与 PHP 连接器(Client)相比,PECL 连接器(Tarantool)显示出更好的性能。 但这并不奇怪,因为后者除了以较慢的语言实现之外,实际上还做了更多的工作:每次调用都会创建一个新对象 请求 и 响应 (在选择的情况下 - 也 标准,并且在更新/更新插入的情况下 ― 运营),单独的实体 连系 :, 包装机 и 处理程序 它们还增加了开销。 显然,灵活性是有代价的。 不过,总的来说,PHP 解释器表现出了良好的性能,尽管存在差异,但差异不大,而且在 PHP 7.4 中使用预加载时,性能可能会更差,更不用说 PHP 8 中的 JIT 了。

让我们继续。 Tarantool 2.0 引入了 SQL 支持。 让我们尝试使用 SQL 协议执行选择、插入、更新和删除操作,并将结果与​​ noSQL(二进制)等效项进行比较:

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器
SQL 结果不是很令人印象深刻(让我提醒您,我们仍在测试同步模式)。 不过,我不会提前对此感到不安;SQL 支持仍在积极开发中(例如,最近添加了支持) 准备好的报表)并且,从列表来看 问题未来SQL引擎还会进行多项优化。

异步

好吧,现在让我们看看 Async 扩展如何帮助我们改进上面的结果。 为了编写异步程序,该扩展提供了一个基于协程的 API,我们将使用它。 我们根据经验发现,我们环境的最佳协程数量是 25:

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器
在 10,000 个协程中“分散”25 个操作,看看会发生什么:

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器
每秒操作次数增加3倍以上 tarantool-php/客户端!

遗憾的是,PECL 连接器并未以 ext-async 启动。

那么 SQL 呢?

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器
正如您所看到的,在异步模式下,二进制协议和 SQL 之间的差异已在误差范围内。

斯沃勒

我们再次找出最佳协程数量,这次是 Swoole:
使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器
让我们在 25 处停止。让我们重复与异步扩展相同的技巧 - 在 10,000 个协程之间分配 25 个操作。 此外,我们将添加另一个测试,其中我们将所有工作分为 2 个两个进程(即每个进程将在 5,000 个协程中执行 25 个操作)。 流程将使用以下方式创建 Swoole进程.

结果:

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器
在一个进程中运行时,与 Async 相比,Swole 显示的结果稍低,但在 2 个进程中,情况发生了巨大变化(数字 2 不是偶然选择的;在我的机器上,是 2 个进程显示了最佳结果)。

顺便说一句,异步扩展也有一个用于处理进程的 API,但我没有注意到在一个或多个进程中运行基准测试有任何区别(我可能在某个地方搞砸了)。

SQL 与二进制协议:

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器
与异步一样,异步模式下消除了二进制操作和 SQL 操作之间的差异。

并行

由于并行扩展不是关于协程,而是关于线程,让我们测量并行线程的最佳数量:

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器
在我的机器上它等于 16。 让我们在 16 个并行线程上运行连接器基准测试:

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器
正如您所看到的,结果甚至比异步扩展更好(不包括在 2 个进程上运行的 Swoole)。 请注意,对于 PECL 连接器,更新和更新插入操作为空。 这是因为这些操作因错误而失败 - 我不知道这是 ext-parallel、ext-tarantool 的错误,还是两者兼而有之。

现在我们来比较一下 SQL 性能:

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器
注意到与同步运行连接器的图表相似吗?

一起

最后,让我们将所有结果总结在一张图表中,以查看测试扩展的整体情况。 让我们向图表中添加一项尚未完成的新测试 - 让我们使用 Parallel* 并行运行异步协程。 整合以上扩展的想法已经 讨论 作者,但没有达成共识,你必须自己做。

* 无法使用 Parallel 启动 Swoole 协程;看来这些扩展不兼容。

所以,最终结果:

使用 Async、Swoole 和 Parallel 加速 Tarantool 的 PHP 连接器

取而代之的是结论

在我看来,结果是相当值得的,并且出于某种原因,我确信这不是极限! 无论您是否需要在实际项目中为自己做出决定,我只想说,对我来说,这是一个有趣的实验,它允许您评估可以以最小的努力从同步 TCP 连接器中“挤出”多少内容。 如果您有改进基准的想法,我将很乐意考虑您的拉取请求。 所有包含启动说明和结果的代码均发布在单独的 储存库.

来源: habr.com

添加评论