Cocos Creator性能优化实战:从原理到20个核心技巧

📅 2026/7/15 10:08:23
Cocos Creator性能优化实战:从原理到20个核心技巧
1. 项目概述为什么Cocos Creator性能优化是门必修课做Cocos Creator开发这些年我见过太多项目倒在性能门槛上。一个玩法设计精良、美术资源酷炫的游戏在真机上跑起来却卡成PPT加载慢如蜗牛甚至直接闪退。这往往不是引擎的锅而是开发者对性能优化缺乏系统性的认知和实战技巧。性能优化不是项目上线前的“急救”而应该是贯穿开发始终的“保健”。它关乎用户体验的下限也决定了产品商业表现的上限。今天我就结合自己踩过的无数坑和总结出的有效经验为你拆解从原理到实战的20个核心技巧目标是让你不仅能解决眼前的问题更能建立起一套可持续的性能优化思维和工程体系。2. 性能分析精准定位瓶颈是优化的第一步优化最忌讳的就是盲目动手。在不清楚瓶颈所在的情况下任何优化都可能是无用功甚至适得其反。性能分析就是我们的“诊断仪”它告诉我们哪里“病了”以及“病”得有多重。2.1 性能监控工具的选择与使用心法工欲善其事必先利其器。Cocos Creator生态和原生平台提供了丰富的性能分析工具但要用对场景。Cocos Creator内置性能面板这是开发阶段最常用、最直观的工具。我习惯在编辑器里常开它。重点看几个核心数据Draw CallDC、帧率FPS和三角面数Tris。Draw Call是CPU向GPU发起绘制命令的次数是2D和轻量3D项目最关键的瓶颈指标之一。一个复杂的UI界面动辄上百个Draw Call不优化根本没法看。帧率波动能直观反映卡顿而三角面数则直接关联GPU的几何处理压力。注意编辑器里的性能数据尤其是帧率和真机有差异因为编辑器本身也在消耗资源。它更适合做相对比较和趋势观察比如优化前后Draw Call从120降到40这个对比是有效的。浏览器开发者工具Chrome DevTools当你的项目发布为Web平台时这是神器。Performance面板可以录制一段时间的运行时性能生成火焰图精确到每个函数的执行时间帮你找到JavaScript逻辑中的热点Hot Path。Memory面板可以拍摄堆快照Heap Snapshot追踪JavaScript对象的内存泄漏那些本该被回收却依然驻留的节点、纹理引用在这里无所遁形。原生平台专业工具Xcode Instruments Android Profiler真机调试的终极武器。Xcode的Instruments套件里的Time ProfilerCPU、Core Animation离屏渲染、Allocations内存模块极为强大。Android Studio的Profiler同样可以监控CPU、内存、网络和能耗。这些工具能帮你发现一些引擎层面之上的、更底层的系统性问题比如频繁的GC垃圾回收导致的卡顿或者纹理上传带宽瓶颈。我的经验是建立一个“由表及里”的分析流程先用Cocos性能面板看整体指标是否异常如果发现JS逻辑耗时高用Chrome DevTools深入分析最后在真机上用原生工具验证和定位平台特定问题。不要一上来就扎进最底层的工具那样效率太低。2.2 关键性能指标解读与健康基线知道看什么数据还要知道什么样的数据是健康的。这里给出一些我在移动端项目中重度游戏中总结的“及格线”参考当然越严苛越好。帧率FPS稳定60帧是黄金标准。允许在复杂场景下短暂波动到55但长期低于50用户就能明显感知到卡顿。对于非动作类游戏稳定30帧是可接受的最低底线。Draw Call这是2D和UI性能的生命线。对于纯2D项目建议将同屏Draw Call控制在50个以内对于有3D场景的可以放宽到100-150但前提是每个Draw Call的渲染负载顶点数不能太高。通过合批技术后面会讲可以大幅降低此数值。内存占用一个粗暴但有效的原则是你的游戏内存峰值不应超过目标设备可用物理内存RAM的50%。例如在2GB RAM的设备上你的游戏最好别超过1GB。iOS的“内存警告”和Android的“OOM内存溢出”崩溃大多源于此。不仅要看总内存还要关注纹理内存它通常是最大的“内存杀手”。加载时间首场景或核心玩法的首次加载控制在3秒以内是优秀5秒是可接受门槛超过8秒用户流失率会急剧上升。这需要资源加载策略和包体优化的配合。CPU/GPU占用率在真机监控中单核持续高于70%或GPU持续高于60%通常意味着存在优化空间可能会引起设备发热和降频进而导致帧率下降。3. 资源优化从根源上为性能减负游戏性能问题十有八九出在资源上。不合理的资源使用就像让一个瘦子背着一座山跑步。3.1 纹理压缩移动端性能的“胜负手”纹理是显存对于WebGL和移动端通常是内存的一部分的最大占用者。一张2048x2048的RGBA888832位未压缩纹理内存占用是16MB这简直是灾难。平台特异性压缩格式是必选项Android (OpenGL ES)推荐使用ASTC格式。它压缩率高质量损失小且支持透明通道。astc_6x6在质量和大小间取得了很好的平衡astc_8x8更小但质量略低。在项目设置的项目设置 - 资源数据库 - 纹理压缩中配置。iOS (Metal)推荐使用PVRTC格式。这是苹果GPU原生支持的硬件解码格式速度极快。pvrtc_rgb4和pvrtc_rgba4分别是无透明和有透明的4bpp每像素4比特格式。Web / 微信小游戏由于浏览器兼容性问题通常无法使用硬件压缩纹理。此时要靠Crunch压缩一种基于DXT的运行时解压压缩或直接使用ETC1不支持透明等。更重要的策略是减小纹理尺寸和使用精灵图集Sprite Atlas。实操配置示例与效果 在settings.json或项目设置面板中可以这样配置// 项目设置中的纹理压缩配置示例 { “textureCompression”: { “android”: { “format”: “astc_6x6”, // ASTC 6x6 块压缩 “quality”: “fast” // 压缩速度优先对质量影响很小 }, “ios”: { “format”: “pvrtc_rgba4”, // PVRTC 4bpp 带透明通道 “dither”: true // 启用抖动改善低精度下的色带问题 }, “web”: { “format”: “etc1”, // 或 “etc2”, “dxt5”取决于目标平台支持度 “quality”: “normal” } } }优化效果立竿见影同样一张2048x2048的UI背景图从PNG约16MB压缩为ASTC 6x6内存占用可能降至2-3MB节省超过85%的纹理内存。这是性价比最高的优化没有之一。3.2 自动图集与合图策略将Draw Call“合并同类项”Draw Call过高的一个主要原因是大量小纹理的单独绘制。自动图集Auto Atlas功能会自动将散落的小图打包成一张大图。这样原本需要N个Draw Call绘制的N个小精灵如果材质相同或可合并就可能被合并到1个或很少的几个Draw Call中完成。配置要点 在项目设置 - 自动图集中关键参数是最大宽度和最大高度如2048这决定了图集的大小。内边距Padding通常设为2防止纹理采样时出现“ bleed ”颜色渗边。允许旋转可以更高效地利用图集空间。高级策略与避坑按功能/场景分包不要把所有UI图都打到一个巨无霸图集里。应该按界面如“主界面图集”、“战斗界面图集”或功能模块分包加载。这符合资源按需加载的原则避免首次加载内存爆炸。动态静态分离频繁变化颜色、透明度的精灵如果和静态精灵打在同一图集会导致整个图集材质失效合批。应将动态元素如血条、特效单独放在一个或多个小图集或直接使用单独纹理。图集冗余与更新开发中经常增删改UI资源会导致图集碎片化。需要定期如每次版本迭代前清理并重新构建所有自动图集。同时要利用好图集的“预留空间”概念为未来可能增加的小图留有余地避免因增加一张小图就导致整个大图集重构。3.3 音频与字体优化容易被忽略的“内存刺客”音频背景音乐BGM使用.mp3或.ogg这种有损压缩格式音效使用.wav短小或更好的.webmOpus编码压缩比极高。务必在AudioClip的导入设置中勾选“WebAudio 解码”针对Web平台这能避免播放时因解码导致的卡顿。对于长音频考虑流式加载而非全部读入内存。字体系统字体如Arial没有额外开销。但使用自定义TTF字体文件时注意它会被完整加载到内存。如果只需要显示数字和少量字母可以使用位图字体Bitmap Font工具生成.fnt和.png性能极佳且风格独特。对于动态文本如果必须用TTF尽量共用字体文件避免为不同大小或样式的文字重复加载多个变体。4. 渲染优化向GPU要效率当资源优化到位后渲染管线就成了下一个主战场。目标是减少GPU的工作负载核心思路是“少画、快画、聪明地画”。4.1 合批Batching深入静态与动态的博弈合批是降低Draw Call的核武器。其原理是将多个使用相同材质纹理、Shader、混合模式等的渲染组件合并到一个绘制调用中。Cocos Creator会自动进行合批但我们需要创造有利于自动合批的条件并在必要时手动干预。静态合批Static Batching对于场景中位置、形态完全固定不变的物体如背景、静态建筑可以将其标记为静态。引擎会预先将这些物体的几何数据合并成一个大缓冲区极大地减少Draw Call。使用BatchingUtility组件或相关API。// 标记节点及其所有子节点为静态适用于复杂静态物体 import { BatchingUtility } from ‘cc’; BatchingUtility.batchStaticModel(node);重要心得静态合批会增加内存和包体因为需要存储合并后的网格数据。只对确定永远不会移动、旋转、缩放或改变可见性的物体使用。滥用静态合批会导致内存暴增。动态合批Dynamic Batching引擎每帧自动尝试合并使用相同材质且顶点数较少的动态精灵。它的开销比静态合批大因为每帧都要重新计算。为了促进动态合批确保精灵使用相同的纹理在同一图集最好。确保精灵使用完全相同的材质实例。即使材质参数相同但如果是不同的材质实例也无法合批。尽量通过修改SpriteFrame而不是创建新Material来改变外观。保持渲染顺序RenderOrder连续。中间插入一个不同材质的精灵会打断合批。4.2 渲染顺序与Overdraw控制像素的绘制次数Overdraw过度绘制指同一个屏幕像素被绘制了多次。例如一个不透明的大背景盖住了后面所有物体那么后面物体的绘制就是浪费。在移动端像素填充率Pixel Fillrate是有限的瓶颈。优化策略层级Layer与渲染顺序Sibling Index管理通过node.setSiblingIndex()或直接在编辑器中调整节点顺序确保从后往前绘制即先画背景再画角色最后画UI。对于不透明物体正确的顺序可以借助深度测试Z-test提前丢弃被遮挡的片段但2D中更依赖画家算法。减少透明与半透明物体半透明物体Alpha Blend需要从后往前排序绘制且无法进行深度写入会严重打断合批并增加Overdraw。UI中大量半透明叠加是性能杀手。尽量使用不透明Opaque材质或使用Alpha Test在Shader中根据Alpha值直接丢弃片段替代 Alpha Blend但Alpha Test也会影响性能需权衡。裁剪Mask与滚动视图ScrollView的优化Mask组件会显著增加性能开销因为它需要生成模板缓冲区Stencil Buffer。确保Mask的Type设置为RECT矩形而非ELLIPSE椭圆后者开销更大。对于超长列表务必使用ScrollView的enableBounding启用边界检测或实现对象池视口裁剪只渲染可视区域内的项。4.3 Shader与材质优化轻量化渲染指令自定义Shader是表现力的源泉也可能是性能的黑洞。简化Fragment Shader片元着色器片元着色器的计算量尤其是复杂的光照、多重纹理采样、循环、分支判断直接影响填充率。在移动端“少采样、少计算”是铁律。避免在片元着色器中使用sin,cos,pow等复杂函数如果可能将计算移到Vertex Shader顶点着色器或CPU端。精度选择在Shader中使用lowp代替mediump或highp来声明低精度变量如颜色可以提升运算速度节省功耗。但要注意精度损失可能带来的显示问题。合并渲染状态通过自定义材质将多个需要相似渲染状态如混合模式、深度测试的物体合并减少GPU状态切换。Cocos Creator的RenderEntity和RenderRoot2D机制正在向这个方向演进理解其原理有助于写出更高效的渲染代码。5. 代码与逻辑优化让CPU跑得更顺畅渲染之外游戏逻辑和业务代码是CPU消耗的大户。低效的代码会让手机发热帧率跳水。5.1 对象池Object Pool告别频繁的创建与销毁实例化cc.instantiate和销毁node.destroy节点是极其昂贵的操作会触发引擎底层的多步生命周期管理和可能的垃圾回收GC。对于频繁生成和消失的对象如子弹、特效、敌人必须使用对象池。一个健壮的对象池实现要点export class BulletPool { private _pool: cc.NodePool; private _prefab: cc.Prefab; private _poolName: string; constructor(prefab: cc.Prefab, poolName: string, initCount: number 10) { this._prefab prefab; this._poolName poolName; this._pool new cc.NodePool(poolName); // 给池子命名便于调试 // 预热预先创建一些实例放入池中 for (let i 0; i initCount; i) { let obj cc.instantiate(this._prefab); this._pool.put(obj); } } get(): cc.Node { let obj: cc.Node null; if (this._pool.size() 0) { obj this._pool.get(); } else { // 池子空了动态扩容可设置上限避免失控 console.warn([${this._poolName}] Pool empty, creating new instance.); obj cc.instantiate(this._prefab); } // 重置对象状态非常重要 obj.active true; // ... 重置位置、速度、血量等所有业务逻辑状态 return obj; } put(obj: cc.Node) { if (!obj || !obj.isValid) return; // 清理状态 obj.active false; obj.stopAllActions(); // 放回池子 this._pool.put(obj); } clear() { this._pool.clear(); } }踩坑记录对象池对象放回前必须彻底重置状态包括停止所有计时器、动画、物理组件清空自定义组件的数据。我曾因为忘记重置一个“倒计时销毁”组件导致子弹从池中取出后立刻自爆排查了半天。5.2 分帧与时间切片Time Slicing避免单帧卡顿有些计算密集型任务如寻路计算、大量数据初始化、复杂AI决策如果在一帧内完成会导致该帧耗时极长出现明显的“跳帧”卡顿。解决方案是将任务拆分成小块分摊到多帧完成。经典的分帧处理模式private _heavyTaskQueue: any[] []; private _tasksPerFrame: number 5; // 每帧最多处理5个任务 startHeavyTask() { // 假设有1000个需要处理的项目 for (let i 0; i 1000; i) { this._heavyTaskQueue.push({ id: i }); } // 开始分帧处理 this.schedule(this._processQueue.bind(this)); } private _processQueue() { let processed 0; while (this._heavyTaskQueue.length 0 processed this._tasksPerFrame) { let task this._heavyTaskQueue.shift(); this._doHeavyCalculation(task); // 执行实际的重计算 processed; } if (this._heavyTaskQueue.length 0) { console.log(“所有任务处理完毕”); this.unschedule(this._processQueue); } }也可以使用setTimeout或requestAnimationFrame进行更精细的控制。对于Web平台甚至可以探索Web Worker将纯计算任务放到后台线程彻底解放主线程。5.3 事件监听与内存泄漏防范不规范的事件监听是内存泄漏和性能问题的重灾区。使用this.node.on并绑定thisCocos Creator的事件系统在节点销毁时会自动清理以其为target的事件。使用this.node.on(‘event’, callback, this)是最安全的方式。及时off对于全局事件如cc.systemEvent、非节点事件或跨脚本监听务必在组件onDestroy或合适时机调用off。闭包陷阱在回调函数中小心使用外部变量避免意外持有对大对象如整个场景的引用导致其无法被回收。使用弱引用如WeakMap或手动置空引用。定时器清理this.schedule和this.scheduleOnce创建的定时器在组件销毁时会自动清理。但使用setInterval或setTimeout时必须在onDestroy中用clearInterval/clearTimeout清理。6. 内存管理精细化的资源生命周期控制Cocos Creator的资源管理大部分是自动的但理解其原理并主动干预能避免很多“隐形”的内存问题。6.1 资源释放Release与引用计数引擎通过引用计数管理资源。当一个资源如纹理、预制体被加载后其引用计数为1。每次实例化一个使用该资源的节点或动态加载计数会增加。当节点销毁或调用release时计数减少。当计数为0时资源才会在下一次垃圾回收时被真正从内存中移除。关键API与策略cc.assetManager.releaseUnusedAssets()释放所有引用计数为0的资源。通常在场景切换后调用这是一个“安全”的清理操作。cc.resources.release(path, type?)/cc.assetManager.releaseAsset(asset)主动释放某个特定资源。风险高需确保没有任何节点或材质还在使用它否则会导致资源丢失出现粉红格子丢失纹理。动态加载资源的释放使用cc.resources.load或cc.assetManager.loadRemote加载的资源你必须负责释放。遵循“谁加载谁释放”的原则在不再需要时调用release。实战技巧建立一个资源依赖关系管理器。对于复杂的动态资源加载如角色皮肤、关卡资源记录加载的资源和其使用者。当使用者如一个角色被销毁消失时检查该资源是否还有其他使用者如果没有则安全释放。这比全局releaseUnusedAssets更精确但实现也更复杂。6.2 纹理内存的专项治理除了压缩纹理内存还能从这些方面优化禁用mipmap对于2D游戏或永远不会有透视缩放的UI纹理关闭Mipmap生成。Mipmap会为纹理生成一系列缩小的副本用于远处物体的抗锯齿但会增加约33%的内存占用。在纹理的导入设置中取消勾选Mipmaps。合理设置Wrap Mode和Filter Mode非必要的重复Repeat和线性过滤Linear会带来额外的采样开销。对于UI小图使用Clamp和Point过滤可能更合适且性能更好。纹理尺寸非2的幂NPOT现代GPU和WebGL 2.0已较好支持NPOT纹理但为了最大兼容性和某些压缩格式如PVRTC的要求仍建议主要纹理尺寸保持为2的幂如25651210242048。6.3 JavaScript堆内存与垃圾回收GC抖动除了引擎管理的资源JavaScript自身创建的对象也会占用大量内存并引发GC。GC发生时主线程会暂停导致帧率骤降。优化策略避免在频繁调用的函数如update中创建新对象。特别是new Array(),new Object(),new Vector3()等。尽量复用对象。// 不好每帧都new一个新数组 update(dt) { let posArray [this.node.position.x, this.node.position.y]; // ... 使用 posArray } // 好复用成员变量 private _tempPos: number[] [0, 0]; update(dt) { this._tempPos[0] this.node.position.x; this._tempPos[1] this.node.position.y; // ... 使用 this._tempPos }使用对象池管理纯数据对象不仅仅是节点对于频繁创建销毁的复杂数据对象如技能数据、路径点也可以使用对象池模式。警惕闭包和事件回调它们可能长期持有对外部变量的引用阻止其被回收。使用cc.js.mixin或Object.assign进行浅拷贝而非总是JSON.parse(JSON.stringify(obj))进行深拷贝。7. 实战案例重度MMO手游的优化攻坚战我曾主导过一个中度MMO手游的深度性能优化。优化前在主流中端机上主城场景帧率在25-40之间剧烈波动内存峰值达到1.6GB首次进入战斗场景需要加载超过7秒。7.1 优化前深度剖析我们使用Xcode Instruments和Android Profiler进行了多轮分析定位到核心瓶颈渲染瓶颈主城同屏玩家和NPC众多Draw Call高达220且大量半透明UI叠加导致Overdraw严重。资源瓶颈所有角色和坐骑使用未经压缩的2048纹理且未使用图集纹理内存占用超过800MB。音频文件全部为未压缩的WAV。逻辑瓶颈每个玩家和NPC的AI都在每帧进行复杂的距离判断和状态更新。大量特效的创建和销毁没有使用对象池。加载瓶颈首包包含所有场景的配置和美术资源首次加载巨大。7.2 系统性优化措施落地我们制定了为期6周的优化专项分阶段实施第一阶段资源瘦身2周为所有角色、场景纹理配置了ASTC 6x6Android和PVRTC 4bppiOS压缩。纹理内存从800MB降至约120MB。将UI图标全部打入按功能划分的自动图集Draw Call下降约30%。将BGM转为mp3音效转为webmOpus音频包体大小减少65%。第二阶段渲染重构2周为所有静态场景物件建筑、树木启用静态合批。此一项将场景基础Draw Call从80降到了12。重构UI渲染层级将全屏半透明背景板置于最底层并尽可能将不透明UI元素提前。减少了约40%的Overdraw。为同屏大量重复的3D小怪模型实现了GPU Instancing渲染需要自定义Shader和渲染组件将100个相同模型的Draw Call从100降为1。第三阶段逻辑与内存优化1.5周为子弹、技能特效、伤害数字等实现了统一的对象池管理系统。将NPC的AI更新从每帧改为每3帧分帧更新并为距离玩家过远的NPC执行“休眠”策略停止动画和AI。实现了资源分级加载和异步加载。首包只包含登录、创角、主城核心资源。战斗场景资源在进入战斗前于后台异步加载。第四阶段高级优化0.5周为3D角色模型实现了简单的LODLevel of Detail在距离摄像机超过一定范围时切换为面数更低的模型。引入了遮挡剔除Occlusion Culling的简化版对于固定场景手动标记了不可见区域避免渲染被完全遮挡的物体。7.3 优化效果数据对比优化完成后在同一台中端测试机上指标优化前优化后提升幅度平均帧率 (FPS)3258 (稳定60)81%Draw Call (主城)22565-71%内存峰值1.6 GB850 MB-47%战斗场景加载7.2秒2.1秒-71%安装包大小1.8 GB1.1 GB-39%这个案例深刻地说明性能优化是一个系统工程需要从资源、渲染、逻辑、内存多个维度协同推进而且每一点微小的优化积累起来就能产生质变。8. 构建持续的性能保障体系优化不是一劳永逸的。随着版本迭代、功能增加性能问题可能悄然回归。建立一个持续的性能监控和卡控体系至关重要。8.1 开发期性能检查清单Checklist将以下清单融入团队的代码审查和提测流程[ ]纹理所有纹理是否使用了正确的平台压缩格式尺寸是否为2的幂非必要纹理是否关闭了Mipmap[ ]Draw Call关键场景如主城、战斗同屏Draw Call是否低于目标值如2D50 3D150[ ]合批静态物体是否标记为Static动态物体材质是否一致以促进合批[ ]对象池所有频繁创建销毁的Prefab是否都使用了对象池[ ]内存是否存在已知的、未释放的动态加载资源堆内存是否在场景切换后能稳定回落[ ]加载首包大小是否在目标范围内非首屏资源是否均为异步加载[ ]逻辑update中是否有高频的对象创建或复杂计算能否分帧8.2 自动化性能监控与告警在游戏中内置一个轻量级的性能守护模块在开发版或测试版中运行// 性能守护脚本 export class PerformanceGuard extends cc.Component { private _checkInterval: number 10.0; // 每10秒检查一次 private _timer: number 0; private _fpsThreshold: number 50; private _memoryThreshold: number 1.2 * 1024 * 1024 * 1024; // 1.2GB start() { this.schedule(this._checkPerformance, this._checkInterval); } private _checkPerformance() { const fps cc.debugger.getRenderStats().fps; if (fps this._fpsThreshold) { console.warn([性能告警] 帧率过低: ${fps.toFixed(1)} FPS); // 可以上报到服务器 this._reportToServer(‘low_fps’, { fps }); } // 注意cc.sys.getTotalMemory() 获取的是JS堆内存不是总内存。 // 真机总内存需要通过原生插件获取这里仅为示例。 const usedMemory cc.sys.getTotalMemory(); if (usedMemory this._memoryThreshold) { console.error([性能告警] 内存占用过高: ${(usedMemory / (1024*1024)).toFixed(1)} MB); this._reportToServer(‘high_memory’, { memory: usedMemory }); } } private _reportToServer(type: string, data: any) { // 使用HTTP或WebSocket将性能数据上报到监控后台 // ... } }这个系统可以帮助你在测试阶段就发现性能回归而不是等到上线后由玩家反馈。8.3 性能优化文化从意识到习惯最后也是最重要的是将性能优化的意识融入团队开发文化设立性能基线为项目设定明确的、量化的性能指标KPI如“目标机型稳定50帧”、“内存峰值不超过XX MB”。性能评审在功能设计评审和代码评审中加入性能考量环节。“这个新特效会新增多少Draw Call” “这个数据结构在频繁更新时效率如何”工具赋能为团队提供便捷的性能分析工具和文档让每个人都能快速上手定位问题。经验分享定期组织内部的技术分享将优化案例、踩坑经验沉淀下来形成团队的知识库。性能优化是一场没有终点的马拉松。它没有银弹依靠的是对引擎原理的深刻理解、对目标平台的熟悉、严谨的工程实践和持续不断的细心打磨。希望这份从原理到实战的指南能为你和你的团队提供一份可靠的路线图让你们的Cocos Creator项目跑得更快、更稳、更远。记住最好的优化是那些在设计和编码阶段就考虑进去的优化。