无限画布压力测试实战:从60FPS到6FPS的完整复盘
2026/9/12 18:18:01 网站建设 项目流程

花了72小时跑完一轮无限画布压力测试,盯着最后生成的曲线图,我半天没说话。倒不是被测程序崩溃了——它没崩,只是数据让我心里发凉:从5000节点到80000节点,帧率从60 FPS一路跌到个位数,内存从480MB飙到接近5GB。作为做白板类产品的前端,这种衰减曲线意味着什么,你我都清楚。这篇是这72小时的完整复盘,测试设计、工具选型、每个用例的数据结果、踩过的坑,全部摊开。给打算做无限画布或正被“画布卡顿”折磨的开发者一份可以直接抄的压测参考。

1. 无限画布压力测试到底在测什么

1.1 无限画布不是普通页面

很多人想做一个类似libtv风格的无限画布,就是那种可以无限平移、缩放、叠元素的画布工具。Demo阶段真的很爽,画布无限大,随手拖,随手画。但真正跑完压力测试后你会发现,“无限”两个字在产品演示里是卖点,在工程实现里是复杂度放大器。

无限画布和普通网页的交互模型完全不同。普通网页是一维流式布局,加载完就展示,用户滚动浏览;无限画布是二维平面,用户能任意平移、缩放,核心特点是“永远有看不见的内容,也永远有新内容要被看见”。从技术栈看,无限画布要在四个层面持续工作:

  • 数据层:维护大量元素的增删改查
  • 空间索引层:快速筛出可视区域的元素
  • 渲染层:把可视元素绘制到屏幕
  • 交互层:处理平移、缩放、框选等手势

压力测试真正要压的,就是这四层协同工作时的性能。很多人误以为只要把绘制函数写快一点就行,实际上四层里任何一层出现瓶颈,整个交互都会卡死。

1.2 压测要回答的三个核心问题

测试开始前,我给自己定了三个必须答出的问题。

第一,数据量到达什么量级时,交互开始明显不跟手?这个量级就是产品的“体验红线”。第二,性能瓶颈是在渲染层还是数据结构?如果渲染层,优化绘制代码;如果数据结构层,得改算法,甚至引入Web Worker把计算挪出主线程。第三,长时运行会不会内存泄漏?白板类产品用户一开就是半天,内存稳定性和初始化性能一样重要。

这三个问题直接决定了测试场景怎么设计、指标怎么选、数据怎么读。后面整个72小时的测试,都是围绕这三个问题展开的。如果你也想做一轮有效的压测,建议先像我一样把问题写下来,不然很容易变成“跑了一堆数据,最后不知道要说明什么”。

1.3 我的压测对象与边界

我用来测试的是一个自研的无限画布demo,基于Canvas API绘制的节点编辑器,支持框选、缩放、拖拽,数据结构用四叉树做空间索引,序列化走JSON。为什么不用现成框架?为了控制变量。测试的目的是验证无限画布通用机制的极限,而不是某个框架的bug。当然,如果你的项目用的是成熟方案,这套压测方法论一样适用,只需要替换被测对象。

2. 三层压测体系:JMeter、Playwright、自研脚本各管一段

2.1 工具选型:为什么不能只用JMeter

很多团队一说压力测试就想到JMeter,这没有错,但必须说清楚边界:JMeter模拟的是“很多人同时发HTTP请求”,它测的是后端接口的并发承受能力。对无限画布而言,真正影响用户体验的往往是浏览器端的渲染性能,而JMeter碰不到浏览器。

所以我把压测拆成三层:

  • 接口层:JMeter扫后端,模拟大量用户并发保存、加载画布,验证后端不会先挂
  • 渲染层:Playwright驱动真实浏览器,往画布里塞不同规模的数据,记录帧率、内存、CPU
  • 数据层:自研Node脚本生成超大JSON,测试序列化、解析、传输耗时

另外提醒一句,不同系统的压测套路差别很大,拿压测人脸识别系统那套请求脚本来套画布产品,基本会测偏。还有人和我说是不是要上CC压力测试那种思路——持续高并发施压这个思路可以参考,但我们要做的是正规的性能容量评估,目的是定位瓶颈、评估容量,方法和工具都得选对。

2.2 JMeter接口层压测怎么做

JMeter这边的目标很清晰:验证后端在大量用户同时保存/加载画布时不会挂掉。

第一步,搭好数据和环境。我准备了50个虚拟用户,每个用户有独立的画布数据,用CSV Data Set Config做参数化,避免所有请求都压在同一个账号和同一张画布上。

第二步,写测试计划。线程组里放两个主要请求:保存画布(POST)和获取画布详情(GET),循环次数根据场景来,比如连续压5分钟,观察TPS和响应时间曲线。

第三步,处理登录态。无限画布项目一般要求登录后才有权限操作,所以JMeter脚本里要先有一个登录请求,从响应里提取Token,再通过HTTP Header Manager带到后续请求。登录响应长这样:

{"token": "abc123"}

在登录请求下面加一个JSON Extractor,提取token变量,然后新建HTTP Header Manager,添加Authorization头,值填${token}。注意Token过期时间,压测一般10-20分钟内完成,如果中途开始报401,多半是Token过期,重登一次就行。

第四步,结果落盘。我通常不在图形界面挂“聚合报告”跑大并发,而是把原始结果写CSV,跑完再用Python或Excel统计。JMeter自身的图形界面在大量采样时也会消耗内存,别让它成为压测瓶颈。

2.3 Playwright渲染层压测怎么做

渲染层压测我用Playwright,它能真实控制Chromium,还能拿到Performance API的数据。脚本核心逻辑不复杂:

  1. 打开画布页面
  2. 用page.evaluate往画布数据源一次性塞入N个节点
  3. 触发完整的视图缩放动画(从100%缩到20%,再放回100%)
  4. 录制动图期间的FPS数据
  5. 读取performance.memory与performance.now数据
  6. 重复拖动和缩放各10轮,记录平均值

这里有个关键细节:一定要用有头模式跑,不要图省事开headless。我前几轮测试用headless,结果FPS虚高了大约20%,后来改成有头模式数据才可信。无头模式的渲染路径和真实用户有差异,压测结果只能当参考。

2.4 数据层压测与指标记录

数据层压测主要回答一个问题:一个80000节点的画布,初始化到底要传输多少数据、解析多久。我用Node脚本生成不同量级的JSON文件,统计文件大小、JSON.parse耗时和内存占用。

结果很明显:80000个节点光JSON就有18MB,主线程解析要2.3秒,这还没算渲染。这个数据给产品设计的启示是:大画布必须做数据分片或者分层加载,不能一股脑把全量数据传给前端。

三层压测的数据最终要汇总到同一张表里。我统一用三类指标衡量:

  • 吞吐类:接口TPS、请求响应时间
  • 渲染类:FPS、掉帧率、主线程长任务次数
  • 资源类:JS堆内存、总内存、CPU占用率

我在测试机上用脚本每5秒采集一次系统级CPU和内存,结合浏览器Performance数据,能区分“卡顿来自浏览器还是系统”。

3. 72小时实测记录:从60 FPS到6 FPS的沉默曲线

3.1 测试环境与基线准备

先说环境。我用了三台配置差异很大的机器:

  • M1 Pro MacBook Pro,16GB内存:主力测试机
  • i5-12400F + RTX 3060 + 32GB的Windows台式机:中高配对照组
  • i5-8250U + 8GB内存的老办公本:低端对照组

三台机器都装最新版Chrome,关掉不必要的后台进程。测试数据放在NVMe固态硬盘上,顺序读取速度3000MB/s以上。这里专门说磁盘,是因为压测时要加载几百MB的测试数据,如果用了机械硬盘或SATA SSD,光读文件就会拖慢进度,形成假瓶颈。这不是画布本身的问题,是测试环境引入的噪音。所以跑压测前,先确认磁盘不是短板。

浏览器方面,我做了一组“关闭硬件加速”的对照测试。为什么做这个?因为真实用户里有人因GPU驱动问题被迫关闭硬件加速,这部分人的体验我们也要覆盖。

3.2 海量元素渲染测试:核心数据

这是整轮压测最重要的数据,我按节点数分档记录。

节点数加载耗时拖动平均FPS缩放平均FPS掉帧率内存占用
5,0000.4s60600.5%480MB
10,0000.8s57502.1%780MB
20,0001.8s48358.7%1.2GB
40,0004.6s281819.4%2.8GB
80,00011.2s12642.3%4.9GB

看到这张表我沉默了。让我沉默的原因不是“数量多了会卡”这种常识,而是性能衰减的形态:数据量翻倍,FPS不是线性下降,而是接近指数下滑;内存增长也不是线性的,从40000节点到80000节点,内存翻了近一倍。这说明算法或数据结构里有隐藏的O(n^2)级别开销。

我还做了一个相对少见的测量:缩放过程中统计函数调用分布。发现卡顿最严重的阶段是缩放到最小级别时,因为画布要把所有节点显示在视口里,空间索引查询范围变大,加上渲染层要遍历大量节点做简化绘制,主线程在短时间内处理了海量任务。

3.3 密集交互压力测试:操作延迟与CPU负载

静态渲染测完,我开始模拟真实用户。脚本每2秒做一次随机操作:框选、拖动、缩放、删除节点,顺序完全随机,持续10分钟。

结果,在20000节点时已经出现明显操作延迟。框选操作从按下到出现选区的平均耗时230ms,用户能感知的阈值一般是100ms,230ms已经属于“明显不跟手”。

更值得注意的是CPU占用。持续操作十分钟后,主线程CPU占用稳定在70%以上,加上GC频繁触发,从火焰图看GC时间片占总时长约18%。如果拿R23这类知名CPU压力测试工具做类比,极限操作下的客户端CPU负载完全不亚于跑一遍渲染评测。当然,实际业务负载不可能像压测脚本这么极端,但这个实验告诉我们:无限画布在极端情况下已经逼近普通设备的算力上限。

3.4 长时稳定性测试:内存泄漏与DOM泄漏

最后一项是长时稳定性。测试机A保持画布打开,每隔30秒执行一次随机平移或缩放,持续2小时,每5秒记录一次内存。

结果:前30分钟内存从800MB缓慢涨到1.3GB,之后曲线逐渐平稳,2小时后停在2.1GB。这个结果说明我的demo没有严重的内存泄漏,但存在“内存只进不出”的隐患——撤销/重做历史栈里保存的节点快照一直占据内存。真实产品里如果用户做上百次撤销,内存压力会更大。

另一个发现来自DOM节点数量监控。虽然是Canvas渲染,但我的一些悬浮提示、右键菜单是DOM元素,长时间操作后DOM节点从最初的200多个涨到900多个,明显有节点未被正确销毁。这类问题用肉眼看不出,监控DOM节点数是个很有效的手段。

4. 瓶颈定位、优化实验与排查技巧

4.1 三组对照实验:找出真正的性能瓶颈

测试只能定位“哪里不行”,要回答“为什么不行”,还得做对照实验。我做了三组。

实验一:取消视口裁剪,全量渲染所有节点。结果20000个节点时帧率已经跌破5FPS,几乎死机。这个实验证明视口裁剪是无限画布不可省的基础能力,但仅靠它在大节点数下会崩溃。

实验二:LOD分级渲染。把节点按缩放级别分三档:100%以上显示完整图形,50%-100%显示简化边框,50%以下只显示点位。结果40000个节点的缩放FPS从18提升到41,掉帧率从19.4%降到6.2%。这说明“渲染得更少”比“渲染得更快”更有效。

实验三:从DOM+Canvas混合改为纯Canvas渲染,同时把事件委托到顶层,避免在大量DOM上绑定监听器。结果80000节点的平均FPS从12提升到29,内存从4.9GB降到2.1GB。这个提升主要是减少了DOM开销和事件监听器的内存占用。

三组实验指向同一个结论:无限画布的性能天花板由数据结构加渲染策略决定,而不是单个绘制函数的效率。

4.2 高频问题排查速查表

结合这次压测和过去几个项目的经验,我整理了一份排查表,遇到问题可以直接对照。

现象常见根因排查方法推荐解法
加载大数据JSON时页面冻结JSON.parse阻塞主线程Performance面板看Main线程Web Worker解析 / 分段加载
拖动时内存持续上涨元素对象未清理,Canvas缓存未释放监控JS Heap与DOM节点数对象池 / 清空离屏Canvas
缩放到最小级别卡顿空间索引查询范围爆炸火焰图看查询函数耗时限制最小缩放级别 / 瓦片化索引
缩放后文字图形模糊位图缩放而非矢量重绘对比缩放下截图LOD分级,高倍率下重绘矢量
JMeter压测大量请求超时后端连接池太小查看后端线程池、数据库连接池调大连接池并引入缓存
Playwright无头模式结果虚高headless渲染路径简化和有头模式结果对比压测用有头模式
长时间操作后DOM节点暴涨弹层/提示未销毁定期打印DOM总数统一命令式弹层并显式销毁

4.3 火焰图、内存快照与性能数据采集的地道姿势

最后分享几个数据采集的实操心得。

火焰图一定要录。我习惯在Performance面板里录10秒操作,重点看三个指标:长任务占比、GC时间占比、事件处理器耗时。很多时候卡顿的真凶不是业务代码,而是GC。GC时间占比超过15%就要考虑引入对象池。

内存分析要配合快照对比。Chrome的Heap Snapshot可以对比两次快照,找出“无法回收的对象”是哪个构造函数创建的。我用这个方法抓过隐藏bug:某个事件处理函数在每次滚动时都会创建新闭包对象,导致内存缓慢上涨,快照对比后一眼就看出来了。

采集数据别全靠浏览器面板。我写了个小脚本,通过CDP每秒读一次performance.memory和系统资源数据,输出CSV,跑完用Python画曲线。好处是全程不用人工盯面板,数据后处理也方便。

最后,测试过程中的原始文件一定要归档。JMeter的结果CSV、Playwright的trace、性能日志,按测试场景命名存好。复盘时会发现这些原始数据比总结报告值钱得多,后续优化完还可以拿来做前后对比。

一直到这里,整轮压测就基本跑完了。如果只让我说一句话总结这72小时的收获,我会说:性能问题的本质是数据结构与渲染策略的设计问题,不是靠几个优化函数就能糊弄过去的。我最大的建议是,如果你正在做无限画布类产品,别等用户投诉卡顿再救火,把压力测试前置到架构选型阶段,先确定你这条产品线的“体验红线”是多少节点,再用测试数据反推技术方案。跑压测很枯燥,但沉淀下来的数据,会是你后续迭代里最硬的底气。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询