Unity Burst编译

官网文档:https://docs.unity3d.com/Packages/com.unity.burst@1.8/manual/index.html

Unity 之Burst 底层原理:https://zhuanlan.zhihu.com/p/623274986

Burst 编译器入门(五):https://developer.unity.cn/projects/5e62f15eedbc2a1feeebb83e

LLVM:https://llvm.org/

LLVM:https://aosabook.org/en/v1/llvm.html

編譯器 LLVM 淺淺玩:https://medium.com/@zetavg/%E7%B7%A8%E8%AD%AF%E5%99%A8-llvm-%E6%B7%BA%E6%B7%BA%E7%8E%A9-42a58c7a7309#20b5

一个故事看懂CPU的SIMD技术:https://www.cnblogs.com/xuanyuan/p/16048303.html

介绍

  • Burst 是一个编译器,目的是把代码跑的更高效。主要优化技术密集的任务。
    • LLVM的激进优化。
      • SIMD向量化:循环操作转为单指令多数据指令。(一个加法变四个加法一起算)
      • 循环展开:if等分支展开,比如经常true的再优化。Hint intrinsics
      • 自动内联:把小函数自动内联展开。
      • 无GC检测:因为操作都是非托管数据,所以省去一些不必要的检测。
  • 2019年官方说的: Burst必须只能在JobSystem中使用,其他地方是不能用的。
  • 从 IL/.NET 字节码转换为使用 LLVM 编译器的优化原生 CPU 代码。
    • C#代码 -> IL -> LLVM IR -> 原生机器码
  • 适用于:
    • 物理碰撞。
    • 粒子系统刷新。
    • 图形算法处理。

LLVM

  • 模块化编译器,是一组库,可以针对特定问题优化。
    在这里插入图片描述
    在这里插入图片描述

和 IL2CPP 的关系

  • 互不影响,两套编译方式。

  • 普通代码走IL2CPP编译成C++机器码。

  • 标记[BurstCompile]的代码,有Burst单独编译优化,生成更高效的机器码。

调试

  • https://docs.unity3d.com/Packages/com.unity.burst@1.8/manual/editor-burst-inspector.html

  • Unity菜单:Jobs/Burst/Open Inspector
    在这里插入图片描述

测试

  • 对比各种遍历耗时

    • 测试了 List<T> 和 定长数组,相同操作都是定长数组更快一点。
  • 测试长度遍历一千万,测了一百万差异差不多。

测试代码

private const int TEST_COUNT = 10000000;
private float[] _managedArray = new float[TEST_COUNT];    // 托管
private NativeArray<float> _nativeArray;                  // 非托管

private void Run()
{
    _nativeArray = new NativeArray<float>(TEST_COUNT, Allocator.Persistent);
    for (int i = 0; i < TEST_COUNT; i++) 
    {
        _managedArray[i] = i;
        _nativeArray[i] = i;
    }
    
    // 测试List,同下面Array
    ...
    // 
    
    Debug.LogError("Test Array");
    using (new MyStopWatch("ManagedArray"))
    {
        for (int i = 0; i < TEST_COUNT; i++) 
            _managedArray[i] = i + 5;
    }
    using (new MyStopWatch("TestNativeArray"))
    {
        for (int i = 0; i < TEST_COUNT; i++) 
            _nativeArray[i] = i + 5;
    }
    using (new MyStopWatch("TestNativeList Job"))
    {
        var job = new NativeArrayJob { Data = _nativeArray };
        job.Run();
    }
    using (new MyStopWatch("TestNativeList Burst Job"))
    {
        var job = new NativeArrayBurstJob { Data = _nativeArray };
        job.Run(); 
    }
    using (new MyStopWatch("TestNativeArray Burst JobParallelFor"))
    {
        // List动态扩容,不支持多线程写入
        var job = new NativeArrayBurstJobParallelFor() { Data = _nativeArray };
        job.Schedule(TEST_COUNT, 64).Complete(); 
    }
    using (new MyStopWatch("TestNativeArray Burst"))
    {
        TestNativeArrayBurst(ref _nativeArray);
    }
    using (new MyStopWatch("TestNativeArray Burst Break"))
    {
        TestNativeArrayBurstBreak(ref _nativeArray);
    }

    // Natvie容器要自己释放
    _nativeList.Dispose();
}

// 普通带Burst编译
[BurstCompile]
private static void TestNativeArrayBurst([NoAlias]ref NativeArray<float> list)
{
    for (int i = 0; i < TEST_COUNT; i++) 
        list[i] = i + 5;
}

// 带Burst编译,但是加分支打断SIMD向量化
[BurstCompile]
private static void TestNativeArrayBurstBreak([NoAlias]ref NativeArray<float> list)
{
    for (int i = 0; i < TEST_COUNT; i++)
    {
        if (i == 0) list[i] = i + 7; // 加个分支,打断Loop vectorization
        list[i] = i + 5;
    }
}

// 普通Job
struct NativeArrayJob : IJob
{
    public NativeArray<float> Data;

    public void Execute()
    {
        for (int i = 0; i < TEST_COUNT; i++) 
            Data[i] = i + 5;
    }
}

// 带Burst的Job
[BurstCompile]
struct NativeArrayBurstJob : IJob
{
    public NativeArray<float> Data;

    public void Execute()
    {
        for (int i = 0; i < TEST_COUNT; i++) 
            Data[i] = i + 5;
    }
}

// 带Burst的多线程Job
[BurstCompile]
struct NativeArrayBurstJobParallelFor : IJobParallelFor
{
    public NativeArray<float> Data;
    public void Execute(int index)
    {
        Data[index] = index + 6;
    }
}

测试结果

  • PC端跑一千万次循环array[i]=i+5

    在这里插入图片描述

  • MUMU模拟器 64位,跑一亿次循环array[i]=i+5

    在这里插入图片描述

安卓跑一亿次array[i]=i+5耗时代码
普通数组80ms在这里插入图片描述
NativeArray32ms在这里插入图片描述
丢到Job中执行34ms在这里插入图片描述
Burst编译的Job31ms在这里插入图片描述
Burst编译的多线程Job。不支持NativeList<T>,因为可能出现动态扩容,导致线程不安全。28ms在这里插入图片描述
普通Burst函数34ms在这里插入图片描述
普通Burst函数,但是打断SIMD向量化78ms在这里插入图片描述
  • 跑一亿次循环array[i]=math.sin(i+5),左图:MUMU模拟器 64位,右图:RedmiNote12
    在这里插入图片描述
    在这里插入图片描述
安卓跑一亿次sin(i+5)模拟器耗时/ms真机耗时/ms
普通数组608015570
NativeArray592215533
丢到Job中执行590315415
Burst编译的Job21251505
Burst编译的多线程Job573332
普通Burst函数21161503
普通Burst函数,但是打断SIMD向量化235208047
Burst编译的Job,但是打断SIMD向量化231658036
Burst编译的多线程Job,但是打断SIMD向量化59441738
  • 模拟器下,同样循环内容下(有ifmath.sin),burst编译反而跑的更慢了。可能中低端设备负优化了?

SIMD向量化

  • https://docs.unity3d.com/Packages/com.unity.burst@1.8/manual/optimization-loop-vectorization.html

  • 如果有分支或者计算结果有前后依赖,就无法向量化。简单的计算可以手动向量化用float4等。
    在这里插入图片描述


总结

  1. 复杂计算效率 Burst + JobParallelFor > Burst + Job >= Burst > Job >= 常规。

  2. 平常开发不涉及大量计算,直接用托管List<T>等常规容器就好了。托管环境Native容器虽然也快一点,但需要自己释放。

### Unity Burst 编译器使用指南 为了充分利用 UnityBurst 编译器来提高性能,需确保已安装正确的 Unity 版本,并满足 Burst 编译器的 AOT(Ahead Of Time)编译需求[^1]。 #### 安装与配置 Burst 编译器支持特定版本的 Unity 和 .NET API 兼容性级别。建议查阅官方文档以确认当前使用的 Unity 版本是否兼容最新版的 Burst 编译器。对于开发环境而言,默认情况下,编辑器中的 burst 编译器会异步执行编译作业;然而,可通过设置 `CompileSynchronously` 属性强制同步编译: ```csharp [BurstCompile(CompileSynchronously = true)] public struct MyJob : IJob { // ... } ``` 此方法有助于调试期间更精确地控制编译过程并减少潜在错误的发生概率[^3]。 #### 性能优化策略 当涉及到基于 Burst 的项目时,在Unity项目中,持续优化应用性能是确保产品稳定性和流畅性的核心任务,特别是在资源受限的移动平台或复杂的3D游戏中。针对此类场景下的性能调优主要集中在以下几个方面: - **内存管理**:合理规划数据结构大小及其生命周期,避免不必要的对象创建和销毁操作,从而减轻垃圾回收压力。 - **计算密集型任务处理**:利用 Job System 结合 Burst 加速多线程运算逻辑,特别是那些涉及大量数值计算的部分,如物理模拟、AI路径查找等。 - **图形渲染管线调整**:精简材质贴图尺寸、降低阴影质量等级以及启用LOD(Level of Detail)技术等方式可以有效缓解GPU负载过重的问题[^2]。 另外值得注意的是 IL2CPP 技术对最终程序运行效率的影响。由于该中间件承担着将C#源码转化为目标平台上可执行指令集的重要职责,因此深入理解其工作机制能够指导开发者绕开某些可能导致性能瓶颈的语言特性和库函数调用方式[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值