基于 Electron 的淘宝直播 PC 推流端已经上线一年多,期间迭代了很多功能,应用也越来越庞大。自上线以来也收到一些用户反馈应用启动慢、打开推流页面慢、运行过程页面交互操作卡、推流画面卡、CPU 占用过高等性能问题。针对这些问题,我们要怎么优化呢?
背景
在开始讨论优化之前,我们先来了解下 Electron 是一个使用 JavaScript、HTML 和 CSS 构建 Windows、MacOS、Linux 跨平台的桌面应用程序的框架,嵌入了 Chromium、Node.js 和 Native API:
由于 Electron 集成了 Chromium,因此天然支持多进程架构。当淘宝直播 PC 推流端运行起来后,通过查看任务管理器就可以知道除主进程、Utility、GPU 进程外,每新建一个页面窗口就会多一个 renderer 进程,如下图所示:
而我们实测也发现也只有推流页面所在 renderer 进程最耗费性能,其中推流 C++ SDK 也运行在该 renderer 进程里。因此,我们接下来主要讨论的是推流页面 renderer 进程是如何优化性能的。
如何优化淘宝直播 PC 推流端性能
▐ 启动耗时优化
- 应用启动耗时优化
当我们双击打开淘宝直播 PC 推流端应用时,需要初始化一系列的逻辑(如设置缓存、登录所需环境、创建目录、检测内网环境创建系统设置菜单、启动本地服务、初始化 IPC、创建首页、预热推流页面等),整体可交互耗时需要 10s 左右,体验不是很好,用户满意度方面很差。因此,我们进行了一轮优化:
- 启动流程优化
检测内网环境创建系统设置菜单优化:请求接口获取是否内网环境比较耗时,同步改为异步
electron log 日志输出优化:涉及 IO 操作,多条合并为一条
electron store 删除及设置优化:涉及 IO 操作,多条合并为一条
删除本地过期目录及杀掉残留独立进程优化:启动执行改为退出时执行
创建首页窗口流程优化:首页显示后再执行预热推流页面逻辑 - 包体积压缩优化
使用 webpack 配合插件 loader 将 js、css 等资源进行压缩,并通过抽离公共模块,整个 asar 大小从 18M 压缩至 2.5M 左右。这样可以提升 js 加载速度。 - V8 code cache
V8 代码提前编译并进行缓存,等加载执行时速度就快很多了。不过由于 Node.js 启动流程已经针对内置模块的 V8 代码进行编译并缓存了,因此项目中设置也没收益。
经过以上优化后,应用启动耗时从 10s 降低到 2.5s。
- 推流页面启动耗时优化
淘宝直播 PC 推流端刚上线时,从首页进入推流页面需要经过数据加载(如通过请求获取直播间配置等数据)、初始化 SDK(C++ 推流 SDK)、页面渲染等流程,整体可交互耗时需要 5s 以上,核心页面 loading 时间比较长:
于是我们进行了一轮优化,通过数据预加载(首页提前加载推流页面数据)、减少 SDK 的初始化耗时(把非关键路径代码改为异步执行),从首页进入推流页面从 5s 以上降至 2s 左右:
但经过优化后还是没有达到秒开效果,主要原因是初始化 SDK 比较耗时,那有人会问为啥 SDK 不能在首页提前初始化呢?这是因为首页和推流页面都运行在各自的 render 进程里,如果要在首页提前初始化 SDK 的话,就得把 SDK 对象传给推流页面进程,而进程间通信传不了对象,因此该方案行不通。
那还有没其他方案呢?其实我们可以参考预加载的能力,将推流页面进行预热,这样就可以做到提前初始化 SDK 了。通过预热方案后,从首页进入推流页面只需要将隐藏的推流窗口进行显示就行,整体耗时在 50 ms,实现秒开效果。
▐ 运行时性能优化
前面优化完核心页面启动耗时后,接下来我们重点介绍如何优化运行时性能。
在做运行时性能优化之前,我们先想一想一个 Electron 应用到底哪里会有性能问题?就拿淘宝直播 PC 推流端为例,我们需要对整个应用进行性能摸底测试,才能知道哪些模块会有性能问题。因此,我们开发了一个性能测试工具,可以分别对前端和 SDK 模块进行单测:
其中测试指标包括 CPU、单帧耗时、渲染输出帧率、GPU、内存、页面帧率、页面卡顿率等,而我们最关心的 CPU 是通过系统 api 获取(对齐业界通用的 ProcessExplorer 三方测试工具),需要在锁频情况下进行测试,CPU 锁频脚本如下:
@echo off set key1=HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\intelppm set key2=HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Processor set value=start set newvalue=4 reg add "%key1%" /v "%value%" /t REG_DWORD /d %newvalue% /f >nul 2>&1 if %errorlevel% equ 0 ( echo Successfully changed %key1%\%value% to %newvalue% ) else ( echo Failed to change %key1%\%value% to %newvalue% ) reg add "%key2%" /v "%value%" /t REG_DWORD /d %newvalue% /f >nul 2>&1 if %errorlevel% equ 0 ( echo Successfully changed %key2%\%value% to %newvalue% ) else ( echo Failed to change %key2%\%value% to %newvalue% ) pause
通过使用性能测试工具进行摸底测试,我们发现了一些模块的性能问题,如轮询请求、baxia、纯净流、端智能、美颜、推流等模块。下面分别从前端和客户端的视角进行性能优化介绍。
- 前端页面性能优化
由于 Electron 集成了 Chromium 内核,因此 Electron 应用的前端页面也可以用 performance 面板来分析性能问题,并进行性能优化。
场景元素操作耗时优化
当我们推纯净流时,推流页面创建的场景元素越多导致操作耗时越长。通过 performance 面板录制后分析发现会重复调用 DuplicateSource NAPI 接口,而该接口调用一次需要花费 100 ms ~ 500 ms。
因此,针对纯净流场景进行性能优化,当场景元素变换时只 copy 变更的元素,将 DuplicateSource 接口调用次数降为 1 次,且不会随着场景元素的增加而增加,经测试发现场景中有 50 个元素时操作耗时从 30s 降为 1s。
baxia 安全逻辑耗时优化
在性能摸底测试时,发现 PM 消息推送过程存在 CPU 间隔性飙升的情况:
一开始怀疑是 PM 解析消息时消耗 CPU,但通过 performance 面板录制后(下图左侧是长任务,右侧是各个 js 耗时占比),分析原因发现是 baxia 引入的 fireye.js 里调用的 getFYToken 接口(采集底层及硬件信息)非常消耗性能。
那为啥是 PM 消息推送时会有这个性能问题呢?原来是因为 PM SDK 也用到了 baxia 的 fireye.js 脚本(AWSC),而该脚本是为了防爬、防刷、人机等安全能力。
由于我们应用接的 baxia 并没有使用到 AWSC,评估后可以将相关耗时(采集底层及硬件信息)接口函数置空即可:
(window as any).AWSC = { use: () => {}, configFYEx: () => {} }
经过处理后,baxia 模块耗时占比只有 0.5%,而且 CPU 使用率非常平稳,不再有飙升情况:
- 客户端性能优化
前面所介绍到的是以前端视角进行性能优化的,那客户端有没像 performance 类似的性能分析工具呢?常用的主要是 Visual Studio 和 Windows Performance Analyzer。
Visual Studio:除了用来日常开发外,调试面板还有个性能分析器功能,可用于帮助开发者分析和优化程序的性能。但是测试发现将 pdb 调试符号映射上后也只能解析出系统动态库及 electron 框架自身的代码,无法进一步解析出应用业务模块代码。
了解完客户端常用性能分析工具后,我们就可以利用这些工具进行性能优化。由于我不直接参与客户端底层的性能优化,只是从应用层面配合优化,因此我只是介绍下整个应用经过一轮性能优化后,大盘 CPU 超过 65% 的直播场次占比从 13% -> 6.7%,工程侧 CPU 占比从 34.5% 降为 9.4%:
- 前处理切独显渲染
- 电脑配置较差时美颜相关算法策略调整
- 采集输入帧率及输出帧率优化
- 端智能智能看点下线及安全相关算法频次调整
- 纯净流默认策略调整
- ARTC 日志优化
- 推流低帧率治理及 264 ultrafast
- ......
如何优化淘宝直播 PC 推流端性能(下):https://developer.aliyun.com/article/1480498