I am opening discussion about https://blogs.unity3d.com/2020/11/25/in-parameters-in-burst/ and copying my comment I've left there. I've been playing around `in` parameters few months back and in my case it turned out to add more assembly lines (probably overhead?) in jobs. I tend to have many small, utility functions that get used commonly in codebase [e.g. `float CalculateTriangleArea(float4 a, float4 b, float4 c)`]. It is also common to have some of their parameters hardcoded in job. I've pressumed that this is the case of compiler being forced to take address of local temporary that is compile-time constant and not investigated further. Example with inlined function: Code (CSharp): [BurstCompile] public struct MyJob : IJob { public readonly struct SomeStruct { public readonly float3 Position; public readonly float4x4 Rotation; public SomeStruct(float3 position, float4x4 rotation) { Position = position; Rotation = rotation; } } public SomeStruct InDataA; public float3 OutData; private static 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, new SomeStruct(math.float3(1,2,3), float4x4.identity)); } } emits: Code (CSharp): vmovsd xmm0, qword ptr [rcx] vbroadcastss xmm1, xmm0 vmulps xmm1, xmm1, xmmword ptr [rcx + 12] vpermilps xmm0, xmm0, 213 vmulps xmm0, xmm0, xmmword ptr [rcx + 28] vaddps xmm0, xmm1, xmm0 vbroadcastss xmm1, dword ptr [rcx + 8] vmulps xmm1, xmm1, xmmword ptr [rcx + 44] vaddps xmm0, xmm0, xmm1 vaddps xmm0, xmm0, xmmword ptr [rip + __xmm@0000000040400000400000003f800000] vmovss dword ptr [rcx + 76], xmm0 vextractps dword ptr [rcx + 80], xmm0, 1 vextractps dword ptr [rcx + 84], xmm0, 2 ret whereas when we add `in` we get an extra `vinsertps` instruction. Thus for: Code (CSharp): private static float3 DoSomething(in SomeStruct a, in SomeStruct b){...} we get: Code (CSharp): vmovsd xmm0, qword ptr [rcx] vinsertps xmm1, xmm0, dword ptr [rcx + 8], 32 vbroadcastss xmm2, xmm0 vmulps xmm2, xmm2, xmmword ptr [rcx + 12] vpermilps xmm0, xmm0, 213 vmulps xmm0, xmm0, xmmword ptr [rcx + 28] vaddps xmm0, xmm2, xmm0 vpermilps xmm1, xmm1, 234 vmulps xmm1, xmm1, xmmword ptr [rcx + 44] vaddps xmm0, xmm1, xmm0 vaddps xmm0, xmm0, xmmword ptr [rip + __xmm@0000000040400000400000003f800000] vmovss dword ptr [rcx + 76], xmm0 vextractps dword ptr [rcx + 80], xmm0, 1 vextractps dword ptr [rcx + 84], xmm0, 2 ret Second example, for which I cannot determine which assembly is "better": `MultiplyRefJob` vs `MultiplyInRefJob` vs `MultiplyInRef2Job`: Code (CSharp): [MethodImpl(MethodImplOptions.NoInlining)] public static float4 Multiply(float4 vec, float v) { return vec * v; } [MethodImpl(MethodImplOptions.NoInlining)] public static float4 MultiplyIn(in float4 vec, in float v) { return vec * v; } [MethodImpl(MethodImplOptions.NoInlining)] public static float4 MultiplyIn2(in float4 vec, float v) { return vec * v; } [BurstCompile] public struct MultiplyRefJob : IJob { public NativeArray<float4> x; public NativeArray<float4> y; public unsafe void Execute() { UnsafeUtility.ArrayElementAsRef<float4>(y.GetUnsafePtr(), 0) = Multiply(UnsafeUtility.ArrayElementAsRef<float4>(x.GetUnsafePtr(), 0), 1000f); } } [BurstCompile] public struct MultiplyInRefJob : IJob { public NativeArray<float4> x; public NativeArray<float4> y; public unsafe void Execute() { UnsafeUtility.ArrayElementAsRef<float4>(y.GetUnsafePtr(), 0) = MultiplyIn(UnsafeUtility.ArrayElementAsRef<float4>(x.GetUnsafePtr(), 0), 1000f); } } [BurstCompile] public struct MultiplyInRef2Job : IJob { public NativeArray<float4> x; public NativeArray<float4> y; public unsafe void Execute() { UnsafeUtility.ArrayElementAsRef<float4>(y.GetUnsafePtr(), 0) = MultiplyIn2(UnsafeUtility.ArrayElementAsRef<float4>(x.GetUnsafePtr(), 0), 1000f); } } MultiplyRefJob has bigger stack (48) than MultiplyInRefJob (32), but `Multiply` second parameter 1000f have been hardcoded into the function body: Code (CSharp): "BurstFoos.Multiply(Unity.Mathematics.float4 vec, float v)_49D95762617E63CF": vbroadcastss xmm0, dword ptr [rip + __real@447a0000] vmulps xmm0, xmm0, xmmword ptr [rcx] ret Probably the winner is `MultiplyInRef2Job`, which has 32-bytes stack and hardcoded 1000f as second parameter. (Assuming optimization for speed, not size, right?). Thus, it seems, that having `in float v` disables hardcoding 1000f into `MultiplyIn` function. I have no idea how it propagates though function chain calls. Anyway, my conclusion: it is better to not use `in` parameters for utility functions. Maybe it is valueable for big functions, but I try to avoid such in jobs. PS. Burst Inspector converts `in` into `ref` in its call name, what suggests that `in` == `ref readonly` is not supported. PPS. I've used the newest Burst package version 1.4.1