C
C#•3mo ago
tired_weirdo_girl

Arrays of floats in stack

I'm creating a structure to represent a 4x4 matrix and want it to be, at least, allocated entirelly on the stack. I also need to be able to get the array pointer in a unsafe context. There is some way to store a array-like value like this on the stack instead of the heap?
48 Replies
Anton
Anton•3mo ago
There are fixed-size arrays and stackalloc ref structs is also a thing you can put a span as a field in a ref struct to get the pointer, use fixed to make a struct that is the object on the stack and not a reference to this object, the only way are fixed-size arrays or listing the amount of fields you need manually which people actually do more often for this like a vector field for each row or a float field for each cell
Jimmacle
Jimmacle•3mo ago
why do you want it on the stack? stackalloc and ref structs are only allowed on the stack and would limit how you can use the data if you need more, then you can create a struct with sequential layout with 16 fields in it which is how every math library i've seen does it
Cattywampus
Cattywampus•3mo ago
that's what InlineArray is for
[InlineArray(16)]
public struct MyMatrix4x4
{
private float _data;
}

//then you can do this
var mat = new MyMatrix4x4();

//accessing the indexer
mat[0] = 0f;

//or do thing what Span<T> can do
Span<float> span = mat;
ReadOnlySpan<float> span1 = mat;

//OR pass them directly as Span with the signature of Span<T> or ReadOnlySpan<T>
MyMethodPassAsSpan(mat);

//It's king of the hill if you're working with vectorization
//by simply unsafe loading stuff
Vector128.LoadUnsafe(ref _data, 0);
Vector128.LoadUnsafe(ref _data, 4);
Vector128.LoadUnsafe(ref _data, 8);
Vector128.LoadUnsafe(ref _data, 12);

//those 0, 4, 8, 12 are the offsets

//Storing is easy peasy if you're working with vectorization via StoreUnsafe()

Vector128.LoadUnsafe(ref _data, 0)
.StoreUnsafe(ref _data);
[InlineArray(16)]
public struct MyMatrix4x4
{
private float _data;
}

//then you can do this
var mat = new MyMatrix4x4();

//accessing the indexer
mat[0] = 0f;

//or do thing what Span<T> can do
Span<float> span = mat;
ReadOnlySpan<float> span1 = mat;

//OR pass them directly as Span with the signature of Span<T> or ReadOnlySpan<T>
MyMethodPassAsSpan(mat);

//It's king of the hill if you're working with vectorization
//by simply unsafe loading stuff
Vector128.LoadUnsafe(ref _data, 0);
Vector128.LoadUnsafe(ref _data, 4);
Vector128.LoadUnsafe(ref _data, 8);
Vector128.LoadUnsafe(ref _data, 12);

//those 0, 4, 8, 12 are the offsets

//Storing is easy peasy if you're working with vectorization via StoreUnsafe()

Vector128.LoadUnsafe(ref _data, 0)
.StoreUnsafe(ref _data);
another neat thing about InlineArray is that passing it by ref will be slower if you've a large enough matrices, instead you pass them directly as raw Span<T> or ROS<T> and it will be faster than by ref passing manually, and as you said for pointer stuff, you can just do this
var mat = new MyMatrix4x4();
Span<float> span = mat;

//for best practices, always use Marshal.GetReference()
//you can ofcourse point to the indexer directly if you would, like :
// e.g: ref var tref = ref mat[0];
ref var tref = ref MemoryMarshal.GetReference(mat);

//then do whatever
var mat = new MyMatrix4x4();
Span<float> span = mat;

//for best practices, always use Marshal.GetReference()
//you can ofcourse point to the indexer directly if you would, like :
// e.g: ref var tref = ref mat[0];
ref var tref = ref MemoryMarshal.GetReference(mat);

//then do whatever
then you can offset it with Unsafe.Add() OR just by slicing them as span 🙂
var mat = new MyMatrix4x4();
Span<float> span = mat;

ref var tref = ref MemoryMarshal.GetReference(mat.Slice(4));
var mat = new MyMatrix4x4();
Span<float> span = mat;

ref var tref = ref MemoryMarshal.GetReference(mat.Slice(4));
easy peasy with inlinearray the main issue with large sturcts has always been defensive copies, if you somehow rolling your own fat custom matrices (NOT the inlinearray way I shown above) make sure you tag each of modifying method with readonly e.g : public readonly void DoSomethingThatWillMutateTheMatrix(T foo, T foo1) including all your getters, yes you're reading it right, getters like so
public readonly float Row0 => row0;
public readonly float Row0 => row0;
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
i already tried stackalloc, but it asks for the structure to be a ref struct. a structure marcked with ref is still allocated on stack? i never understood how exactly ref structures works...
Anton
Anton•3mo ago
if you just make a local variable of a struct type, it will be on the stack
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
oh, this is new for me. will give a look
Anton
Anton•3mo ago
ref is not required ref is for when you want to have a ref field in the struct or a span field stackalloc is for a stack array
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
why marking the struct as ref is needed to have a field marked as ref?
Anton
Anton•3mo ago
because refs are only allowed on the stack. while regular structs can be on the heap too, ref structs can only be on the stack
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
:0 waaaaat lol this is really new to me i need to study about thx
Anton
Anton•3mo ago
you're welcome um, to be clear, not ref as in a class reference ref as in e.g. ref int a = ref otherInt; @tired_weirdo_girl
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
ohh ok ok
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
@Anton here what i get
No description
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
also i really cannot get what it wants...
No description
Anton
Anton•3mo ago
where is that code show context
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
sry i forgot, here the entire struct declaration
No description
Anton
Anton•3mo ago
stackalloc is only usable in methods + if you want a span field, it's going to have to be a ref struct use a fixed buffer for what you're trying to do rn, that's what it's for but it's honestly really pathetic I haven't used the attribute that the person before suggested, but it might be better
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
ohh nice this is new to me i'm giving a look on the attribute rn, after i see if the fixed buffer is better
Anton
Anton•3mo ago
the fixed buffer is pathetic is what I meant to be clear as a language feature it's poorly designed
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
i can see it needs to be unsafe because "CLR can't understand fixed buffers" or something like it? lol this is just stupid i need to say
Cattywampus
Cattywampus•3mo ago
skip all this, just use InlineArray ffs 😄
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
lol fair
Cattywampus
Cattywampus•3mo ago
simply bcos InlineArray is already a Span.. technically I already gave you examples how to span them up and you want to vectorize that Matrix4x4 with pretty much ease, also gave example of it already vectorize or bust, bcos if you're not, your matrix4x4 will just be the super slow version of System.Numerics.Matrix4x4 😄 any reason why you want to make your own custom matrix4x4 here?
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
i'm making a little help library for making games from scratch
Cattywampus
Cattywampus•3mo ago
then why not just use System.Numerics ones?
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
i don't like Matrix4x4 so much cause their methods asks for lots of parameters that usually are constant and i can't directly get the ptr of it to send though the functions of the unmanaged methods i think it would be nice if i can do it without giving more shit to the GC
Cattywampus
Cattywampus•3mo ago
what?...
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
XD in short Matrix4x4 is too complex i want something more simple and access it in low level
Cattywampus
Cattywampus•3mo ago
bcos they're marked as readonly for almost all the methods, to prevent defensive copies fucking up your game or project
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
i know, i'm marking mines too
Cattywampus
Cattywampus•3mo ago
I don't get it, you can simply just do this
fixed(float* ptr = &mat.x)
{

}
fixed(float* ptr = &mat.x)
{

}
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
but at the end of the operation i still need to create a array to get the data as it wait i can :bigthonk:
Cattywampus
Cattywampus•3mo ago
System.Numerics.Matrix4x4 and all of them matrices has CopyTo method, which you can feed your own Span or array yeah I don't think you should make your own here tbfh, reason being most nugets or external libs are using System.Numerics maths surely can 😄 note the mat.x there was meant to point the very 1 byte I forget what numerics.matrix4x4 uses, so look the source and report back so we can help you
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
wow looks like this works...
No description
Cattywampus
Cattywampus•3mo ago
lol, yeah just dont forget pinning it with fixed and avoid Unsafe.AsPointer
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
looks like fixed is not needed something about M11 be already fixed
Cattywampus
Cattywampus•3mo ago
errr... it will be needed, if it moves byt the GC you're fucked thats why we need to pin them
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
ik, bit like the language don't allows me to put a fixed on it lol
Cattywampus
Cattywampus•3mo ago
what
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
No description
Cattywampus
Cattywampus•3mo ago
take the span out instead, so you dont need the fixed, then do this
ref var tref = ref MemoryMarshal.GetReference(span);
ref var tref = ref MemoryMarshal.GetReference(span);
better be safe
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
well, i still need to send it to unmanaged code that asks for a float*... but maybe it has some kind of overload for spans or idk
Anton
Anton•3mo ago
there's Unsafe.GetPointer or something iirc
Cattywampus
Cattywampus•3mo ago
ref var tref = ref mat.x;
(float*)Unsafe.AsPointer(ref tref));
ref var tref = ref mat.x;
(float*)Unsafe.AsPointer(ref tref));
either way you shouldn't be doing this, take the span approach instead
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
okk really thx
Cattywampus
Cattywampus•3mo ago
public static void CopyTo(Span<float> span, int startIndex = 0)
{
Unsafe.WriteUnaligned(ref Unsafe.As<float, byte>(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), (uint)startIndex)), this);
}
public static void CopyTo(Span<float> span, int startIndex = 0)
{
Unsafe.WriteUnaligned(ref Unsafe.As<float, byte>(ref Unsafe.Add(ref MemoryMarshal.GetReference(span), (uint)startIndex)), this);
}
@tired_weirdo_girl make that an extension for your matrix or whatever the this is your struct
tired_weirdo_girl
tired_weirdo_girlOP•3mo ago
right right
Cattywampus
Cattywampus•3mo ago
that should be pretty fast also size matters kinda surprised Numerics.Matrix4x4 doesnt have that tbfh 🤔

Did you find this page helpful?