客户端性能优化测试心得

文摘   游戏   2024-05-11 16:10   浙江  
客户端性能对于游戏项目来说至关重要。客户端性能优化的工作中,测试组通常负责协助确认问题、提供测试数据、验证优化效果。回顾客户端性能相关的测试工作,笔者踩过一些坑,也收获了一些经验。除了客户端性能测试的科普和偏执行落地向的方法外,最值得拿出来分享的就是和客户端性能优化相关的一些测(踩)试(坑)心(记)得(录)了。本文不涉及成体系的客户端性能测试的基础知识,也不涉及具体的测试执行步骤和工具介绍。笔者将以个人视角,结合项目实际案例,挑选7个个人认为最有价值、可跨组迁移的测试心得,聊聊客户端性能优化的测试工作中我们遇到的实际问题和解决思路。

01 认识到性能优化的长期性

在当前项目之前,我有过两个手游项目的上线经验,每个项目都在后期优化过程中遇到了技术规范逐步明确、各类美术资源跟随技术规范大范围调整甚至返工的情况。因此,在项目建立初期,我就试图推动程序和美术制定项目的制作规范,提前确认好各类资源的规格和制作标准,希望减少后续的返工情况。结果并不令人意外,这个努力并没有成功,项目还是走了一条“先产出后优化”的路子,和过往项目的优化过程并无二致。

从现在的时间点回顾项目性能优化的过程,我认识到当时预想的“提前制定规范并按照计划严格实行,避免非必要的返工”是一件非常困难的事情。阻碍这个目标实现的主要问题有两个:
其一,整个团队对于性能问题的技术积累和认识是在逐渐发展的。项目初期的工作重心聚焦于核心玩法的完善,内容优先、表现效果优先几乎是一个必然的选择。前期工作以游戏功能向为主,自然也就无法兼顾到性能优化相关的人才和技术积累。在项目核心玩法逐步明确和稳定后,组内引入了越来越多从事性能优化的人力,才逐步完成了对应的技术积累,同时也交了很多“学费”。这个情况对于一个前期投入规模有限的团队来说,是很普遍的。
其二,项目开发的过程中有很多不确定的因素,这些因素都会影响到性能标准的制定。策划方案在不停地演变,不同的设计产生不同的需求,会导致很多提前制定的标准发生根本性的变化。策划设计对于制作标准的影响是巨大的,一些提前设定的规范可能会因设计的变化而失效。
产品的发行目标也会极大地影响性能标准的制定在2021-2022年国内版号发放陷入停滞的背景下,出海成为了一个必然的选择。当我们把发行地区设定为东南亚篮球用户最多的地区菲律宾时,菲律宾的机型适配工作成为了一项高优先级工作。当国内制作标准的美术资产遇上菲律宾的低端机型,削减模型面数、削减骨骼数量等方案一一被提出并付诸实践。菲律宾低端机的适配极大地影响了项目美术资产的制作规格,原先针对国内机型制定的性能标准也都失效了,需要重新根据目标进行调整。
以上两个案例,我们不难发现,在开发一款游戏的过程中,性能标准可能会跟随着项目目标发生巨大变化
项目对于性能优化工作的人力投入时机和项目发展过程中性能标准的不确定性,决定了性能优化将会是一件长期性的工作,不能一蹴而就,我们需要对此有清醒的认识。
与此同时,承认性能优化工作的长期性,并不意味着我们在项目前中期就应当两手一摊、放任自由了。资产(Asset)目录规范、各类资源标准检测工具、性能问题的定位和分析工具等通用型的规范和工具都是可以跨越整个制作周期长期生效的。

02 为重大的性能决策增加数据打点

日志数据打点都是记录信息,但在游戏中起到的作用有很大差别。前者数据量大、内容详细,难以全部收集;后者数据量小、方便收集,不过只记录特定信息。
客户端性能优化过程中,程序通常会在客户端日志里增加一些调试输出,用于验证优化是否生效。大部分调试日志会在对外发布前清理或者调整为不输出状态。正式上线后,绝大部分产生的日志都留在客户端里不会被用到。我们一般只会在玩家反馈问题时要求玩家主动上传日志信息,便于分析问题。
数据打点则是按照指定的格式记录事件内容,附带客户端和设备的基础信息,实时上报给日志服务器。数据打点一般都是基于某种记录和统计上的目的,为游戏分析和决策提供数据参考。游戏的数据打点需求主要集中在和游戏留存、玩家操作行为相关的方面,而客户端性能相关的数据打点会容易被忽略
2022年初,项目组确定了海外菲律宾的测试计划,机型适配工作提上日程。在通过各种渠道获取到菲律宾的机型数据后,我们把目标定在了最低支持骁龙410级别的设备(约等于国内2014的安卓普通机型)。
除了CPU型号外,还有一些指标也影响东南亚市场的机型下沉,包括图形API贴图的压缩格式。图形API方面,根据我们获取到的机型数据,部分在东南亚市场占比较高的设备都只支持OpenGLES 3.0,所以图形API定在了OpenGLES 3.0。贴图的压缩格式方面,安卓平台有两种相对主流的贴图压缩格式——ETC2和ASTC。相比于ETC2, ASTC支持更高的压缩比、更好的图像质量和更高的颜色精度,且对透明度、高动态范围(HDR)等功能支持更好。所有支持vulkan和OpenGLES 3.1的设备都支持ASTC格式,部分OpenGLES 3.0的设备也支持。考虑到和图形API的最低支持相匹配,我们把安卓平台的贴图压缩格式设定为ETC2,以确保在所有OpenGLES 3.0上的机型都能正确显示。贴图格式从ASTC切换为ETC2的代价是,安卓手机高画质下的图像表现变差了,相比于同样高画质的苹果手机,显示效果上有明显差距。

高市占率低配机型ASTC格式兼容情况(2020.5测试),引自知乎用户Ssiya

我们一直维持着安卓平台OpenGLES 3.0和ETC2贴图压缩格式的设定,直到2023.3月我们完成了海外菲律宾上线,启动国内上线准备的准备工作。安卓平台高画质贴图质量的问题被美术同事再度提出,希望能在国内上线前优化解决。解决的方案其实也不复杂,国内安卓设备整体领先东南亚5年左右,把贴图压缩格式切换为ASTC,基本不会存在不支持的情况,画质问题就自然而然解决了。这个改动在项目落地的困难在于,我们在制作海外版本初期就确立了“国内和海外版本同时基于单一主干工程开发、不单独区分处理”的开发模式,这个开发模式不支持保持海外继续使用ETC2的情况下将国内调整为ASTC。要在单一主干工程实现国内ASTC和海外ETC2两套不同的贴图压缩格式,只能是在打包的时候支持根据国内和海外切换不同的贴图压缩格式。在同一个工程下,如果需要反复切换两种不同的贴图压缩格式,由批量贴图格式导入带来的时间花费将是巨大的,这对于已经习惯经常安卓包调试各类功能(单日安卓包出包数量能达到20-30个)的开发流程来说无疑是一种灾难。还有一种不切换贴图格式的方案是将打包机进行划分,一半国内一半海外,减少了格式切换的耗时,不过另一份贴图格式的文件如何维护、如何保证和同版本svn上的文件只有贴图压缩格式的差异,仍然是一笔不小的开发量和维护量。

国内渠道测试在即,考虑到切换贴图压缩格式的功能需要一定开发时间,我们准备在国内渠道测试专用分支上临时切换到ASTC,并利用渠道测试收集国内设备对于ASTC的支持程度。ASTC格式内分为不同级别的压缩级别,Unity引擎内置了判断函数SystemInfo.SupportsTextureFormat,可用于判断设备对于某种ASTC压缩级别是否支持。我们在国内渠道测试分支开发了相应的日志打点,如果支持所有我们预计使用的ASTC压缩级别,就记录SupportASTC为true。在一次对海外玩家图形API的数据统计过程中,我们发现所有设备中只有0.69%的设备是OpenGLES 3.0级别的,与当初预计的有较大差别,这意味着我们先前对于海外低端机最低配置的预估可能是不准确的。这让我们对海外设备支持ASTC的程度也产生了怀疑,于是在海外版本更新中加入了是否支持ASTC的日志记录。新版本上线后的激活日志表明,不支持ASTC的设备约为0.7%。综合市场重要程度、高画质表现效果、两套贴图压缩格式所带来的开发维护量和海外不支持ASTC设备的占比等诸多因素,放弃海外0.7%不支持ASTC的设备、全面切换为ASTC压缩格式成了我们必然的选择。

回顾整个决策流程,每个步骤都有相应的决策逻辑,海外切ETC2也并不是拍脑袋想出来的决定。从结果上来看,我们绕了一个大弯,最终还是回到了以实际数据为准的正确道路上来。这个过程中的一些环节上,我们有不少可以改进的地方:我们没有利用好2022年9月的菲律宾测试数据(万人级别),数据的分析集中在留存、玩法等层面,设备方面有性能档位的数据,但没有关于图形API、贴图压缩格式支持程度的统计。一些设备特性的支持程度数据,也是在发现了问题后才增加了记录功能,如果能更早地记录这些信息,能少走很多弯路。像图形API级别、贴图压缩格式支持程度这类指标,一旦确定是无法在线上动态切换的,初版按照适配往低走的思路没有问题,但需要同时加上日志,尽快利用线上数据验证指标设定的合理性

03 为性能优化的相关内容加开关

性能优化内容加开关在不同的阶段有不同的意义。

在性能优化初期,首要的任务是确定哪些逻辑是消耗性能的主要内容。一种可行的方式是增加profile打点,统计每段逻辑的性能开销。这种方式下,所有功能都处于开启状态,采集高端机的数据没什么问题,但如果需要调试低端设备上的效果就会比较困难。此时,可以引入不同逻辑的开关,通过直接在游戏中打开/关闭开关的方式,快速切换功能的状态,并借助自带的FPS展示功能,直观地验证不同模块对性能的影响程度。在发现低端机型粉丝团整体消耗过大的情况下,我们果断砍掉了低端机上的该功能。

性能调优初期用到的功能切换开关,能快速在游戏中切换功能的启用状态

在项目上线前的性能优化中,开关能帮助程序和测试验证一项性能优化的有效性。以比赛内的优化为例,验证时基于相同的性能包,在开关开启和关闭的两种状态下,分别采集性能数据,然后观察指定函数的性能变化。

一次性能优化的结果记录,使用开关切换是否启用优化,对比优化前后的性能

在项目上线运营后,开关能保证线上版本的稳定性。相比于开发期,游戏上线后的开发节奏更快,版本的稳定性要求也远高于开发阶段,部分性能优化的方案在缺少足够时间/用户量验证的情况下,很难确保正确性,此时开关就会显得尤为重要。如果一个性能优化在封版前来不及得到充分验证(比如晚于兼容性测试开始后才提交),我们会要求程序提供相应的开关,并让优化默认处于关闭状态。待定版过审后,再进行验证。对于验证无问题的优化,我们一般会选择在版本更新后大部分线上问题已解决的情况下,视情况在线上灰度/全量开启性能优化,并密切关注dump的相关信息,避免优化带来负体验。这里有一个反面的案例:国服线上版本在23年末一批新的骁龙8gen3 + Android 14的机器上,会出现非战斗期间切换不同界面时卡住崩溃的情况,起初没有找到明确的问题,暂定结论是12月版本不处理等年后的版本。12月版本定版准备发包前,产品留意到相同问题的玩家反馈有增多的趋势,于是推动程序紧急攻坚。虽然没有100%明确原因,程序还是找到了一个注释部分崩溃逻辑方案,通过更新引擎库的方式绕过了该问题。iOS提审时间在即,受限于测试时间,我们验证了2台小米14机型(已经是能借到的所有符合条件的机型)在更新引擎后不再出现此问题,权衡后决定将更新后的引擎版本用于12月版本。12月版本上线后,发现部分启用了vulkan的高端机型出现了相比过往更多的崩溃,因此通过patch将线上启用vulkan的机型都切换回OpenGL的图形API,切换后崩溃量明显减少。vulkan机型在12月版本的兼容性测试中进行过验证,当时并未发现异常,也没有收集到崩溃,我们推测主要还是临时更换引擎版本+更换后未做全面的兼容性测试导致了该问题。幸而vulkan功能是一项针对高端机型的优化,有预留开关,我们通过关闭开关避免了更大的问题。


04 根据测试目标选择合适的测试包

测试包体接近线上配置,这一点不用过多解释,相信所有人都能明白测试包体和线上配置越接近,测试数据越准确。然而,在执行的过程中,可能会遇到各种各样的需求,使得测试包体的配置需求复杂化。

一个最直观的影响因素是游戏的日志等级。程序在客户端运行过程中记录了很多日志,有些日志是报错性质的,有些日志则是调试性质的。调试日志在开发期对于排查问题出现的原因十分重要,而到了正式上线时,调试性质的日志会被剔除,以减少总日志量的大小。根据我们项目优化初期的一份测试数据,普通带调试日志的包在中端机型上只能跑23帧(平均耗时约43ms),而去除了调试日志的包在同样机型上能跑到接近30帧(平均耗时约33ms),单帧耗时相差约10ms,仅剔除调试一项就减少了近23%的帧均耗时。

另一个和客户端性能直接相关的因素是是否开启Development Build。Unity生成指定平台的游戏包体使用BuildPlayer函数,其参数中有一项BuildOptions支持不同的生成选项。启用BuildOptions.Development选项时,生成的游戏包体可以被Unity Profiler读取和采集性能相关的数据指标。Unity提供的性能分析工具UPR(Unity Performance Reporting)和我们内部的深度性能分析工具 ,都依赖游戏客户端启用Development Build才能正确采集数据。因此,程序在性能优化过程中需要大量使用启用Development Build的游戏包体来测试。关于是否在日常所有包体上启用Development Build选项,在程序和测试间还有过一段拉扯的过程,最终因为启用Development Build的包体在一些兼容性问题上和关闭状态下不一致、不利于在内部尽早发现问题,程序同意了只在性能优化相关调试包上启用该选项。

我们项目目前使用的3种比较常见的打包参数组合如下表所示。

在需要测试最准确的各档位性能指标时,我们会用g_perf_test命令打出的包体。根据其他项目的经验,是否启用Development Build,性能差异大约在10%左右,使用不带Development Build的包体能获得更准确的性能数据。当发现初步性能数据有异常或需要针对特定内容进行性能优化时,我们会使用g_perf命令打出的包体,通过Unity Profiler等调试工具采集深度的性能数据。采集到的数据除了整体比发布版本耗时偏高一些以外,各函数和渲染耗时的分布都是准确的。如果在优化过程中,还是需要结合更多的日志来分析,那么程序会使用g_perf_with_log命令打出的包体,此时采集到的数据和分布可能会有一些偏差,只适用于特定需要采集性能数据同时配合日志分析的场景。


05 测试行为要接近真实用户

日常工作中,如果要测试某种设备在某个条件下的性能数据,我们一般会预设好测试环境,直接测完一轮得到数据结果。如果需要多轮对比测试,为了减少对比误差,我们一般还会在每次测试后重启整个环境,以确保多轮测试的基准是一致的。一个更极端一些的例子是,在游戏前中期(此时一般性能较差发热严重)测同一个设备的多组数据时,我们甚至有可能中间需要让设备冷却降温一段时间,再开始下一轮的测试,以防手机降频给测试结果带来干扰。这些测试方法都能减少多次测试间的误差,对我们获取对比数据是有意义的。

然而,用户的使用场景和我们测试的场景是有一定区别的。一个最直观的例子是,用户不会只打一局比赛就停下来,几十分钟甚至更长的游戏时间是常见的游戏时长。具体到我们项目,单局比赛的时长一般在3分钟到5分钟,半小时间大约能打6-10局。如果要保证玩家的游戏体验,我们不能仅以单局的数据作为验证的标准,而是要模拟和玩家相同的游戏强度,采集到连续多局比赛的性能数据。项目组每周会组织一次集体测试,体验最新制作的游戏内容和真实的比赛对局。我们会利用集体测试的时机,单独出带development的性能包,由负责性能验证的同事连续完成10局比赛,并在第1局、第5局和第10局各采集一份性能数据。第1局的数据最接近我们的峰值性能数据,可以比较直观的评价当前版本的性能;第5局和第10局的性能数据则可以用于分析多轮比赛后,是否有帧率下降、内存持续增加、非预期的函数调用等情况。

下图展示的是2021年4月一次连续多局测试过程中,第1局和第5局采集到的UPR性能数据概览。

第一局和第五局采集的UPR性能数据概览

相较于第1局,第5局的比赛帧率开始出现明显的波动,Mono内存占用增加了约50MB。单局的高耗时函数也发生了变化,新增了一个非正常的函数,该函数为动态骨骼更新函数,原本在比赛内部应该有调用,说明动态骨骼计算的开关被意外打开了,这是单局测试很难发现的问题。


06 对有代价的优化进行谨慎的评估

客户端性能优化中,有一部分优化是对原方案的纯改良,比如原来的写法有大量字符串拼接,新方法去掉了拼接,降低了内存的GC量,提升了客户端运行的稳定性。另一部分优化可能是对原问题做一些权衡(trade-off),支付一定的代价,选择对我们有利的一面

对于我们项目来说,游戏中比赛内的卡顿是一个非常影响体验的因素,所以降低比赛内的突发瞬间CPU高耗时行为就成为了我们主力优化手段之一。通过各种预先计算和预先加载,在loading过程中提前提前加载好比赛中可能需要的各种动作、图标、特效、音效、球运动轨迹数据等资源,在实际需要用到的时候快速实例化甚至是提前实例化好放在缓存池中随时取用,对于降低比赛过程中的突发高耗时行为有很明显的帮助,越是低端的机器体验改善越明显。预加载优化的代价是更高的内存占用和更长的加载时长,必须小心地控制预加载的数量和耗时。我们的新手场景在海外菲律宾低端机型上的加载耗时较长,单次加载耗时在7-45秒不等,游戏安装后第一次冷启动耗时在某次测试中出现了最高99秒的数据。对于这个结果,鉴于大部分的机型加载时间可以接受,我们当时并没有详细分析各部分的耗时。

在后续一次其他loading问题的定位过程中,程序为了分析各部分逻辑的耗时,也将预加载各部分的耗时进行了拆分统计。测试结果表明,球运动轨迹的耗时占比非常高。为了让游戏中球的运动轨迹更真实,策划预设了非常多的球轨迹,每次投篮、传球后的球轨迹都会从符合条件的球轨迹中随机一条。程序在进行比赛内数据预加载的优化工作时,球轨迹的数量还不多,因此采用了全量加载的方式。后续随着策划逐步完善和加入更多的球轨迹,这部分耗时变得越来越大,但因为缺乏对应数据的记录,并未及时发现。特别是新手引导的比赛,其整个流程都是按照设计的方案走,完全不需要加载那么多比赛中各种条件下才会出现的球轨迹。针对性地减少新手引导比赛的球轨迹后,新手引导比赛的平均加载耗时降低了约15秒,整个新手引导的通过率也提高了不少。

球轨迹优化的事例提醒我们,在面对有一定代价的性能优化方案时,需要对方案进行更为谨慎的评估。评估方案可以是做更多更详细的测试验证,也可以是适当增加对性能优化结果的日志记录,辅以定期的性能回顾,应该能有效避免这类非预期优化问题的发生。


07 性能优化内容需及时同步

客户端性能优化过程很多时候是一个做减法的过程,哪个部分的消耗大、削减后影响小,就把哪个部分优化掉。以我们游戏的大厅场景优化为例,渲染的耗时包括3D场景和2D UI两部分的耗时。当我们打开一个全屏或者几乎全屏的2D UI界面时,3D场景的渲染其实还是一直在进行中,这一消耗对于游戏表现并无作用,属于可被优化的内容。根据这个想法,每个界面都被打上了标签:全屏界面出现时,背后的场景相机关闭停止场景渲染;几乎全屏的界面出现时,使用一张预先准备的大厅固定背景作为2D渲染的底图;界面关闭后,重新恢复场景相机的渲染。

这个优化想法很简单,效果也是立竿见影,唯一的问题是,在各种界面相互跳转的过程中,容易出现计算是否回到大厅不正确的问题,其结果是大厅场景完全变黑,无法正确显示场景和球员。

客户端性能优化的跟进一般来说都是由特定的几个人在处理,一些优化措施和影响只有少数人清楚。如果没有同步这些信息给全组,其他人不清楚问题出现的原理,就会出现很多“xx界面打开后关闭,大厅场景变黑”的BUG,分别开给对应功能的程序去修改。如果负责的程序也不清楚该优化,可能就会一头雾水,查半天也未必能发现问题。这种信息上的不对称在越大的项目组中越容易出现,此时信息的同步就显得尤为重要。

通知到全组我们做了什么样的优化,可能出现什么样的异常,如果出现了异常要找谁来处理,能够极大地降低性能优化可能带来的负面影响,提高问题的排查和解决效率。


08 结语

本文的篇幅写了不少,内容比较零散,干货有限,主要还是分享一些个人心得,感谢阅读,希望能给你带来一点收获。







推荐阅读


如何当好普通QA

支付测试方法分享

如果早知道,音频也可以自动化......


 
都看到这里了,点个赞再走吧~

网易雷火测试中心
雷火测试中心致力于提供高质、高效的质量保障服务,建设成为国内顶尖的测试团队!
 最新文章