Search Unity

Is it possible to calculate min/max in parallel?

Discussion in 'Entity Component System' started by torano, Aug 12, 2019.

  1. torano

    torano

    Joined:
    Apr 7, 2017
    Posts:
    46
    Let's say I have a NativeArray of vector3 and since its length is tens of thousands, I want to calculate the min/max of the values in parallel. I know I can do that in a single job(IJob), but not sure if it is possible in parallel(IJobPrallelFor). I got used to job system(without ECS), but it was the first time for me to do parallel programming, so I am not sure how to read/write a value from multiple threads(and even if it is possible with job system). Any help/suggestions appreciated.
     
  2. RecursiveEclipse

    RecursiveEclipse

    Joined:
    Sep 6, 2018
    Posts:
    298
    You can find the min/max for each thread, then in the main thread find the real one.
     
  3. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    328
    Alternatively...you could sort the array in parallel (your choice of algorithm), then anywhere Job/main thread, the first and last index would be your answers.
     
    RecursiveEclipse likes this.
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    You certainly can and I just threw together a very quick benchmark on sorting min/max values of 100,000 float3s in single vs parallel threads and it's faster as you'd expect. However, even with 100,000 float3s in a single thread it's taking only 0.4ms.

    upload_2019-8-12_13-13-44.png

    I just used IJobParallelForBatch to split the work over multiple threads.
    My implementation is pretty naive/simple/hackish just for a quick test so there are likely much nicer ways of implementing this but here ya go.

    Code (CSharp):
    1. using NUnit.Framework;
    2. using Unity.Burst;
    3. using Unity.Collections;
    4. using Unity.Jobs;
    5. using Unity.Mathematics;
    6. using Unity.PerformanceTesting;
    7.  
    8. public class ArraySortTest
    9. {
    10.     private const int TestCount = 100000;
    11.  
    12.     [Test]
    13.     [Performance]
    14.     public void SingleThread()
    15.     {
    16.         var nativeArray = new NativeArray<float3>(TestCount, Allocator.TempJob);
    17.         var random = new Random((uint)UnityEngine.Random.Range(0, int.MaxValue));
    18.  
    19.         Measure.Method(() =>
    20.             {
    21.                 using (var min = new NativeArray<float3>(1, Allocator.TempJob))
    22.                 using (var max = new NativeArray<float3>(1, Allocator.TempJob))
    23.                 {
    24.                     new SingleThreadJob
    25.                     {
    26.                         ToSort = nativeArray,
    27.                         Min = min,
    28.                         Max = max,
    29.                     }.Schedule().Complete();
    30.                 }
    31.             })
    32.             .SetUp(() =>
    33.             {
    34.                 for (var i = 0; i < TestCount; i++)
    35.                 {
    36.                     nativeArray[i] = random.NextFloat3();
    37.                 }
    38.             })
    39.             .Run();
    40.  
    41.         nativeArray.Dispose();
    42.     }
    43.  
    44.     [Test]
    45.     [Performance]
    46.     public void MultiThread()
    47.     {
    48.         var nativeArray = new NativeArray<float3>(TestCount, Allocator.TempJob);
    49.         var random = new Random((uint)UnityEngine.Random.Range(0, int.MaxValue));
    50.  
    51.         Measure.Method(() =>
    52.             {
    53.                 using (var minQueue = new NativeQueue<float3>(Allocator.TempJob))
    54.                 using (var maxQueue = new NativeQueue<float3>(Allocator.TempJob))
    55.                 using (var min = new NativeArray<float3>(1, Allocator.TempJob))
    56.                 using (var max = new NativeArray<float3>(1, Allocator.TempJob))
    57.                 {
    58.                     var handle = new MultiThreadJob
    59.                     {
    60.                         ToSort = nativeArray,
    61.                         Min = minQueue.AsParallelWriter(),
    62.                         Max = maxQueue.AsParallelWriter(),
    63.                     }.ScheduleBatch(TestCount, 1024);
    64.  
    65.                     new MultiThreadCombine
    66.                     {
    67.                         Mins = minQueue,
    68.                         Maxs = maxQueue,
    69.                         Min = min,
    70.                         Max = max,
    71.                     }.Schedule(handle).Complete();
    72.                 }
    73.             })
    74.             .SetUp(() =>
    75.             {
    76.                 for (var i = 0; i < TestCount; i++)
    77.                 {
    78.                     nativeArray[i] = random.NextFloat3();
    79.                 }
    80.             })
    81.             .Run();
    82.  
    83.         nativeArray.Dispose();
    84.     }
    85.  
    86.     [BurstCompile]
    87.     private struct SingleThreadJob : IJob
    88.     {
    89.         [ReadOnly]
    90.         public NativeArray<float3> ToSort;
    91.  
    92.         // Output
    93.         public NativeArray<float3> Min;
    94.  
    95.         public NativeArray<float3> Max;
    96.  
    97.         public void Execute()
    98.         {
    99.             float3 min = new float3(float.MaxValue);
    100.             float3 max = new float3(float.MinValue);
    101.  
    102.             for (var i = 0; i < this.ToSort.Length; i++)
    103.             {
    104.                 var f = this.ToSort[i];
    105.  
    106.                 if (f.x < min.x) min.x = f.x;
    107.                 if (f.y < min.y) min.y = f.y;
    108.                 if (f.z < min.z) min.z = f.z;
    109.                 if (f.x > max.x) max.x = f.x;
    110.                 if (f.y > max.y) max.y = f.y;
    111.                 if (f.z > max.z) max.z = f.z;
    112.  
    113.             }
    114.  
    115.             this.Min[0] = min;
    116.             this.Max[0] = max;
    117.         }
    118.     }
    119.  
    120.     [BurstCompile]
    121.     private struct MultiThreadJob : IJobParallelForBatch
    122.     {
    123.         [ReadOnly]
    124.         public NativeArray<float3> ToSort;
    125.  
    126.         // Output
    127.         public NativeQueue<float3>.ParallelWriter Min;
    128.  
    129.         public NativeQueue<float3>.ParallelWriter Max;
    130.  
    131.         public void Execute(int startIndex, int count)
    132.         {
    133.             float3 min = new float3(float.MaxValue);
    134.             float3 max = new float3(float.MinValue);
    135.  
    136.             for (var i = startIndex; i < count; i++)
    137.             {
    138.                 var f = this.ToSort[i];
    139.  
    140.                 if (f.x < min.x) min.x = f.x;
    141.                 if (f.y < min.y) min.y = f.y;
    142.                 if (f.z < min.z) min.z = f.z;
    143.                 if (f.x > max.x) max.x = f.x;
    144.                 if (f.y > max.y) max.y = f.y;
    145.                 if (f.z > max.z) max.z = f.z;
    146.  
    147.             }
    148.  
    149.             this.Min.Enqueue(min);
    150.             this.Min.Enqueue(max);
    151.         }
    152.     }
    153.  
    154.     [BurstCompile]
    155.     private struct MultiThreadCombine : IJob
    156.     {
    157.         public NativeQueue<float3> Mins;
    158.  
    159.         public NativeQueue<float3> Maxs;
    160.  
    161.         // Output
    162.         public NativeArray<float3> Min;
    163.         public NativeArray<float3> Max;
    164.  
    165.         public void Execute()
    166.         {
    167.             float3 min = new float3(float.MaxValue);
    168.  
    169.             while (this.Mins.TryDequeue(out var f))
    170.             {
    171.                 if (f.x < min.x) min.x = f.x;
    172.                 if (f.y < min.y) min.y = f.y;
    173.                 if (f.z < min.z) min.z = f.z;
    174.             }
    175.  
    176.             float3 max = new float3(float.MinValue);
    177.  
    178.             while (this.Maxs.TryDequeue(out var f))
    179.             {
    180.                 if (f.x > max.x) max.x = f.x;
    181.                 if (f.y > max.y) max.y = f.y;
    182.                 if (f.z > max.z) max.z = f.z;
    183.             }
    184.  
    185.             this.Min[0] = min;
    186.             this.Max[0] = max;
    187.         }
    188.     }
    189. }
    190.  
    ~now back to working on a problem that's annoying me sigh
     
  5. torano

    torano

    Joined:
    Apr 7, 2017
    Posts:
    46
    Looks great. I'll definitely try it out! Thanks.