33.协同程序优化

33.性能优化-CPU-脚本-协程


33.1 知识点

协同程序的额外开销

上节课的 Update 优化里提到,可以通过降低 Update 的执行频率来提升效率,很容易想到用协程来实现(例如 yield return new WaitForSeconds(0.25f) 再执行逻辑)。当时没有推荐协程,主要是因为协程本身会带来额外开销

与标准函数调用相比:

  1. 启动协程有额外成本,大约相当于标准函数调用的 3 倍左右。
  2. 启动协程会额外分配内存,用于保存当前协程的状态。
  3. 协程里使用 yield 时,每次执行到 yield(如 WaitForSecondsWaitForEndOfFrame),都会再次产生类似的调度与状态维护开销。

因此在使用协程时,要权衡:这些成本是否小于它带来的便利(如代码可读性、少写计时变量等),再决定是否用协程。

利用 Update 替代协程处理时间

某些只跟时间相关的逻辑(例如倒计时、隔一段时间执行一次),完全可以在 Update 里用 Time.deltaTime 累加计时实现,不必开协程,往往更省开销。

private float _timer;

void Update()
{
    _timer += Time.deltaTime;
    if (_timer >= 1f)
    {
        // 每秒执行一次的逻辑
        _timer = 0f;
    }
}

避免协程中逻辑过于复杂

若协程里嵌套调用其他协程、状态跳转很多,维护成本会变高,问题难以追踪、调试也困难。应尽量避免把复杂流程全部塞进协程,必要时用状态机或拆成多个简单协程。

避免大量并发的协程

若场景中大量对象同时启动协程,会带来可观的调度开销和 GC 压力。应避免「每个对象都自己开一堆协程」的做法。

例如:对象需要定时执行某段逻辑时,可以优先用上节课的 Update 优化手段——降低执行频率(Update 里按间隔执行)、自定义 Update 管理器(统一在一个 Update 里驱动),而不是每个对象各起一个协程。

善用 WaitForSecondsRealtime

WaitForSecondsRealtimeWaitForSeconds 的区别在于:

  • WaitForSecondsRealtime:不受 Time.timeScale 影响,按真实时间等待。
  • WaitForSeconds:受 Time.timeScale 影响,缩放后时间会变快/变慢。

需要「和游戏暂停无关的等待」时(如 UI 动画、真实时间倒计时),用 WaitForSecondsRealtime;需要「跟游戏时间同步的等待」时,用 WaitForSeconds。按需求选择即可。

// 受 Time.timeScale 影响
yield return new WaitForSeconds(1f);

// 不受 Time.timeScale 影响,按真实时间
yield return new WaitForSecondsRealtime(1f);

利用 C# 的 async/await 替代协程

async/await 在性能与可读性上通常优于协程(无额外协程调度、状态机由编译器生成)。但 Unity 自带的不少异步 API 最初是为协程设计的,并不直接返回 Task,无法直接 await。

可以借助第三方库,让原本配合协程使用的 API 也能配合 async/await 使用,例如:

在允许引入依赖的前提下,用 UniTask 等替代协程做异步流程,往往能获得更好的性能和更清晰的代码结构。


33.2 知识点代码

Lesson33_性能优化_CPU_脚本_协程.cs

public class Lesson33_性能优化_CPU_脚本_协程
{
    #region 知识点一 协同程序的额外开销

    /*
     * 降低 Update 执行频率时容易想到协程,但协程有额外开销:
     * 1. 启动成本约为标准函数调用的 3 倍
     * 2. 启动时会额外分配内存保存状态
     * 3. 每次 yield 都会再次产生调度与状态维护开销
     * 使用协程前要权衡:成本是否小于它带来的好处。
     */

    #endregion

    #region 知识点二 利用Update替代协程处理时间

    /* 基于时间的操作(如倒计时)可在 Update 中配合 deltaTime 实现,无需协程 */

    #endregion

    #region 知识点三 避免协程中逻辑过于复杂

    /* 协程嵌套、状态跳转多时维护成本高、难追踪、调试困难,应尽量避免 */

    #endregion

    #region 知识点四 避免大量并发的协程

    /*
     * 大量对象同时启动协程会造成调度开销和 GC 压力。
     * 定时逻辑可优先用 Update 优化手段:降低执行频率、自定义 Update 管理器等。
     */

    #endregion

    #region 知识点五 善用WaitForSecondsRealtime

    /* WaitForSecondsRealtime 不受 timeScale 影响,WaitForSeconds 会;按需选择 */

    #endregion

    #region 知识点六 利用C#的async/await相关方案替代协程

    /*
     * async/await 性能与可读性通常优于协程。
     * Unity 部分 API 不直接支持 Task,可用第三方:UniTask、Unity3dAsyncAwaitUtil 等。
     */

    #endregion
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏