Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

Is there reason to use math.select?

Discussion in 'Entity Component System' started by Micz84, May 20, 2022.

  1. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    451
    I have made some test jobs (code below) to test code generated by Burst for if, c? a:b and math.select. I thought that math.select helps with auto vectorisation. All of these jobs got vectorised and their performance is quite similar (measured with performance tests). So do newer versions of burst handle ifs and ?: better than older ones and are able to predict flow better?

    Code (CSharp):
    1.  
    2.     [BurstCompile]
    3.     public struct IfJob:IJob
    4.     {
    5.         [ReadOnly] public NativeArray<bool> cA;
    6.         [ReadOnly] public NativeArray<int> aA;
    7.         [ReadOnly] public NativeArray<int> bA;
    8.         [WriteOnly] public NativeArray<int> dA;
    9.      
    10.         public void Execute()
    11.         {
    12.             for (var i = 0; i < cA.Length; i++)
    13.             {
    14.                 var a = aA[i];
    15.                 var b = bA[i];
    16.  
    17.                 if( a == b)
    18.                     continue;
    19.              
    20.                 if (a > b)
    21.                 {
    22.                     dA[i] = (a + b)*a;
    23.                 }
    24.                 else
    25.                 {
    26.                     dA[i] = (b - a)*b;
    27.                 }
    28.             }
    29.         }
    30.     }
    31.     [BurstCompile]
    32.     public struct ShortIfJob:IJob
    33.     {
    34.         [ReadOnly] public NativeArray<bool> cA;
    35.         [ReadOnly] public NativeArray<int> aA;
    36.         [ReadOnly] public NativeArray<int> bA;
    37.         [WriteOnly] public NativeArray<int> dA;
    38.      
    39.         public void Execute()
    40.         {
    41.             for (var i = 0; i < cA.Length; i++)
    42.             {
    43.                 var a = aA[i];
    44.                 var b = bA[i];
    45.                 dA[i] = a == b ? dA[i] : (a > b ? (a + b)*a : (b - a)*b);
    46.             }
    47.         }
    48.     }
    49.  
    50.     [BurstCompile]
    51.     public struct SelectIfJob:IJob
    52.     {
    53.         [ReadOnly] public NativeArray<bool> cA;
    54.         [ReadOnly] public NativeArray<int> aA;
    55.         [ReadOnly] public NativeArray<int> bA;
    56.         [WriteOnly] public NativeArray<int> dA;
    57.      
    58.         public void Execute()
    59.         {
    60.             for (var i = 0; i < cA.Length; i++)
    61.             {
    62.                 var a = aA[i];
    63.                 var b = bA[i];
    64.  
    65.                 dA[i] = math.select(math.select((b - a)*b, (a + b)*a, a > b), dA[i], a == b);
    66.             }
    67.         }
    68.     }
    69.  
    70.     [BurstCompile]
    71.     public struct SimpleIfJob:IJob
    72.     {
    73.         [ReadOnly] public NativeArray<bool> cA;
    74.         [ReadOnly] public NativeArray<int> aA;
    75.         [ReadOnly] public NativeArray<int> bA;
    76.         [WriteOnly] public NativeArray<int> dA;
    77.      
    78.         public void Execute()
    79.         {
    80.             for (var i = 0; i < cA.Length; i++)
    81.             {
    82.                 var a = aA[i];
    83.                 var b = bA[i];
    84.  
    85.                 if( a == b)
    86.                     continue;
    87.              
    88.                 if (a > b)
    89.                 {
    90.                     dA[i] = a;
    91.                 }
    92.                 else
    93.                 {
    94.                     dA[i] = b;
    95.                 }
    96.             }
    97.         }
    98.     }
    99.     [BurstCompile]
    100.     public struct SimpleShortIfJob:IJob
    101.     {
    102.         [ReadOnly] public NativeArray<bool> cA;
    103.         [ReadOnly] public NativeArray<int> aA;
    104.         [ReadOnly] public NativeArray<int> bA;
    105.         [WriteOnly] public NativeArray<int> dA;
    106.      
    107.         public void Execute()
    108.         {
    109.             for (var i = 0; i < cA.Length; i++)
    110.             {
    111.                 var a = aA[i];
    112.                 var b = bA[i];
    113.                 dA[i] = a == b ? dA[i] : (a > b ? a :b);
    114.             }
    115.         }
    116.     }
    117.  
    118.     [BurstCompile]
    119.     public struct SimpleSelectIfJob:IJob
    120.     {
    121.         [ReadOnly] public NativeArray<bool> cA;
    122.         [ReadOnly] public NativeArray<int> aA;
    123.         [ReadOnly] public NativeArray<int> bA;
    124.         [WriteOnly] public NativeArray<int> dA;
    125.      
    126.         public void Execute()
    127.         {
    128.             for (var i = 0; i < cA.Length; i++)
    129.             {
    130.                 var a = aA[i];
    131.                 var b = bA[i];
    132.  
    133.                 dA[i] = math.select(math.select(b, a, a > b), dA[i], a == b);
    134.             }
    135.         }
    136.     }
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    6,155
    Did you run tests with OptimizeFor.Performance (see project settings -> AOT Settings) and all safety checks disabled?
     
  3. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    451
    I had safety checks disabled. Optimize For I had set to default I have run with performance and here my results:
    upload_2022-5-20_13-40-13.png
    Parameters
    Arrays size 10000;
    MeasureCount 50,
    Warmup 10,
    IterationsPerMeasure 10
     
  4. Arnold_2013

    Arnold_2013

    Joined:
    Nov 24, 2013
    Posts:
    287
    If I look in the implementation of .select() it is just a "bool ? int : int" with AggressiveInlining attribute. I don't see why you would expect a faster burst code from it. But I would really like to know, if you know :). I have not looked at vectorization a lot, or what burst code looks like.

    Code (CSharp):
    1. [MethodImpl(MethodImplOptions.AggressiveInlining)]
    2. public static int select(int falseValue, int trueValue, bool test) { return test ? trueValue : falseValue; }
    https://github.com/Unity-Technologies/Unity.Mathematics/blob/master/src/Unity.Mathematics/math.cs
     
  5. Anthiese

    Anthiese

    Joined:
    Oct 13, 2013
    Posts:
    73
    Burst processes the IL. Codegen can do whatever it wants in translation to machine code, it doesn't need to use the method implementation in C#, which is why there can be any SIMD optimization at all, seeing as even 4-wide overloads of that still use the ternary operator in the managed implementation. The C# source code doesn't really matter beyond having the implementation work for non-Bursted jobs and quickly seeing what the operation should do.
     
    Micz84 and RaL like this.
  6. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,277
    math.select directly tells the Burst compiler it is a select operation, whereas with a ternary, Burst has to deduce that the operation reduces to a select operation. Many times Burst can figure it out, but sometimes it fails. While I don't have a great example of select failing, I do have a similar example regarding float4 constructors and shuffle operations which I detail in this article: https://github.com/Dreaming381/Lati...imization Adventures/Part 5 - Find Pairs 2.md
     
  7. Micz84

    Micz84

    Joined:
    Jul 21, 2012
    Posts:
    451
    Thing that surprised is that if also has SIMD instructions. So it has figured out how to vectorise it. I am pretty sure that in the past if and ?: where breaking auto vectorisation.