Search Unity

[SOLVED] mathematics.Random in job without ECS?

Discussion in 'Entity Component System' started by TheDemiurge, Apr 19, 2019.

  1. TheDemiurge

    TheDemiurge

    Joined:
    Jul 26, 2010
    Posts:
    42
    I have an IJobParallelFor that has a Random member in it.
    I initialize this random by calling either InitState() or InitState((uint)Random.Range(int.MinValue, int.MaxValue)), with no real difference in behavior.

    In the job's Execute method, I'm occasionally calling random.NextFloat(), and it always just gets stuck on the same 3-5 values, such as the example below:
    upload_2019-4-19_12-5-6.png

    When I call NextFloat() from the main thread, it works great. Since I can't call UnityEngineRandom.Value in another thread, I was under the impression that this random should be working this way.

    I'm using version 1.0.1 from the Package Manager in Unity 2018.3.12f1.
    Am I missing something? Help.
     
  2. ilih

    ilih

    Joined:
    Aug 6, 2013
    Posts:
    1,416
    IJobParallelFor is run the job in parallel with more than one job instance.
    If you run the job with a single random, then it will always return the same value for different job instances because they use the same seed.
    You need to use different random for different instances: pass an array instead of the single random and use thread index to get random from the array.

    So you should use something like this:
    Code (CSharp):
    1.     public class SomeSystem : ComponentSystem
    2.     {
    3.         struct Job : IJobParallelFor
    4.         {
    5.             [NativeSetThreadIndex]
    6.             private int threadIndex;
    7.  
    8.             [NativeDisableParallelForRestriction]
    9.             [DeallocateOnJobCompletion]
    10.             public NativeArray<Unity.Mathematics.Random> RandomGenerator;
    11.  
    12.             public void Execute(int index)
    13.             {
    14.                 var rnd = RandomGenerator[threadIndex];
    15.                 var value = rnd.NextInt();
    16.                 RandomGenerator[threadIndex] = rnd;
    17.  
    18.                 // do something with value
    19.             }
    20.         }
    21.  
    22.         Unity.Mathematics.Random Rnd = new Unity.Mathematics.Random(1);
    23.  
    24.         NativeArray<Unity.Mathematics.Random> RandomGenerator;
    25.  
    26.         protected override void OnUpdate()
    27.         {
    28.             RandomGenerator = new NativeArray<Unity.Mathematics.Random>(Environment.ProcessorCount, Allocator.TempJob);
    29.             for (int i = 0; i < RandomGenerator.Length; i++)
    30.             {
    31.                 RandomGenerator[i] = new Unity.Mathematics.Random((uint)Rnd.NextInt());
    32.             }
    33.  
    34.             var job = new Job()
    35.             {
    36.                 RandomGenerator = RandomGenerator,
    37.             };
    38.  
    39.             var jobHandle = job.Schedule(...);
    40.         }
    41.     }
     
    Last edited: Apr 19, 2019
  3. TheDemiurge

    TheDemiurge

    Joined:
    Jul 26, 2010
    Posts:
    42
    Thank you :) I just found this same sort of solution from this thread.
    I just put a persistent array sized to JobsUtility.MaxJobThreadCount inside the job and dispose of it along with the rest of my data. I think I'll encapsulate this functionality since it'll likely be reused at some point...
     
    LukePammant likes this.
  4. m_n_h

    m_n_h

    Joined:
    Feb 27, 2019
    Posts:
    3
    Maybe someone needs this:

    [Unity.Collections.LowLevel.Unsafe.NativeSetThreadIndex]

    System.Environment.ProcessorCount
     
    Last edited: Sep 20, 2019
    Tudor likes this.