Search Unity

IJobNativeMultiHashMapMergedSharedKeyIndices unexpected behavior

Discussion in 'Entity Component System' started by davidfrk, Oct 14, 2018.

  1. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    Should IJobNativeMultiHashMapMergedSharedKeyIndices change its behavior depending on the values?

    Look at this script, in the first run using keys equal to values, the output is 10 and 0.

    In the second using values being key + 1, the output is 5 and 5.

    Should not both executions be 5 and 5?

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Collections;
    5. using Unity.Jobs;
    6.  
    7. public class MultiHashExample : MonoBehaviour
    8. {
    9.     struct MultiHashExampleJob : IJobNativeMultiHashMapMergedSharedKeyIndices
    10.     {
    11.         public NativeQueue<int>.Concurrent firstElementPerKey;
    12.         public NativeQueue<int>.Concurrent otherElements;
    13.  
    14.         public void ExecuteFirst(int index)
    15.         {
    16.             firstElementPerKey.Enqueue(index);
    17.         }
    18.  
    19.         public void ExecuteNext(int firstIndex, int index)
    20.         {
    21.             otherElements.Enqueue(index);
    22.         }
    23.     }
    24.  
    25.     void Start()
    26.     {
    27.         NativeMultiHashMap<int, int> hashMap = new NativeMultiHashMap<int, int>(100, Allocator.TempJob);
    28.         NativeQueue<int> firstElementPerKey = new NativeQueue<int>(Allocator.TempJob);
    29.         NativeQueue<int> otherElements = new NativeQueue<int>(Allocator.TempJob);
    30.  
    31.      
    32.         for (int i = 0; i < 5; ++i)
    33.         {
    34.             //Adding two elements of each value
    35.             hashMap.Add(i, i);
    36.             hashMap.Add(i, i);
    37.         }
    38.  
    39.         MultiHashExampleJob job = new MultiHashExampleJob
    40.         {
    41.             firstElementPerKey = firstElementPerKey.ToConcurrent(),
    42.             otherElements = otherElements.ToConcurrent()
    43.         };
    44.         JobHandle jobHandle = job.Schedule(hashMap, 64);
    45.         jobHandle.Complete();
    46.  
    47.         Debug.Log("Elements in the first execute " + firstElementPerKey.Count + " other elements " + otherElements.Count);
    48.  
    49.         hashMap.Dispose();
    50.         firstElementPerKey.Dispose();
    51.         otherElements.Dispose();
    52.     }
    53.     }
    54.  
    Second run uses

    Code (CSharp):
    1. //Adding two elements value and value + 1
    2. hashMap.Add(i, i);
    3. hashMap.Add(i, i + 1);
    I tested NativeMultiHashMap manually and it works as expected

    Code (CSharp):
    1.         NativeMultiHashMapIterator<int> it;
    2.         int value;
    3.  
    4.         for (int key = 0; key < 5; key++)
    5.         {
    6.             if (hashMap.TryGetFirstValue(key, out value, out it))
    7.             {
    8.                 do
    9.                 {
    10.                     Debug.Log("Key " + key + " value " + value);
    11.                 } while (hashMap.TryGetNextValue(out value, ref it));
    12.             }
    13.         }
     
  2. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    NativeQueue has no guaranteed order when enqueued concurrently on a job, that may explain the discrepancy.
     
  3. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    Sadly not.

    I used two lists to independently collect the processed values in ExecuteFirst and ExecuteNext;

    The point here, from what I understand, is that ExecuteFirst should run once per key, and that's what happens in the other tests I've done.

    For example, using

    Code (CSharp):
    1. hashMap.Add (i, i);
    2. hashMap.Add (i + 1, i);
    I add six different keys, so 6 elements should be the first of your own key and 4 would enter as Next. And this is what happens, the output is 6 for First and 4 for Next.
     
  4. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    If i do

    Code (CSharp):
    1.         for (int i = 0; i < 5; i++)
    2.         {
    3.             //Adding two elements of each value
    4.             hashMap.Add(i, i);
    5.             hashMap.Add(i, i);
    6.         }
    7.         hashMap.Add(1, 6);
    The last line corrects the key 1 to 1 First value and 2 Next, while the other 4 keys still count as two First

    Resulting in 9 and 2, while without the last line the output is 10 and 0.
     
  5. davidfrk

    davidfrk

    Joined:
    Feb 13, 2017
    Posts:
    43
    In case someone needs a workaround, an isFirst check like this works.

    Code (CSharp):
    1.  
    2. bool isFirst = true;
    3. while (entryIndex != -1)
    4. {
    5.   var key = UnsafeUtility.ReadArrayElement<TKey>(keys, entryIndex);
    6.   var value = UnsafeUtility.ReadArrayElement<int>(values, entryIndex);
    7.   int firstValue;
    8.  
    9.   NativeMultiHashMapIterator<TKey> it;
    10.   fullData.HashMap.TryGetFirstValue(key, out firstValue, out it);
    11.   if (firstValue == value && isFirst)
    12.   {
    13. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    14.  
    15.     JobsUtility.PatchBufferMinMaxRanges(bufferRangePatchData,
    16.     UnsafeUtility.AddressOf(ref fullData), value, 1);
    17. #endif
    18.     fullData.JobData.ExecuteFirst(value);
    19.     isFirst = false;
    20.   }
    21. ...
    22.  
     
  6. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    I logged a bug for this. We'll have a look. But might take some time due to Unite next week etc.
     
    GilCat, T-Zee, davidfrk and 1 other person like this.