如视 VR 看房性能优化经验总结

壹、背景

贝壳 VR 看房是贝壳找房如视事业部(现已独立,如你所视科技有限公司)做的一款在线 VR 3D 看房服务。通过专业的三维空间扫描设备采集房源户型三维数据,经过算法加工之后,可以通过 WebGL/Three.js 等工具将房源以1:1复刻至浏览器上,并支持720°空间自由行走和模型、全景等多种模态间的自由切换。

尤其是在新冠疫情的影响下,用户可以直接在线上进行 VR 3D 看房,降低筛选、沟通成本。此外,在后续的业务迭代中又引入 VR 带看、VR 经纪人/ AI 讲房、“一键换装”看装修等新业务模式。随着业务复杂度的提升、用户使用群体的覆盖面越来越广,性能问题已经成为项目瓶颈,亟待解决。

1. 现状分析

业务分析

如视 VR 团队是2017年开始成立的,2018年4月份贝壳找房App 首次对外发版,VR 看房属于新品牌的核心亮点。于是从2017年开始近一年的时间内从0-1搭建贝壳VR看房,团队节奏是很紧的——倒排、抢时间。

2018年后半程在贝壳 VR 看房的基础上,又新增 VR 经纪人讲房和 VR 线上实时同步带看业务。

2019年初,引入早期版本的 AI 讲房业务。内部项目“未来家”——即 VR 装修(渲染)技术突破,支持“一键看装修”功能,并支持与实景 VR 同屏对比。

由于2019年末、2020初新冠疫情的影响,VR 线上实时同步带看业务转变为公司级别核心业务。实现 VR 带看二手房、新房、租赁等业务全场景的覆盖,并支持微信小程序(高流量)。

2021年初,则重点投入 AI 讲房业务新的探索——添加算法权重,实现 AR 数字人,往更智能(基于用户画像和性能条件实现“千人千面”体验)、更具空间表达的方向发展。
2021年末至今(2022年7月),团队方向调整,从贝壳找房剥离并成立如你所视科技有限公司。由支撑贝壳找房VR看房转向 SaaS、PaaS 数字空间综合解决方案创业公司。

技术分析

早期为了,架构上基于 jQuery +发布/订阅者模式实现的模块化开发,后期(2020年中)转向分层+基于 React 技术栈实现的动态模块化架构形式,见下图。

前端架构图
图一:前端架构图

2. 优化目标

优化目标很多,本文仅抽取两点(围绕内存、FPS、TTI、进VR带看耗时这四点)进行详细说明:

① 性能满足更多用户诉求,贝壳VR 看房覆盖面更广,不能局限于某些高端设备——提高用户覆盖面
几个关键路径体验 亟待解决,已经阻塞业务发展——比如启动Loading耗时长、VR 带看链路上流失率高等等。

贰、优化经验

前期实际落地时并没有按照 性能优化方法论 来执行(当初也没经验),实际上也因此踩了很多坑,浪费了很多时间、资源——特别是在旧架构体系上和产品策略上做的工作 ROI 极低。

1. 指标体系

1.1 系统指标

房源的VR 3D模型是通过WebView基于前端WebGL能力渲染出的,核心指标有两个:

  • 内存占用(iOS 端直接上报;线上 Android 端无法上报、黑盒,只能通过 PerfDog 线下统计)。
  • 体现流畅度的 FPS 值。

分析分布大致的结论如下:

  • 内存(高崩溃率):一个 VR 占用内存大概300MB,正常情况两个 VR 实例大概700MB内存(最低值区间700MB),但线上平均指标实际是 1.2G——而 iOS 系统崩溃阈值是1.5G左右;Android 系统差异大,无明确阈值。
  • FPS:前11s平均50fps以内,正常55fps以上。是合格值,但是进入 VR 7s 阶段,FPS 降至 40fps 以下,拉低平均值。

1.2 关键路径指标

关键路径指标有很多,这里抽取两个做详细说明:

  • TTI:可交互时间,即从房源详情页点击进入 VR 到 VR 页面渲染完成可交换的耗时。这个过程有 Loading 过程,内部又称为 Loading 耗时长,优化前平均值在7s左右,优化后2s。
  • 点击 VR 带看入口到带看就绪耗时:优化前21s,优化后用户发起端1s内,经纪人端2.5s。

此外,还有跟渲染引擎相关模型渲染、模态切换等指标,由于偏三维领域,本文不展开。本文分别去两个系统指标和关键路径指标进行分析、经验介绍。

2. 摸底分析

2.1 内存

前文提到,一个 VR 占用内存大概300MB,正常情况两个 VR 实例大概700MB 内存,但线上平均指标实际是 1.2G。分析定位后发现:

  • 非 VR 渲染模块:除了 VR 耗资源之外,还有地图(百度/腾讯)、多媒体(小区图集/小区视频/讲房音频等)等模块亦占用内存。
  • RTC 功能:除了渲染模块之外,VR 带看依赖的 RTC 功能(实时语音)也会占用 WebView 进程资源。
  • UI 资源:首面板逐帧动画以及其他过渡动画等。

这些占用内存的模块短期内都是无法省去的,因此性能指标的瓶颈在 1.2G。而且,功能越用越多,内存占用越高,崩溃的概率越高。

2.2 FPS

除了在7s左右 FPS 急剧下降之外,整体 FPS 处在合理值范畴。为啥 7s 左右 FPS 会明显下降呢?主要是这里有个 用计算换降低存储空间成本 的优化——将三角面片数据及 uv 贴图数据压缩后存储,端上使用再解压使用。

2.3 TTI

可交互时间,即从房源详情页点击进入 VR 到 VR 页面渲染完成可交换的耗时。分析后,关键流程如下:

启动 Loading 耗时关键阶段流程图
图二:启动 Loading 耗时关键阶段流程图

从关键流程图来看,到能交互阶段(虽然是部分交互),需要大概7s时间。

Node 计算

  • WHY:户型图敏感数据,不适合暴露在端上计算(比如两点间最短路径)。或无理由,就是写在 Node 层。
  • 调整:计算结果缓存,离线化支持。

浏览器端渲染

  • WHY:全模块渲染,无动态加载。造成 js 臃肿(依赖的 Three.js 库本身就巨)。
  • 调整:需 架构升级、先分层、非首屏内容异步加载或用户触发渲染。

六张图居然要花4s去下载?

  • WHY:由于 JS/CSS/图标等静态资源(前4s大概200多个 HTTP 请求)都在同个CDN域上,浏览器或 WebView 同时只能执行3-5个 HTTP 请求,无法并行请求六张全景图片。
  • 调整:多 CDN 域名 + HTTP2 多路复用支持。

2.4 点击 VR 带看入口到带看就绪耗时

何为VR带看?VR带看是指用户和经纪人(可以多个用户、多个经纪人)打开同个VR 页面,可以实时语音并且交互画面同步,视频效果如下:

VR 带看启动流程

VR 带看启动流程耗时节点流程图
图三:VR 带看启动流程耗时节点流程图

线下分析15s耗时进入带看就绪状态,但线上真实情况却是21s左右。

VR 带看类似于远程视频语音,只不过视频内容换成了 VR 画面同屏。可想而之,从触发到就绪需要21s,这是用户不可接受的,这个业务推广面临极大的困难。

3. 策略调整

3.1 产品策略调整

  • 内存:产品经理将页面拆分为 首屏模块非首屏模块,首屏模块强制渲染,非首屏模块延迟渲染或用户触发加载——旧的前端架构不支持。
  • 点击VR带看入口到带看就绪耗时:
    • 不需要新开启 WebView,直接在原有的 WebView 上执行带看流程——旧的前端架构不支持
    • 就绪重新定义:不需要等 RTC 联通、三维模型渲染就绪才能进入带看;只要 WebSocket 联通就行。
    • 新产品模式:抢单模式,一个用户对应多个线上经纪人/职业顾问,谁先响应客户资源归谁。

3.2 技术架构升级

从产品策略的调整来看,基于 jQuery +发布/订阅者模式实现的增量式模块化开发前端架构已经不满足现有的业务和性能诉求。原有的设计是典型的SPA应用,但是新的架构诉求则更像是一个平台,即架构上分层:数据层、View 层,View 层又细分 DOM 层、Canvas 层、协议层及基础插件层。数据层和 View 层组成基础的首屏内容,非首屏内容则基于这两层以动态模块的形式进行开发——需要时挂载(占内存),不需要时卸载(会延迟清部分内存)。

前端架构分层设计
图四:前端架构分层设计

图四是图一的简化版本,以首屏内容(产品定义)为核心,非首屏内容以动态模块“热插拔”式支持:

  • 数据层:基于 MobX 二次抽象,以React Context <StoreProvider> 形式驱动UI。
  • 协议层:类 jsBridge,实现与客户端通信,保障业务层逻辑通用——App(iOS/Android) 即jsBridge,小程序依托 WebSocket 实现。
  • DOM 层:HTML 标签二维交互。
  • Canvas 层:基于 WebGL 三维模型建模抽象——Three.js 生态及自研渲染引擎。
  • 插件层:以插件的形式进行抽象,实现二维 DOM 和三维 Canvas 混合编程。
  • 动态模块:经纪人/AI 讲房、VR 带看、地图、多媒体资源等——以主副面板等形式集成。

3.3 产品策略和技术架构带来的提升

  • 内存:浅用户(功能使用少的用户,停留时长50s内)崩溃率降低明显;深度用户崩溃率有降低,但是未发生质变。
  • FPS:无直接影响。
  • TTI-Loading 耗时:由于基于首屏渲染,渲染依赖极大减少,平均值降低至3.3s;再加上摸底分析提到的优化,最后能降到到2s左右。
  • 点击VR带看入口到带看就绪耗时:
    • 用户端1s内——得益于不需要新开启 WebView,直接动态载入 VR 带看模块即可。不强依赖 RTC,瓶颈在 WebSocket 连接速度。
    • 经纪人/置业顾问端 3.5s 内,基本跟 TTI-Loading 耗时保持一致。

优化后数值基本都达到预期性能指标,但TTI-Loading耗时和内存溢出问题还是严重影响业务,可以成立专项再深度去治理。

4. 专项治理

经过前面三个阶段之后,基本能做到 ①整体指标大盘稳定②产品策略合理③技术架构无缺陷 ——能考八十分的高分水准。而专项治理则是将八十分往九十分继续提高。

4.1 TTI 指标:Loading 耗时长

虽然已经将Loading 耗时缩减到 3.3s以内了,但是这个过程本身很“膈应”,对业务还是有影响的。更进一步地我们开始思考怎么能把这个过程给去掉,但仅仅局限在 Web 前端的角度我们很难再有所突破。

本着 渐进增强 的原则,由于我们大部分用户是在贝壳/链家App上使用VR看房服务,我们可以重复利用客户端渲染能力。

分析3.3s的瓶颈:

  • 1s HTTP请求至浏览器端渲染(HTML「壳子」/CSS/JS等)。
  • 2s 左右的全景图片请求(六张)。

至此,我们可以基于 WebView 拦截HTTP请求,让客户端提供HTTP请求预载、代理、缓存等能力。静态资源、全景贴图等在房源详情页提前请求,到 WebView 层拦截使用,终于整个流程平均值降到2s内(高端设备已经到1s内)——已经达到一个很好的效果。

都是,Loading 这个过程依旧存在。我们继续深度挖掘客户端能力:客户端浅渲染三维模型——即客户端最小程度渲染三维模型(全景效果),由于资源已经提前预载,客户端渲染速度在300ms内(视终端设备性能来定),然后等 WebView 渲染就绪后再替换成前端渲染。所要做的工作是客户端渲染和前端渲染效果对齐即可。

最终,300ms的延迟肉眼近乎无法感知,无缝衔接——效果如下视频。这个加载效果也步入业内第一梯队。

4.2 内存溢出

由于动态载入\卸载的加成由于内存瓶颈造成的崩溃率已经有较明显下降。但是针对深度用户,崩溃依旧无法避免,但这部分用户又尤其重要。

同样的,遵循 渐进增强,优雅降级 的原则,我们先系统地整理了影响内存情况的所有因素——见内存溢出影响因素鱼骨图。

内存溢出影响因素鱼骨图
图五:内存溢出影响因素鱼骨图

同时按照线上内存性能分布情况、算法用户画像分析和测试团队线下测试情况建立了一份数据库。基于这份数据库和算法的用户画像数据来给用户提供不同的功能——即“千人千面”的用户体验,大体逻辑如下:

  • 针对低端环境用户(终端设备性能弱,电池影响等):仅提供基本功能,高端功能(高分辨率、装修对比等)禁用(不会加载渲染)。
  • 针对高端环境用户(高性能设备):渲染质量高,功能丰富。
  • 针对用户画像提供功能:比如,用户对装修感兴趣,则推荐装修模块;比如,用户购买意向高,则渐进推荐 VR 带看、AI 讲房等功能

至此,将原本前端性能优化工作转换成算法团队根据用户画像来推荐功能的工作。性能状况是用户画像的一部分,在性能条件容许的情况下给用户最好的体验和功能,而非之前一股脑儿全给——不管你是什么样的用户,都能得到合适的 VR 3D 看房服务体验。

而前端的工作重点则开始转变解析 WebSocket 推送的指令——在首屏模块的基础上,该渲染哪些异步模块,该何时卸载哪些异步模块,卸载的同时内存的清理情况。

很可惜这部分并没有很务实地落地——可能对于家长而言,孩子考八十就足够了,不强求九十分或更高~

叁、表格形式-简化

表格形式-简化