Home > Back-end >  NativeArray of float4 how to set one of the xyzw to a value in loop?
NativeArray of float4 how to set one of the xyzw to a value in loop?

Time:09-28

When looping through a NativeArray of float4[], and wanting to set one of the 'fields' of each float4 (let's say y) to a value, it doesn't seem immediately possible because it's a temporary value.

this:

  NativeArray<float4> naFloat4s ;
  void Start ( ) {
    naFloat4s = new NativeArray<float4> ( 96000, Allocator.Persistent ) ;
  }
  void MakeNoise ( ) {
    for ( int i = 0 ; i < naFloat4s.Length ; i   ) {
      naFloat4s[i].y = Random.Range ( - 1f, 1f ) ;
    }
  }

Generates the complaint about it being a temporary value of a struct, can't be set.

How is this problem most performantly overcome such that there's no garbage created and the NativeArray and Burst/Jobs can do their utmost to get through this process for tens of thousands of settings as fast as possible?

Note: the random used here is just an example. Presume there's something else there generating something more interesting.

Also note, when doing this, the other values (in this case x, z and w) must remain unchanged. They're still useful, as they are. The need is to change just one of the values in the float4, throughout the array.

Edit: Fixed floats in range, as per Sir Hugo's comment.

In response to comment by sir Hugo regarding pointer to float in float4:

I got the pointer to the individual float working, by doing this:

      void LoopDemoWithPointerToFloatOfFloat4_NativeArray() {
        int    samples = 2000;
        int    size_T = UnsafeUtility.SizeOf<float4> ( ) ;
        int    size_F = UnsafeUtility.SizeOf<float> ( ) ;
        int    mStepr = size_F * 1 ; // 1 is y's index value in the float4 struct
        IntPtr dstPtr = ( IntPtr )NativeArrayUnsafeUtility
                         .GetUnsafeBufferPointerWithoutChecks ( naFloat4s ) ;
        
        for ( int i = 0 ; i < samples ; i   ) {
          unsafe {
            float* yOffset =  (float*) (dstPtr   i * size_T   mStepr);
            *yOffset = (float)i ;
          }
        }
      }

Haven't had a chance to check the speed, it seems fast.

Need to create a rig to test various with StopWatch...

CodePudding user response:

As usual for any struct (= value) type if it is within an array you can only do

var value = naFloat4s[i]; 
value.y = Random.Range(-1, 1); 
naFloat4s[i] = value;

The indexer ([i]) is a property and either returns or takes a complete float4 struct value.

So if you would (be able to) do

naFloat4s[i].y = something;

you would basically return a float4 and change that returned float4's y component. But this wouldn't in any way alter the value that is stored in the array.


UPDATE

To the pointer thing:

I just made a little test and indeed if you are really willing to go unsafe you could use pointers. Following test

private unsafe void Test()
{
    var testIndexer = new NativeArray<float4>(amount, Allocator.Persistent);
    var testPointer = new NativeArray<float4>(amount, Allocator.Persistent);

    Profiler.BeginSample("TEST Indexed");

    for (var i = 0; i < testIndexer.Length; i  )
    {
        var value = testIndexer[i];
        value.y = i;
        testIndexer[i] = value;
    }

    Profiler.EndSample();

    Profiler.BeginSample("TEST Pointer");
    var pointer = (float4*)testPointer.GetUnsafePtr();

    for (var i = 0; i < testPointer.Length; i  )
    {
        (pointer   i)->y = i;
        // or also (seems equally fast - 10ms slower on 10.000 elements)
        // pointer[i].y = i;
        // or also (seems equally fast)
        // (*(pointer   i)).y = i;
    }

    Profiler.EndSample();

    for (var i = 0; i < amount; i  )
    {
        Debug.Log($"indexed: {testIndexer[i].y}, pointer: {testPointer[i].y}");
    }

    Debug.Assert(testIndexer.ToArray().SequenceEqual(testPointer.ToArray()));

    testIndexer.Dispose();
    testPointer.Dispose();
}

already was at least three times faster which is most probably ofcourse due to the fact that using the indexer you have more operations (read a value, store the value, write the value).

Some benchmark values:

  • amount = 100

    • Indexed: 0.77 ms
    • Pointer: 0.25 ms
  • amount = 1000

    • Indexed: 3.40 ms
    • Pointer: 0.67 ms
  • amount = 10000

    • Indexed: 37.39 ms
    • Pointer: 7.70 ms

Whether it is possible to actually directly write to a single float pointer I don't know, tbh, but it might even be faster yes.

  • Related