Search Unity

Burst CAN use foreach statement now!

Discussion in 'Data Oriented Technology Stack' started by pcysl5edgo, Aug 21, 2019.

  1. pcysl5edgo

    pcysl5edgo

    Joined:
    Jun 3, 2018
    Posts:
    45
    Burst Compiler ver 1.1.0 currently does NOT support 'foreach statement' as it is requiring 'try/finally statement'.

    Today, I made a solution of this problem.

    https://github.com/pCYSl5EDgo/UniEnumExtension

    It is an Unity Editor extension software which supports Unity2018.4, 2019.2 and is provided under GPL-v3 and a commercial license.

    UniEnumExtension internally uses Mono.Cecil to manipulate IL codes and hooks the Player Build Pipeline.

    I know that just foreach with NativeArray&ltT> is not enough. The C# programmers actually need LINQ.
    I've been working for importing LINQ to DOTS world. I will show you UniNativeLinq in a couple months.

    Please try the UniEnumExtension and give me your feedback on that.
     
  2. tarahugger

    tarahugger

    Joined:
    Jul 18, 2014
    Posts:
    104
    I'm not sure what foreach support in burst jobs has to do with enums?

    You can use ref based enumerators with foreach statements in burst.

    For example:

    Code (CSharp):
    1. public unsafe class TestSystem : JobComponentSystem
    2. {
    3.     [BurstCompile]
    4.     public struct TestJob : IJobForEach_B<SomeData>
    5.     {
    6.         [NativeDisableUnsafePtrRestriction]
    7.         public int* Sum;
    8.  
    9.         public void Execute(DynamicBuffer<SomeData> someDataBuffer)
    10.         {
    11.             foreach (ref var item in AsEnumerator(someDataBuffer))
    12.             {
    13.                 *Sum += item.Value;
    14.             }
    15.         }
    16.     }
    17.  
    18.     protected override void OnCreate()
    19.     {
    20.         using (var entities = new NativeArray<Entity>(100, Allocator.Temp))
    21.         {
    22.             var archetype = EntityManager.CreateArchetype(typeof(SomeData));
    23.             EntityManager.CreateEntity(archetype, entities);
    24.  
    25.             for (int i = 0; i < entities.Length; i++)
    26.             {
    27.                 var buffer = EntityManager.GetBuffer<SomeData>(entities[i]);
    28.                 buffer.ResizeUninitialized(1000);
    29.                 for (int j = 0; j < 1000; j++)
    30.                 {
    31.                     buffer[j] = new SomeData { Value = j };
    32.                 }
    33.             }
    34.         }
    35.     }
    36.  
    37.     public struct SomeData : IBufferElementData
    38.     {
    39.         public int Value;
    40.     }
    41.  
    42.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    43.     {
    44.         var result = 0;
    45.         inputDeps = new TestJob
    46.         {
    47.             Sum = &result
    48.  
    49.         }.Run(this, inputDeps);
    50.  
    51.         Debug.Log($"Result = {result}");
    52.         return inputDeps;
    53.     }
    54.  
    55.     public static DynamicBufferEnumerator<T> AsEnumerator<T>(DynamicBuffer<T> buffer) where T : unmanaged, IBufferElementData
    56.     {
    57.         return new DynamicBufferEnumerator<T>(ref buffer);
    58.     }
    59.  
    60.     public struct DynamicBufferEnumerator<T> where T : unmanaged
    61.     {
    62.         public DynamicBufferEnumerator<T> GetEnumerator() => this;
    63.  
    64.         private T* _ptr;
    65.         private int _index;
    66.         private int _maxIndex;
    67.  
    68.         public DynamicBufferEnumerator(ref DynamicBuffer<T> buffer)
    69.         {
    70.             _index = -1;
    71.             _ptr = (T*)buffer.GetUnsafePtr();
    72.             _maxIndex = buffer.Length - 1;
    73.         }
    74.  
    75.         public bool MoveNext()
    76.         {
    77.             if (_index == _maxIndex)
    78.             {
    79.                 return false;
    80.             }
    81.             _index++;
    82.             return true;
    83.         }
    84.  
    85.         public ref T Current => ref UnsafeUtilityEx.ArrayElementAsRef<T>(_ptr, _index);
    86.     }
    87.  
    88. }
     
  3. starikcetin

    starikcetin

    Joined:
    Dec 7, 2017
    Posts:
    235
    Did you link the wrong library perhaps?
     
  4. pcysl5edgo

    pcysl5edgo

    Joined:
    Jun 3, 2018
    Posts:
    45
    Thank you for replying.

    If Enumerator struct implements System.IDisposable (or ref struct has Dispose method in C#8), foreach statement will turn into try/finally.
    Standard Burst compiler cannot process try/finally.
    Your example code works fine because your DynamicBufferEnumerator neither has Dispose method nor implements System.IDisposable.
    I forgot that kind of fact.
    I should change this thread title to "Burst CAN use try/finally statement now!".

    No, I didn't link wrong library.
    I made a library that make the enum type more faster. It is achieved by Mono.Cecil.
    While developing, I found that UniEnumExtension can also rewrite foreach(try-finally) as normal goto/switch statement.
    I add this function to the UniEnumExtension.
    I should have named UniEnumExtension differently.