In parameters in Burst

The Unity Burst Compiler transforms your C# code into highly optimized machine code. One question that we get often from our amazing forum users like @dreamingimlatios surrounds in parameters to functions within Burst code. Should developers use them and where? We’ve put together this post to try and explain them a bit more in detail.

What are in parameters

C# 7.2 introduced in parameter modifiers as a way to pass something by reference to a function where the called function is not allowed to modify the data.

int Foo(in int a, ref int b) { a = 42; // This would be a compiler error! b = a; // This is fine because b is passed by reference. return a; }

1

2

3

4

5

6

int Foo(in int a, ref int b)

{

    a = 42; // This would be a compiler error!

    b = a; // This is fine because b is passed by reference.

    return a;

}

In parameters are a really useful language concept because it enforces a contract between the developer and the compiler as to how data will be used and modified. The in parameter modifier allows arguments to be passed by reference where the called function is not allowed to modify the data. It pairs up with the out parameter modifier (where parameters must be modified by the function) and the ref parameter modifier (where parameter values may be modified).

Indirect arguments and the ABI

Let’s look at the following simple job:

[BurstCompile] public struct MyJob : IJob { public struct SomeStruct { public float3 Position; public float4x4 Rotation; } public SomeStruct InDataA; public SomeStruct InDataB; public float3 OutData; [MethodImpl(MethodImplOptions.NoInlining)] private float3 DoSomething(SomeStruct a, SomeStruct b) { return math.rotate(a.Rotation, a.Position) + math.rotate(b.Rotation, b.Position); } public unsafe void Execute() { OutData = DoSomething(InDataA, InDataB); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

[BurstCompile]

public struct MyJob : IJob

{

     public struct SomeStruct

     {

         public float3 Position;

         public float4x4 Rotation;

     }

     public SomeStruct InDataA;

     public SomeStruct InDataB;

     public float3 OutData;

     [MethodImpl(MethodImplOptions.NoInlining)]

     private float3 DoSomething(SomeStruct a, SomeStruct b)

   {

         return math.rotate(a.Rotation, a.Position) +

                math.rotate(b.Rotation, b.Position);

   }

     public unsafe void Execute()

     {

         OutData = DoSomething(InDataA, InDataB);

     }

}

The above code can be broken down into:

 Call the DoSomething method which takes two structs passed by value.  It performs some operation on the data, then returns the result (the operation doesn’t really matter for the purposes of this demo).  Note that we’ve placed [MethodImpl(MethodImplOptions.NoInlining)] on the DoSomething method. We do this for two reasons: It lets us pinpoint the specific method in the resulting assembly using the Burst Inspector. It lets us simulate what would happen if the DoSomething method was really a very large function that Burst would not have inlined anyway. 

Now if we pull up the Burst Inspector, we can begin to dive into what is actually produced by the compiler for the above code:

Continue reading

This post was originally published on this site