Files
Convention-Unity-Demo/.tasks/2025-12-01_1_performance_optimization_aggressive.md

18 KiB
Raw Blame History

背景

文件名2025-12-01_1_performance_optimization_aggressive.md 创建于2025-12-01 创建者User 主分支main 任务分支task/performance_optimization_aggressive_2025-12-01_1 Yolo模式Off

任务描述

对Unity音游编辑器项目进行激进的、彻底的性能优化使用最先进的技术栈。项目限定在Windows 10+平台运行。

核心问题

基于Tracy Profiler分析结果

  1. SplineNode.ScriptUpdate: 5.66ms (19.37%), 3,240次调用 - 函数调用开销过大
  2. 递归Update模式: 深度树形遍历导致缓存不友好
  3. 剪枝不彻底: 大量对象在非活跃时间范围内仍在Update
  4. Timeline UI更新: 存在严重性能开销(已知问题)

性能目标

  • 将SplineNode.ScriptUpdate从5.66ms优化到<0.5ms10倍以上提升
  • 帧率从77fps提升到120fps+
  • 消除不必要的Update调用减少70%+调用次数)

项目概览

  • 项目类型: Unity音游编辑器
  • 核心框架: Convention框架 + RScript脚本系统基于FLEE
  • 主要系统:
    • ScriptableObject树形更新系统
    • Updatement插值系统
    • IInteraction多级判定系统
    • SplineCore样条线渲染
    • TimelineScriptObject时间轴对象

技术栈

  • Unity 6.2 (6000.2.51)
  • C# (支持最新特性)
  • Tracy Profiler 0.11.1
  • Windows 10+ 专用

⚠️ 警告:永远不要修改此部分 ⚠️

RIPER-5核心规则摘要

  • 必须在响应开头声明当前模式:[MODE: MODE_NAME]
  • RESEARCH模式只读取和理解禁止建议和实施
  • INNOVATE模式讨论多种方案禁止具体实施
  • PLAN模式创建详尽技术规范必须转换为编号清单
  • EXECUTE模式只实施已批准计划禁止偏离
  • REVIEW模式验证实施与计划的完全一致性

关键原则:

  • 未经明确许可不得在模式间转换
  • EXECUTE模式必须100%忠实遵循计划
  • 标记任何偏差,无论多小
  • 使用中文响应(除模式声明和代码) ⚠️ 警告:永远不要修改此部分 ⚠️

分析

当前架构分析

1. Update调用链存在问题

GameController.Update()
  └─ RootObject.ScriptUpdate()
      └─ foreach (child in UpdateChilds)
          └─ child.ScriptUpdate()  // 递归3,240次
              └─ child.UpdateTicks()  // 虚函数调用
                  └─ 具体逻辑

性能问题

  • 递归调用开销累积
  • 虚函数调用开销
  • 缓存不友好(对象内存分散)
  • 无法并行化
  • 大量对象在非活跃时间范围内仍被调用

2. 现有优化机制(不足)

代码中存在一些优化:

  • UpdatePerFrame: 降低Update频率
  • IsEnableUpdate: 静态剪枝(只能剪掉永远不更新的对象)
  • Update树剪枝简化单子节点调用链

不足之处

  • 无法剪枝"暂时不活跃"的对象
  • 无法利用多核CPU
  • 内存布局对缓存不友好

3. 技术约束

  • Unity主线程限制Transform等API必须在主线程调用
  • MonoBehaviour限制继承自MonoBehaviour的对象管理复杂

可用的先进技术

Windows 10+ 专属优势

  1. SIMD指令集: AVX2, AVX-512如果CPU支持
  2. 现代CPU特性: 更多核心,更好的缓存
  3. .NET 最新特性: Span, Memory, stackalloc
  4. Unity DOTS: ECS + Job System + Burst Compiler

Unity DOTS技术栈

  1. ECS (Entity Component System)

    • 数据驱动架构
    • 缓存友好的内存布局
    • 易于并行化
  2. C# Job System

    • 托管的多线程系统
    • 自动线程池管理
    • 安全检查避免竞态条件
  3. Burst Compiler

    • 将C#编译为高度优化的机器码
    • SIMD自动向量化
    • 接近C/C++的性能
  4. Collections Package

    • NativeArray, NativeList等非托管集合
    • 可在Job中安全使用
    • 避免GC压力

性能提升潜力

技术 预期提升 适用场景
时间范围剪枝 3-5倍 所有TimelineObject
扁平化调度 2-3倍 Update调用链
Burst编译 5-10倍 数学密集计算
Job并行 2-4倍 批量数据处理
缓存优化 2-3倍 数据访问模式
综合 30-100倍 整体系统

提议的解决方案

方案概览

采用混合架构保留MonoBehaviour作为外壳内部使用ECS数据处理。

架构设计

传统层MonoBehaviour
    ↓ 数据同步
ECS数据层Burst Job处理
    ↓ 结果同步
传统层应用到Transform

核心方案要素

1. 时间分片调度系统

目标: 只更新活跃时间范围内的对象

实现:

  • 使用SortedSet维护时间排序
  • 激活/停用事件驱动
  • 扁平化对象列表(无递归)

预期: 减少70%无效Update调用

2. ECS化核心系统

2.1 Updatement系统最适合ECS

  • 纯数据Entry列表、插值参数
  • 纯计算Lerp、曲线求值
  • 可完全Burst编译和并行化

2.2 Interaction判定系统

  • 输入处理可并行化
  • 判定计算可Burst优化
  • 结果批量应用

2.3 SplineNode计算

  • 样条线计算适合SIMD
  • 批量计算所有节点位置
  • Burst编译获得巨大提升

3. Burst编译的Job

[BurstCompile]
public struct UpdatementCalculationJob : IJobParallelFor
{
    [ReadOnly] public float CurrentTime;
    [ReadOnly] public NativeArray<UpdatementEntry> Entries;
    [ReadOnly] public NativeArray<int> ContentIndices;
    
    [WriteOnly] public NativeArray<float3> Results;
    
    public void Execute(int index)
    {
        // 纯数学计算Burst自动SIMD优化
        int content = ContentIndices[index];
        float percent = CalculatePercent(CurrentTime, Entries, content);
        Results[index] = math.lerp(
            Entries[content].Position,
            Entries[content + 1].Position,
            EvaluateCurve(percent, Entries[content].CurveType)
        );
    }
}

4. 内存布局优化

SoA (Structure of Arrays) 代替 AoS (Array of Structures):

// 坏AoS缓存不友好
struct UpdatementData {
    float timePoint;
    Vector3 position;
    EaseCurveType curve;
}
UpdatementData[] data; // 数据交错

// 好SoA缓存友好
struct UpdatementDataSoA {
    NativeArray<float> timePoints;     // 连续
    NativeArray<float3> positions;     // 连续
    NativeArray<byte> curveTypes;      // 连续
}

5. 对象池系统

  • 避免GC压力
  • 重用NativeArray
  • 预分配Job句柄

实施分层

Phase 1: 基础设施1周

  1. 时间分片调度器
  2. UpdateScheduler基类
  3. 基础性能测试框架

Phase 2: ECS核心2周

  1. Updatement系统ECS化
  2. Burst Job实现
  3. 数据同步机制

Phase 3: 扩展优化2周

  1. SplineNode优化
  2. Interaction判定优化
  3. 对象池和内存管理

Phase 4: 深度优化1周

  1. SIMD手动优化关键路径
  2. 内存布局调优
  3. 性能剖析和微调

技术细节

Job依赖管理

// 计算Job
JobHandle calcHandle = calculationJob.Schedule(count, 64);

// 应用Job依赖计算Job
JobHandle applyHandle = applyJob.Schedule(calcHandle);

// 等待完成
applyHandle.Complete();

安全检查

Unity Job System提供编译时和运行时安全检查

  • 检测数据竞争
  • 验证NativeArray生命周期
  • 防止悬空指针

Profiler集成

保持Tracy Profiler集成

using (Profiler.BeginZone("BurstJob.Schedule"))
{
    handle = job.Schedule(count, 64);
}

风险与挑战

技术风险

  1. 学习曲线: ECS和Burst需要学习
  2. 调试难度: Burst代码调试较困难
  3. 兼容性: 现有脚本系统集成

缓解措施

  1. 渐进式迁移,保持向后兼容
  2. 完善的性能测试
  3. 详细的文档和注释

回滚计划

每个Phase完成后打tag必要时可回滚

当前执行步骤:

"阶段4.8 - 阶段4整体测试"

任务进度

阶段1基础设施搭建

1.1 创建目录结构

[2025-12-01 17:16:16]

  • 已修改:创建目录 Assets/Scripts/Framework/UpdateScheduler/
  • 更改新建UpdateScheduler模块目录
  • 原因:为扁平化调度器准备代码组织结构
  • 阻碍因素:无
  • 状态:未确认

1.2 创建IUpdateable接口

[2025-12-01 17:17:30]

  • 已修改:创建文件 IUpdateable.cs (48行)
  • 更改定义IUpdateable接口3个方法和UpdateMode枚举3个值
  • 原因为ScriptableObject提供统一的Update接口支持扁平化调度
  • 阻碍因素:无
  • 状态:未确认

1.3 创建UpdateScheduler核心类

[2025-12-01 17:18:45]

  • 已修改:创建文件 UpdateScheduler.cs (201行)
  • 更改实现完整的扁平化调度器Register、DoUpdate、时间分片、Reset/Clear等
  • 原因提供时间分片和扁平化Update管理的核心实现
  • 阻碍因素:无
  • 状态:未确认

1.4 RootObject添加Scheduler字段

[2025-12-01 17:20:15]

  • 已修改RootObject.cs (添加1行Line 19)
  • 更改添加Scheduler字段并初始化
  • 原因为RootObject提供调度器实例
  • 阻碍因素:无
  • 状态:未确认

1.5 RootObject添加清理逻辑

[2025-12-01 17:21:30]

  • 已修改RootObject.cs UnloadScript方法 (添加2行Line 38-39)
  • 更改在卸载时调用Scheduler.Clear()
  • 原因:防止内存泄漏,正确释放注册对象
  • 阻碍因素:无
  • 状态:未确认

1.6 阶段1整体测试

[2025-12-01 17:22:45]

  • 已修改阶段1所有代码完成等待测试
  • 更改基础设施搭建完成3个新文件RootObject修改2处
  • 原因验证阶段1无破坏性影响
  • 测试项目:
    1. 编译项目 - 已通过无linter错误
    2. 运行编辑器 - 用户确认成功
    3. 打开项目 - 用户确认成功
    4. 检查RootObject.Scheduler字段 - 用户确认成功
    5. 关闭项目 - 用户确认成功
    6. 检查控制台无错误 - 用户确认成功
  • 阻碍因素:无
  • 状态: 成功
  • Commit: 用户已提交

阶段2接口适配

2.1 ScriptableObject添加IUpdateable接口声明

[2025-12-01 17:30:00]

  • 已修改ScriptableObject.cs 类声明 (Line 582添加IUpdateable接口)
  • 更改ScriptableObject实现IUpdateable接口
  • 原因使所有ScriptableObject可以被调度器管理
  • 阻碍因素:无
  • 状态:未确认

2.2 添加Update模式字段

[2025-12-01 17:32:15]

  • 已修改ScriptableObject.cs (添加3个字段Line 111-116)
  • 更改添加_updateMode、_activeStartTime、_activeEndTime字段
  • 原因存储对象的Update模式和时间范围
  • 阻碍因素:无
  • 状态:未确认

2.3 添加虚方法供子类重写

[2025-12-01 17:34:30]

  • 已修改ScriptableObject.cs (添加2个虚方法Line 175-183)
  • 更改添加GetUpdateMode和GetActiveTimeRange虚方法
  • 原因允许子类自定义Update模式和时间范围
  • 阻碍因素:无
  • 状态:未确认

2.4 实现IUpdateable接口方法

[2025-12-01 17:36:45]

  • 已修改ScriptableObject.cs (添加新的partial class约35行Line 916-950)
  • 更改实现IUpdateable的3个接口方法ExecuteUpdate、GetUpdateName、IsUpdateReady
  • 原因提供扁平化Update入口复用原有UpdateTicks逻辑
  • 阻碍因素:无
  • 状态:未确认

2.4.1 重命名方法为FlatOptimizationUpdate更具描述性

[2025-12-01 17:40:00]

  • 已修改IUpdateable.cs、UpdateScheduler.cs、ScriptableObject.cs
  • 更改将方法重命名为FlatOptimizationUpdate明确表达"扁平化优化Update"的含义
  • 原因:提高代码可读性,方法名清晰表达优化目的
  • 阻碍因素:无
  • 状态:未确认

2.5 阶段2整体测试

[2025-12-01 17:38:00]

  • 已修改阶段2所有代码完成等待测试
  • 更改ScriptableObject完整实现IUpdateable接口添加3个字段、2个虚方法、3个接口方法
  • 原因:验证接口实现无破坏性影响
  • 测试项目:
    1. 编译项目 - 已通过无linter错误
    2. 运行编辑器 - 用户确认成功
    3. 打开项目 - 用户确认成功
    4. 检查Inspector中新增字段是否可见 - 用户确认成功
    5. 验证游戏功能不受影响 - 用户确认成功
  • 阻碍因素:无
  • 状态: 成功
  • Commit: 用户已提交

阶段3可选注册机制

3.1 添加注册控制字段

[2025-12-01 17:45:00]

  • 已修改ScriptableObject.cs (添加1个字段Line 120)
  • 更改添加_useUpdateScheduler控制字段默认false
  • 原因:提供可选的调度器注册开关,默认不影响现有逻辑
  • 阻碍因素:无
  • 状态:未确认

3.2 添加脚本方法启用调度器

[2025-12-01 17:47:15]

  • 已修改ScriptableObject.cs (添加EnableUpdateScheduler方法Line 178-183)
  • 更改添加RScript可调用的启用方法
  • 原因:允许脚本中手动启用调度器
  • 阻碍因素:无
  • 状态:未确认

3.3 修改ApplyScript添加注册逻辑

[2025-12-01 17:49:30]

  • 已修改ScriptableObject.cs ApplyScript方法 (添加5行Line 678-682)
  • 更改在ApplyScript中添加可选注册调用
  • 原因:对象应用时自动注册到调度器(如果启用)
  • 阻碍因素:无
  • 状态:未确认

3.4 实现RegisterToScheduler方法

[2025-12-01 17:51:45]

  • 已修改ScriptableObject.cs (添加RegisterToScheduler私有方法约25行Line 684-708)
  • 更改:实现调度器注册逻辑,包含错误处理和日志
  • 原因封装注册流程自动获取Update模式和时间范围
  • 阻碍因素:无
  • 状态:未确认

3.5 阶段3整体测试

[2025-12-01 17:53:00]

  • 已修改阶段3所有代码完成等待测试
  • 更改实现可选注册机制添加1个字段、1个公开方法、1个私有方法、修改ApplyScript
  • 原因:验证注册机制正确工作
  • 测试项目:
    1. 编译项目 - 已通过无linter错误
    2. 运行编辑器 - 待测试
    3. 在测试脚本中调用EnableUpdateScheduler() - 待测试
    4. 检查控制台日志,确认对象被注册 - 待测试
    5. 验证默认行为不变_useUpdateScheduler=false - 待测试
  • 阻碍因素:无
  • 状态:未确认 → 等待用户确认:成功/不成功?

阶段4双轨运行和实际调用

4.1 GameController添加控制字段

[2025-12-01 18:00:00]

  • 已修改GameController.cs (添加1个字段Line 22)
  • 更改添加_enableSchedulerUpdate控制字段默认false
  • 原因控制GameController使用新旧Update方式
  • 阻碍因素:无
  • 状态:未确认

4.2 修改GameController主Update循环

[2025-12-01 18:02:15]

  • 已修改GameController.cs Update方法主循环 (修改8行Line 318-329)
  • 更改添加新旧Update方式分支支持调用FlatOptimizationUpdate或ScriptUpdate
  • 原因允许运行时切换Update方式进行对比
  • 阻碍因素:无
  • 状态:未确认

4.3 修改GameController滚动时间轴Update调用

[2025-12-01 18:04:30]

  • 已修改GameController.cs Update方法滚动逻辑 (修改4行Line 373-376)
  • 更改:滚动时间轴也支持新旧方式切换
  • 原因确保所有Update调用点一致
  • 阻碍因素:无
  • 状态:未确认

4.4 修改GameController滚动Reset逻辑

[2025-12-01 18:05:45]

  • 已修改GameController.cs Update方法滚动Reset (修改10行Line 378-388)
  • 更改:滚动重置也支持新旧方式切换
  • 原因确保Reset调用点一致
  • 阻碍因素:无
  • 状态:未确认

4.5 修改ForceScriptUpdate方法

[2025-12-01 18:07:00]

  • 已修改GameController.cs ForceScriptUpdate方法 (修改5行Line 294-299)
  • 更改:强制更新也支持新旧方式切换
  • 原因确保所有Update入口一致
  • 阻碍因素:无
  • 状态:未确认

4.6 修改Stop/Pause/Play方法

[2025-12-01 18:08:15]

  • 已修改GameController.cs Stop/Pause/Play方法 (修改15行Line 263-292)
  • 更改:播放控制方法也支持新旧方式切换
  • 原因确保播放控制的Update调用一致
  • 阻碍因素:无
  • 状态:未确认

4.7 RootObject.UpdateTicks调用调度器关键

[2025-12-01 18:10:30]

  • 已修改RootObject.cs UpdateTicks方法 (添加10行Line 111-120)
  • 更改RootObject调用Scheduler.DoUpdate()进行扁平化Update移除base.UpdateTicks()调用
  • 原因这是关键步骤让Scheduler实际被调用建立完整调用链
  • 阻碍因素:无
  • 状态:未确认

4.7.1 修复崩溃问题 - 条件调用Scheduler

[2025-12-01 18:15:00]

  • 已修改RootObject.cs UpdateTicks方法 (修改逻辑Line 112-124)
  • 更改添加条件判断仅在Scheduler有注册对象时调用Scheduler否则调用base.UpdateTicks()保持旧行为
  • 原因:修复旧方式下的崩溃问题,避免双重更新和冲突
  • 阻碍因素:无
  • 状态:未确认

4.8 阶段4整体测试

[2025-12-01 18:12:00]

  • 已修改阶段4所有代码完成等待测试
  • 更改实现双轨Update支持新旧方式切换GameController 6处修改RootObject 1处关键修改
  • 原因验证新方式功能正确且性能提升Scheduler实际被调用
  • 测试项目:
    1. 编译项目 - 已通过无linter错误
    2. 旧方式测试_enableSchedulerUpdate=false - 待测试
    3. 新方式测试_enableSchedulerUpdate=true部分对象启用调度器 - 待测试
    4. 功能验证(播放/暂停/停止/滚动时间轴) - 待测试
    5. 性能对比Tracy Profiler数据 - 待测试
  • 阻碍因素:无
  • 状态:未确认 → 等待用户确认:成功/不成功?
  • 调用链验证:
    • 旧方式GameController → MainObject.ScriptUpdate() → 递归
    • 新方式GameController → MainObject.FlatOptimizationUpdate() → RootObject.UpdateTicks() → Scheduler.DoUpdate() → 扁平化

最终审查

[待完成]