Unity Burst 用户指南(转)

2018-12-02  本文已影响0人  HaruHappy

Burst用户指南

原文:https://docs.unity3d.com/Packages/com.unity.burst@0.2/manual/index.html#memory-aliasing-and-noalias
转自 : https://blog.csdn.net/alph258/article/details/83997917

Unity Burst User Guide
翻译不易,转载请注明原译者。

概观

Burst是一个编译器,它使用LLVM将IL / .NET字节码转换为高度优化的本机代码。它作为Unity包发布,并使用Unity Package Manager集成到Unity中。

快速开始

使用burst编译器编译Job

Burst主要用于与Job系统高效协作。
您可以通过使用属性[BurstCompile]装饰Job结构,从而在代码中简单地使用burst编译器 。

using Unity.Burst;using Unity.Collections;using Unity.Jobs;using UnityEngine;public class MyBurst2Behavior : MonoBehaviour
{
    void Start()
    {
        var input = new NativeArray<float>(10, Allocator.Persistent);
        var output = new NativeArray<float>(1, Allocator.Persistent);
        for (int i = 0; i < input.Length; i++)
            input[i] = 1.0f * i;
        var job = new MyJob
        {
            Input = input,
            Output = output
        };
        job.Schedule().Complete();
        Debug.Log("The result of the sum is: " + output[0]);
        input.Dispose();
        output.Dispose();
    }
    // Using BurstCompile to compile a Job with burst
    // Set CompileSynchronously to true to make sure that the method will not be compiled asynchronously
    // but on the first schedule
    [BurstCompile(CompileSynchronously = true)]
    private struct MyJob : IJob
    {
        [ReadOnly]
        public NativeArray<float> Input;
        [WriteOnly]
        public NativeArray<float> Output;
        public void Execute()
        {
            float result = 0.0f;
            for (int i = 0; i < Input.Length; i++)
            {
                result += Input[i];
            }
            Output[0] = result;
        }
    }
}

默认情况下,在编辑器中,Burst JIT是通过异步来编译job,但在上面的示例中,我们使用该选项CompileSynchronously = true确保在第一个Schedule中编译该方法。通常,您应该使用异步编译。见[BurstCompile]选项

Jobs/Burst Menu菜单

Burst在Jobs菜单中添加了一些菜单项:

Burst 属性面板

从“Jobs”菜单中,您可以打开Burst 属性面板。属性面板允许您查看可以编译的所有作业,然后您还可以检查生成的本机代码。


在这里插入图片描述

在左侧窗格中,我们有Compile Targets,它提供了一个可以编译的Jobs列表。以白色突出显示的作业可以通过Burst 编译,而禁用的作业则不具有该[BurstCompile]属性。
1.从左窗格中选择一个活动的编译目标。
2.在右窗格中,按“ 刷新反汇编 ”按钮
3.在不同选项卡之间切换以显示详细信息:

4.您还可以切换不同的选项:

C#/ .NET语言支持

Burst正在研究.NET的一个子集,它不允许在代码中使用任何托管对象/引用类型(C#中的类)。
以下部分提供了更多有关burst实际支持的构造类型详细信息。

支持的.NET类型

原始类型

Burst支持以下原始类型:

矢量类型

Burst能够将矢量类型从Unity.Mathematics原生SIMD矢量类型转换为优化的第一类支持:

枚举类型

Burst支持所有枚举,包括具有特定存储类型的枚举(例如public enum MyEnum : short)
Burst目前不支持Enum方法(例如Enum.HasFlag)

结构类型

Burst支持具有支持类型的任何字段的常规结构。
Burst支持固定数组字段。
关于布局,LayoutKind.Sequential和LayoutKind.Explicit都受到支持,该StructLayout.Pack包装尺寸不支持
本机支持System.IntPtr和UIntPtr作为直接表示指针的内部结构。

指针类型

Burst支持任何Burst支持类型的指针类型

通用类型

Burst支持结构使用的泛型类型。具体来说,它支持对具有接口约束的泛型类型的泛型调用的完全实例化(例如,当具有通用参数的结构需要实现接口时)

数组类型

Burst不支持托管阵列。例如,您应该使用本机容器NativeArray。

语言支持

Burst支持以下代码流和语法:

内部函数

System.Math

Burst为声明的所有方法提供内在函数,System.Math但不支持以下方法:

System.IntPtr

Burst支持System.IntPtr/的所有方法System.UIntPtr,包括静态字段IntPtr.Zero和IntPtr.Size

System.Threading.Interlocked

Burst支持由System.Threading.Interlocked(例如Interlocked.Increment…等)提供的所有方法的原子内存内在函数
NativeArray
Burst 仅支持以下NativeArray方法的有noalias的内在函数:

优化指南

内存别名(Memory Aliasing)和 noalias

Memory aliasing是一个重要的概念,可以为编译器提供重要的优化,使编译器知道代码如何使用数据。

问题

让我们举一个简单的例子,将数据从输入数组复制到输出数组:

[BurstCompile]private struct CopyJob : IJob
{
    [ReadOnly]
    public NativeArray<float> Input;
    [WriteOnly]
    public NativeArray<float> Output;
    public void Execute()
    {
        for (int i = 0; i < Input.Length; i++)
        {
            Output[i] = Input[i];
        }
    }
}

翻译不易,转载请注明原译者alph258。

没有内存别名:

如果两个数组Input并Output没有轻微重叠,这意味着它们各自的内存位置没有别名,我们将在示例输入/输出上运行此作业后得到以下结果:


在这里插入图片描述

自动矢量化器没有内存别名:

现在,如果编译器是noalias唤醒,它将能够通过所谓的向量化来优化先前的标量循环(在标量级别工作):编译器将代表您重写循环以按小批量处理元素(工作在矢量级别,例如4乘4个元素)像这样:


在这里插入图片描述

内存别名:

接下来,如果由于某些原因(今天很困难的将JobSystem引入),输出数组实际上是将一个元素与输入数组重叠(例如Output[0]实际指向的点Input[1])意味着内存是别名,我们将得到以下内容运行时的结果CopyJob(假设自动矢量化器没有运行):


在这里插入图片描述

使用无效矢量化代码的内存别名:

更糟糕的是,如果编译器不知道这种内存别名,它仍然会尝试自动向量化循环,我们会得到以下结果,这与以前的标量版本不同:


在这里插入图片描述

此代码的结果将无效,如果编译器未识别它们,则可能导致非常严重的错误。

解决方案burst和JobSystem

为了确保Job可以安全地进行矢量化(当有循环时),burst依赖于:

使用noalias分析生成代码的示例

让我们以CopyJob编译到本机代码并禁用noalias分析为例。
以下循环是x64使用启用AVX2的noalias analysis enabled的指令进行编译的结果:(注意我们只复制核心循环,而不是整个方法的完整序言和结尾)
该指令vmovups在这里移动了8个浮点数,因此单个自动向量化循环现在移动4 x 8 = 每个循环迭代复制32个浮点数而不是一个!(因此,相对于与原始循环,将有/ 32次循环步骤迭代)

.LBB0_4:
    vmovups ymm0, ymmword ptr [rcx - 96]
    vmovups ymm1, ymmword ptr [rcx - 64]
    vmovups ymm2, ymmword ptr [rcx - 32]
    vmovups ymm3, ymmword ptr [rcx]
    vmovups ymmword ptr [rdx - 96], ymm0
    vmovups ymmword ptr [rdx - 64], ymm1
    vmovups ymmword ptr [rdx - 32], ymm2
    vmovups ymmword ptr [rdx], ymm3
    sub     rdx, -128
    sub     rcx, -128
    add     rsi, -32
    jne     .LBB0_4
    test    r10d, r10d
    je      .LBB0_8

禁用noalias分析的相同循环将每次循环迭代仅复制一个浮点数:

.LBB0_2:
    mov     r8, qword ptr [rcx]
    mov     rdx, qword ptr [rcx + 16]
    cdqe
    mov     edx, dword ptr [rdx + 4*rax]
    mov     dword ptr [r8 + 4*rax], edx
    inc     eax
    cmp     eax, dword ptr [rcx + 8]
    jl      .LBB0_2

我们可以看到,这里的性能差异很大。这就是为什么noalias 意识到本机代码生成是基础,而这正是burst 试图解决的问题。

编译器选项

编译作业时,可以更改编译器的行为:

准确性

准确性由以下枚举定义:

public enum Accuracy
{
    Std,
    Low,
    Med,
    High,
}

目前,实施仅提供以下准确性:

High对于大多数游戏来说,使用准确度应该足够了。
ULP(最后位置的单位或最小精度的单位)是浮点数之间的间隔,即,一个值的最小精度数字决定了它是否为1.
我们希望支持更多的ULP准确性Med和Low突发的未来版本

编译器放松

编译器松弛由以下枚举定义:

public enum Support
{
    Strict,
    Relaxed
}

通常,某些硬件可以支持乘法和加法(例如mad a * b + c)到单个指令中。使用宽松计算可以允许这些优化。重新排序这些指令会导致精度降低。
使用Relaxed编译器松弛可以用于许多场景,其中严格要求计算的确切顺序和NaN值的统一处理。

同步编译

默认情况下,编辑器中的burst 编译器是采用异步进行编译作业的。
您可以通过设置CompileSynchronously = true的[BurstCompile]属性改变这种行为:

[BurstCompile(CompileSynchronously = true)]public struct MyJob : IJob
{
    // ...
}

Unity.Mathematics

所述Unity.Mathematics提供矢量类型(float4,float3被直接映射到硬件寄存器SIMD …)。
此外,来自math类型的许多功能也直接映射到硬件SIMD指令。
请注意,目前,该库的最佳使用,建议使用SIMD 4位宽类型(float4,int4,bool4…)

独立播放器支持

burst 编译器支持独立播放器。

用法

在构建播放器时,burst将为游戏中的所有突发作业编译单个动态库。根据平台的不同,动态库将输出到不同的文件夹(在Windows上,它位于路径中Data/Plugins/lib_burst_generated.dll)
jobs系统运行时将在由burst编译的第一个作业上加载此库。
启用编译的设置由Jobs/burst 菜单控制,与编辑器相同。
在将来的迭代中,这些设置将被移动到每个平台/播放器的适当设置。

支持的平台

Burst支持以下独立播放器平台:

已知的问题

上一篇下一篇

猜你喜欢

热点阅读