Search Unity

[SOLVED] Gen random number in Job Execute causes hang

Discussion in 'Entity Component System' started by JamesWjRose, Nov 19, 2019.

  1. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    687
    Solved: I did not know that the mathematics library had a random function, and in learning that I also learned that I need to be VERY careful on which functions/libraries I use with DOTS.

    This works:
    Unity.Mathematics.Random mathRandom = new Unity.Mathematics.Random(88);
    int randomValue = mathRandom.NextInt(0, 10);

    Also, while I haven't done it yet, what EizenHorn states is worth looking into.
    ------------------------------------------------
    Hello gang,

    I'm having an odd situation where I am creating a NativeList in a Job's Execute and when I generate a random number number Unity hangs. Task Manager shows 0% CPU and the memory usage does not change. I left it running 10 minutes to see if there is any change to these values, nope. Had to End Task on Unity

    int randomValue = UnityEngine.Random.Range(0, 10); //This line uncommented causes Unity to hang

    Code (CSharp):
    1.  
    2. public void Execute(
    3.             DynamicBuffer<AutoLanePointsComponent> autoLanePoints,
    4.             [ReadOnly] ref AutoDetailsComponent autoDetails,
    5.             ref AutoPositionComponent autoPosition,
    6.             ref Translation autoTranslation,
    7.             ref Rotation autoRotation)
    8.         {
    9.             var distance = math.distance(autoPosition.destination, autoTranslation.Value);
    10.  
    11.             if (distance < reachedPositionDistance)
    12.             {
    13.                 autoPosition.currentIndex += 1;
    14.  
    15.                 //If the current position is at the end of the list of points,
    16.                 //if so, get the next list from the road/connection
    17.                 if (autoPosition.currentIndex >= autoLanePoints.Length)
    18.                 {
    19.                     NativeList<int> roadIdents = new NativeList<int>(Allocator.Temp);
    20.  
    21.                     //autoLanePoints.Clear();  //Before we add new values...
    22.  
    23.                     //Get the next connection and it's points
    24.                     if (autoPosition.isOnRoad)
    25.                     {
    26.                         for (int i = 0; i <connectionEntities.Length; i++)
    27.                         {
    28.                             var connectionDetails = ConnectionDetailsFromEntity[connectionEntities[i]];
    29.  
    30.                             if ((connectionDetails.LaneIdentity == autoPosition.laneIdentity) &&
    31.                                 (connectionDetails.RoadEntryIdentity == autoPosition.roadIdentity))
    32.                             {
    33.            
    34.                                 roadIdents.Add(connectionDetails.RoadExitIdentity);
    35.                             }
    36.                         }
    37.  
    38.                         if (roadIdents.Length > 0)
    39.                         {
    40.                             //int randomValue = UnityEngine.Random.Range(0, roadIdents.Length - 1);
    41.                             int randomValue = UnityEngine.Random.Range(0, 10);  //LOCK UP HERE
    42.  
    43.                             //autoPosition.roadIdentity = roadIdents[randomValue];
    44.                         }
    45.                     }
    46.  
    47.                     autoPosition.isOnRoad = !autoPosition.isOnRoad;
    48.                     autoPosition.currentIndex = 0;
    49.  
    50.                     roadIdents.Dispose();
    51.                 }
    52.  
    53.                 autoPosition.destination = autoLanePoints[autoPosition.currentIndex].value;
    54.                 autoPosition.destination.y += autoDetails.yOffset;
    55.             }
    56.  
    57.             float3 lookVector = autoPosition.destination - autoTranslation.Value;
    58.  
    59.             if (math.all(lookVector))
    60.             {
    61.                 Quaternion rotationLookAt = Quaternion.LookRotation(lookVector);
    62.                 autoRotation.Value = rotationLookAt;
    63.             }
    64.          
    65.             float3 smoothedPosition = math.lerp(autoTranslation.Value, autoPosition.destination, autoDetails.speed * deltaTime);
    66.             autoTranslation.Value = smoothedPosition;
    67. }
    68.  
     
    Last edited: Nov 19, 2019
  2. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    328
    Unsure off the top of my head why hanging would occur. AN error might happen sure, hanging is odd.

    My recommendation: Look into the new math library's random. It is a lot more performant and deterministic
     
    JamesWjRose likes this.
  3. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Use Unity.Mathematics.Random for random in bursted job, but don't forget that this is struct and copied by value. You should prepare some NativeArray of random's with count equal worker thread count and pass this array to your job and after changing - update this array.
    (based on something like this https://forum.unity.com/threads/mat...ijobprocesscomponentdata.598192/#post-4009273)
    Code (CSharp):
    1. _randoms = new NativeArray<Random>(JobsUtility.MaxJobThreadCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
    2.             var r = (uint) UnityEngine.Random.Range(int.MinValue, int.MaxValue);
    3.             for (int i = 0; i < JobsUtility.MaxJobThreadCount; i++)
    4.                 _randoms[i] = new Random(r == 0 ? r + 1 : r);
    Code (CSharp):
    1. [BurstCompile]
    2.         private struct SomeJob: IJobForEachWithEntity<SomeData>
    3.         {
    4.             [NativeDisableContainerSafetyRestriction]
    5.             public NativeArray<Random> Randoms;
    6.             [NativeSetThreadIndex] private int _threadId;
    7.  
    8.             public void Execute(Entity entity, int index, ref SomeData data)
    9.             {
    10.                 var rnd      = Randoms[_threadId];
    11.                 //Do stuff
    12.                 Randoms[_threadId] = rnd;
    13.             }
     
  4. Radu392

    Radu392

    Joined:
    Jan 6, 2016
    Posts:
    210
    Why not use random.NextFloat() or its variants inside the job? Your solution has quite a bit of overhead due to populating that native array.
     
  5. JamesWjRose

    JamesWjRose

    Joined:
    Apr 13, 2017
    Posts:
    687
    Thanks all of you. I did not know the Mathmatics library had a random function, and today I learned so much. Thank you, thank you, thank you.
     
  6. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    You will use NextXXX on
    rnd
    .
     
  7. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Here fast example why "just" passing Random to job and using it wouldn't be a "good" random.
    Just run parallel job, and set batch size to 1 for splitting work to different threads. And here result of this "random" in every Execute, not so random huh? :)
    Code (CSharp):
    1. public class RandomSample : JobComponentSystem
    2. {
    3.     private struct PrintRandoms : IJobParallelFor
    4.     {
    5.         public Unity.Mathematics.Random random;
    6.  
    7.         public void Execute(int index)
    8.         {
    9.             Debug.Log(random.NextFloat(0, 1000f));
    10.         }
    11.     }
    12.  
    13.     private uint _seed = 999;
    14.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    15.     {
    16.         Debug.Log("----------START---------");
    17.         new PrintRandoms()
    18.         {
    19.             random = new Unity.Mathematics.Random(_seed)
    20.         }.Schedule(5, 1, inputDeps).Complete();
    21.         Debug.Log("----------END---------");
    22.         _seed++;
    23.         return inputDeps;
    24.     }
    25. }
    upload_2019-11-19_20-9-12.png
     
  8. Radu392

    Radu392

    Joined:
    Jan 6, 2016
    Posts:
    210
    Ahh wow very interesting. Thanks for clearing it up.
     
  9. randomdragon

    randomdragon

    Joined:
    Feb 11, 2020
    Posts:
    31
    Well now in 2022.1.7f1 the test result above has changed.
    Code (CSharp):
    1.  
    2. [BurstCompile(DisableSafetyChecks = false)]
    3. public struct TestRandomJob : IJobFor
    4.     {
    5.         Random rand;
    6.         [WriteOnly] NativeArray<int> dst;
    7.  
    8.         public void Execute(int index)
    9.         {
    10.             // dst[index] = rand.NextInt() % 100;
    11.             Debug.Log(rand.NextInt(100));
    12.         }
    13.  
    14.         public static JobHandle ScheduleParallel(Random rand, NativeArray<int> dst)
    15.         {
    16.             var job = new TestRandomJob()
    17.             {
    18.                 rand = rand,
    19.                 dst = dst
    20.             };
    21.             return job.ScheduleParallel(dst.Length, 1024, default);
    22.         }
    23.     }
    upload_2022-7-9_22-53-4.png

    Guess that there have been some interior code used to gen a unique rand for each Random field in the IJobFor struct for each Execute index.
     
  10. Anthiese

    Anthiese

    Joined:
    Oct 13, 2013
    Posts:
    73
    What do you mean? Did you try eizenhorn's code verbatim? They specifically use a batch size of 1 there.

    The code you show specifies a batch size of 1024, which should mean at least that many are executed per worker batch, ergo you likely (for list size <= 1024) run every single iteration on one struct instance, and the Random instance itself would be mutated per iteration. If you'd have set the batch size to something lower, say in the range of 1 to 10, you'd see that many unique values repeated in the logs since each worker execution starts with the same (default) Random and subsequently produces a predictable value (as in it's PRNG purely based off the generator state).

    Were this not the case and Random fields are actually randomized outside user code... I would find that incredibly cursed.
     
    Last edited: Jul 10, 2022