Logo
Section 1 / 2

添加基准测试 - 第一部分

学习如何添加基准测试来比较纯函数和非纯函数的实现。

导入

添加基准测试 - 第一部分

学习如何添加基准测试来比较纯函数和非纯函数的实现。

设置

现在我们的实现已经就位,可以开始进行对比了。我们将首先实现一些基准测试,以评估两种实现的性能。

请注意,基准测试与负载测试不同。后者旨在将系统或应用推向极限,而前者则是为了在特定条件下收集性能统计数据。

有许多应用程序可用于执行负载测试和基准测试。对于基准测试,Apache JMeter 是一个不错的起点。

启动使用非常简单。文档中提到:“真正的工作应仅使用命令行应用程序,而用图形界面来创建和测试基准。”

我们将跳过对 JMeter 的冗长介绍和教程,因为其官方文档中已有大量相关内容,网上也有许多教程可供参考。

系统环境

服务端和测试软件(Apache JMeter)运行在通过 100 MBit/s 网络连接的不同工作站上。

服务端工作站

  • CPU:Core i5-9600K,6 核,3.7 GHz
  • 内存:32 GB
  • 硬盘:2 块三星 SSD 860 PRO 512GB,SATA 接口
  • 操作系统:FreeBSD 12(禁用超线程)
  • JDK:11.0.4+11-2
  • 数据库:PostgreSQL 11.3

客户端工作站

  • CPU:AMD Ryzen Threadripper 2950X
  • 内存:32 GB
  • 硬盘:2 块三星 SSD 970 PRO 512GB,M.2 接口
  • 操作系统:FreeBSD 12(禁用超线程)
  • JDK:11.0.4+11-2

使用 Apache JMeter 版本 5.1.1 进行基准测试,每个测试使用 10 个线程,每轮测试的预热时间为 10 秒。

指标

我们将采用三次基准测试结果的平均值作为最终指标。以下为表格和图例中使用的缩写:

  • AVG:响应时间的平均值(毫秒)
  • MED:响应时间的中位数(毫秒)
  • 90%:90% 的请求在指定毫秒数内完成响应
  • 95%:95% 的请求在指定毫秒数内完成响应
  • 99%:99% 的请求在指定毫秒数内完成响应
  • MIN:最小响应时间(毫秒)
  • MAX:最大响应时间(毫秒)
  • ERR:错误率(百分比)
  • R/S:每秒可处理的请求数
  • MEM:服务在基准测试期间占用的最大内存(MB)
  • LD:服务机器在测试期间的平均系统负载

创建 100,000 个产品

指标不纯实现纯实现
AVG (ms)9812
MED (ms)9511
90% (ms)12915
95% (ms)14318
99% (ms)17230
MIN (ms)535
MAX (ms)1288675
ERR (%)0%0%
R/S (每秒请求数)100765.33
MEM (MB)11581308
LD169

人们普遍认为不纯实现虽然不够干净,但速度一定更快。然而,实际情况似乎正好相反。

我们逐项分析一下。

最引人注目的是,纯实现的速度大约是不纯实现的七倍!不纯实现平均每秒处理约 100 个请求,而纯实现则达到每秒 765.33 个请求。

<chart config={{ "type": "bar", "data": { "labels": [ "R/S" ], "datasets": [ { "label": "纯实现", "yAxisID": "y-axis-1", "backgroundColor": "rgb(255, 99, 132)", "borderColor": "rgb(255, 99, 132)", "data": [ "765.33" ] }, { "label": "不纯实现", "yAxisID": "y-axis-1", "data": [ "99.9999999" ] } ] }, "options": { "responsive": true, "title": { "display": true, "text": "每秒请求数" }, "tooltips": { "mode": "index", "intersect": true }, "scales": { "yAxes": [ { "type": "linear", "display": true, "position": "left", "id": "y-axis-1" } ] } } }}>

此外,响应时间的各项指标也支持这一结论。在内存使用方面,纯实现比不纯实现多消耗约 13% 的内存。

<chart config={{ "type": "bar", "data": { "labels": [ "内存使用量" ], "datasets": [ { "label": "纯实现", "yAxisID": "y-axis-1", "backgroundColor": "rgb(255, 99, 132)", "borderColor": "rgb(255, 99, 132)", "data": [ "1308" ] }, { "label": "不纯实现", "yAxisID": "y-axis-1", "data": [ "1158" ] } ] }, "options": { "responsive": true, "title": { "display": true, "text": "内存使用量基准测试" }, "tooltips": { "mode": "index", "intersect": true }, "scales": { "yAxes": [ { "ticks": { "beginAtZero": true, "steps": 100 }, "type": "linear", "display": true, "position": "left", "id": "y-axis-1" } ] } } }}>

在当前内存成本低廉的时代,为获得显著的性能提升而多付出约 13% 的内存开销,是完全可以接受的。

另一个值得注意的点是,不纯实现的平均系统负载明显更高(接近两倍)。虽然合理利用资源没有问题,但较低的资源利用率能为其他任务(如操作系统、数据库等)留出更多“缓冲空间”。

加载 100,000 个产品

指标不纯实现纯实现
AVG (ms)77
MED (ms)87
90% (ms)109
95% (ms)1110
99% (ms)1419
MIN (ms)42
MAX (ms)347118
ERR (%)0%0%
R/S (每秒请求数)1162.201248.63
MEM (MB)14491538
LD138

加载基准测试的结果更为均衡。尽管纯实现仍略胜一筹(约快 7%),但其内存使用量比不纯实现高出约 6%。

<chart config={{ "type": "bar", "data": { "labels": [ "内存使用量" ], "datasets": [ { "label": "纯实现", "yAxisID": "y-axis-1", "backgroundColor": "rgb(255, 99, 132)", "borderColor": "rgb(255, 99, 132)", "data": [ "1538" ] }, { "label": "不纯实现", "yAxisID": "y-axis-1", "data": [ "1449" ] } ] }, "options": { "responsive": true, "title": { "display": true, "text": "内存使用量基准测试" }, "tooltips": { "mode": "index", "intersect": true }, "scales": { "yAxes": [ { "ticks": { "beginAtZero": true, "steps": 100 }, "type": "linear", "display": true, "position": "left", "id": "y-axis-1" } ] } } }}>

总体来看,两种实现的结果非常接近。但再次说明,纯实现在系统负载方面表现更优,与首次基准测试一致。

我们将在下一课中继续对剩余接口进行基准测试。

测试纯服务

添加基准测试 - 第二部分