#DOTS and C# struct size considerations

1 messages · Page 1 of 1 (latest)

regal prism
#

DOTS is based around using C# structs.
In C# structs have a maximum recommended size.
Exceeding that size is possible, but is said to bring various penalties.
How does DOTS deal with this?

I might've missed something, but I haven't found any mention about treating that C# fact.
I've only encountered info on DOTS memory layout, chunks, etc..
But this C# struct specifics is more about how the structs are processed by the CPU.
Considering how DOTS is all about faster CPU processing, it would seem that this point should've been clarified.

So the question is: What is it for DOTS?
Does that C# specific still hold for DOTS?
Is it irrelevant because the DOTS code actually gets compiled into LLVM code?
Is it something else?

Details and references:
Most sources, including the Microsoft official docs talk about the 16 bytes limit.
There are also sources which say it's 32 or 64 bytes these days for most CPUs.
Just to cite the sources, some of them explain why is this a thing:
-- Microsoft C# docs -- https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/choosing-between-class-and-struct
-- a SO answer -- https://stackoverflow.com/questions/1082311/why-should-a-net-struct-be-less-than-16-bytes#:~:text=As long as the structure,make it a class instead.
-- a Reddit thread -- https://www.reddit.com/r/csharp/comments/8grfur/struct_what_is_16_bytes/?rdt=61707
-- a thread with a lot of relevant answers -- https://www.appsloveworld.com/csharp/100/27/why-is-16-byte-the-recommended-size-for-struct-in-c

inner bison
#

In standard C# (not Unity/Dots) a lot of this penalty can be removed by passing your struct by ref/in where possible to avoid the extra copying.

#

That being said, these issues are likely completely moot in Unity's "HPC#" which heavily relies on code generation and can avoid most of the problems from the get-go.

fossil bay
#

i always use in/ref/RefRW/RefRO to avoid this. Is it right?

safe magnet
heady elbow
#

in is just ref, but it'll perform defensive copies if it can't be sure that the type isn't readonly

safe magnet
#

as for the struct size considerations, reading the links you have given it seems to be related to performance of struct value copies vs. class reference copies, so indeed using ref will circumvent most of those issues

It should be noted that allocations are really not cheap (even though the temp allocator does a very good job), I would prefer a struct in HPC# up to quite large sizes (even 512 bytes). But, as they say, profiling is key!

safe magnet
fossil bay
#

then is ref the way to go on IEntityJob?

heady elbow
#

They have their similarities, but ultimately in is a lot more dependent on the compiler, and I've heard consistent advice to avoid it when structs are not marked as readonly.
Though I've no idea if Unity have some specific handling of it with entities or Burst. I presumed they use it in the foreach codegen stuff in a fairly fancy way that avoids it in the end, though I've never looked into it. I'm certainly not the one to ask, I just have poked around on Sharplab and read a bunch of blogs ;D

keen linden
#

(I personally like 'in' and I find rider very good at warning me when i've accidently done a defensive copy, but there are plenty who dislike it)

heady elbow
#

I thought there were more cases than just invoking non-readonly members inside of the function that uses an in parameter, but as I never committed them to memory I'm probably just wrong. If it's just the cases that Rider already picks up on, yeah in is great 😄

winter star
#

so the tradeoffs for in/ref/neither are very different for IJE vs. normal c# code

#

really 3 situations: IJE, bursted normal c#, unbursted normal c#. in IJE, in marks your job as accessing the component as readonly, and so other readonly jobs can access the same component in parallel to that job, which is, like, pretty clutch sometimes. in non-bursted C#, it's just like ref except mono will sometimes do a defensive copy behind your back; i didn't know rider helped with that, but that would probably mitigate the issues (especially if it knows to go inside nested functions that also take in). in bursted c#, burst seems to be pretty good about not doing stupid copies, so it seems harmless and even vaguely helpfully hygienic.

regal prism
#

Bumping up the question.

TLDR:
When working with Unity DOTS, how should I treat the official C# struct size considerations:

  • Do they still hold for DOTS?
  • Are they irrelevant because DOTS code actually gets compiled into LLVM code where such details of C# do not apply? (what about Bursted and not-Bursted code mix then? since not all code is bursted, but structs are shared throughout all the code)
  • Is it something else?

See the original question for full info.

fossil bay
#

They apply but if you pass your components by reference (RefRW, RefRO) then it does not matter

winter star
#

type layout is generally the same between mono and burst (because you have to be able to pass data back and forth), so they all apply, but yes, if passing by ref, things do not get copied