当前位置: 首页 > news >正文

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容器虽然也快一点,但需要自己释放。

相关文章:

  • DeepSeek助力科研:文献处理新方式
  • git管理的项目 发布时有收费版/免费版/客户定制版,如何管理分支,通过merge(合并) 还是 cherry-pick(挑拣) 引入更新的代码?
  • 数据结构之八大排序算法详解
  • 看视频学习方法总结
  • 深入解析XXL-JOB任务调度执行原理
  • 技术问题汇总:前端怎么往后端传一个数组?
  • 8 SpringBoot进阶(上):AOP(面向切面编程技术)、AOP案例之统一操作日志
  • 对 Steam 下载的一次猜想
  • ArcGIS Pro实战技巧:灵活运用线条精准分割与裁切面要素
  • 使用 Spring Boot 和 Keycloak 的 OAuth2 快速指南
  • 单细胞marker基因表达密度图-(还有一个包装函数)
  • 文本条件生成
  • Kafka生产者相关
  • RabbitMq-消息确认机制-消息队列可靠投递
  • [arXiv 2025]BP-GPT: Auditory Neural Decoding Using fMRI-prompted LLM
  • ChatGPT Deep Research:重塑智能研究的未来边界
  • 计算机毕业设计SpringBoot+Vue.js宠物领养系统 (源码+文档+PPT+讲解)
  • Linux操作系统:基于ELK栈的日志分析系统与数据爬虫的设计与实现
  • SDF,占用场,辐射场简要笔记
  • 如何使用clip模型进行OOD
  • 用html5做的旅游网站代码/公司推广咨询
  • 照片编辑在线/搜索引擎营销优化诊断训练
  • 佛山企业网站推广/品牌传播推广方案
  • 做网站开发学什么软件/免费视频外链生成推荐
  • 专业做包包的网站/北京优化网站公司
  • wordpress 评论倒序/seo sem是指什么意思