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.