I took a C++ class about a year ago and they talked about padding with variables and how bytes can be wasted when the variables aren’t aligned properly (a bool before a float could waste 3 bytes for instance). Does C# have a similar principle? If so, how does it handle size and padding of variables for the component classes of game objects, and the game object class variables we make in our scripts? Is there a way to check what their size will be so we can pad it appropriately?
Being a managed language, C# doesn’t technically let you poke around memory outside of your own variables the way C / C++ does (except with unsafe and native interop stuff), so this question is mostly irrelevant.
Certainly nothing in Unity cares about data outside of your legitimate data space.
To an extent this will also be compiler-dependent, as well as somewhat specified in C#.
If you actually care then construct a few quick experiments and find out.
You can pin structures and then ransack them byte by byte if you’re curious, like this:
https://discussions.unity.com/t/731131/6
It’s not really “waste.” It’s a tradeoff in instruction set simplicity and CPU memory access alignment. Just like C/C++, you can flip any single bit you want in C# and it will let you noodle all the bytes of memory you control.
Don’t worry about it until you measure something (see the profiler, Window → Analysis → Profiler) and find that something is a problem for your game.
In C# you can only layout struct types, not classes, by using the linked [StructLayout] attribute.
So you could, for example, represent and access an Int32 field also as 4 byte fields via explicit layout (aliasing the same memory location).
The default layout type for structs is “sequential”, so the same order as you specify the fields. In that case it can matter to size of the type whether you have bool,int,bool vs int,bool,bool. The former is likely 12 bytes, the latter probably 8. Compiler settings play a role in this too, I would assume there are packing options for C#.
I don’t know for classes but either the fields are also sequential, or fields are prone to compiler optimizations as in reordering fields because there is no field offset addressing for users of the class in C#.
Thanks for the clarifications. My friends and I just started our own project together and I’d figure I would try to optimally organize our variables early before we get too many and it becomes a chore in the end game. I may later on look into checking byte-by-byte as a curious experiment in the future though.
Thanks for the help! I wasn’t aware the packing was only a concern in structs, I’ll my friends know so we are careful once we implement them.
That should be the absolutely dead-last thing necessary in Unity development.
It just literally is not a thing, (almost) ever.
Sure, I guess if that’s your thing, go nuts.
Just know that personally and professionally under Unity and C# I never worry about this bitsy-bytesy stuff.
I guess the closest I ever came to worrying about it was back-of-napkin calculating the amount of data required to store the position and rotation and vital statistics of my Jetpack Kurt Space Flight each frame, in order to reason about how long of a flight I could record before I ran into memory issues.
using UnityEngine;
// @kurtdekker - a single flight datapoint in time, part of Jetpack Kurt Space Flight
public struct FlightDataDatapoint
{
// We are NOT storing timeStamp. We are inferring this
// from using Time.fixedDeltaTime and the index number.
public FlightDataEventFlags flags;
public Vector3 position;
// control inputs, not orientation:
public float power, pitch, yaw, roll;
// radar altimeter readout; this is not simply position.y!
public float altitude;
public float heading;
}
From that I surmised one 4-byte thing per identifier:
flags, position.x, position.y, position.z,
power, pitch, yaw, roll,
altimeter, heading
That comes to 40 bytes per sample… 50 samples per second, so 2000 bytes per second of recorded flight time.
One minute burns up 120k of memory…
And one full hour burns up 7.2mb of memory.
Tiny!!
So after making that back-of-napkin assay of the problem space, I just implemented it and it’s awesome, since most games are only a few minutes long at most.
The data recorder starts with 1000 entries and doubles it every time it gets full.
int size = 1000;
int ptr;
FlightDataDatapoint[] Points;
// all parts of a frame of operation shovel data into this slot for later recording
FlightDataDatapoint WorkDP;
void AddWorkDP()
{
if (Points == null || ptr >= size)
{
var nuSize = size * 2;
var nuPoints = new FlightDataDatapoint[nuSize];
if (Points != null)
{
System.Array.Copy( Points, nuPoints, Points.Length);
}
Points = nuPoints;
size = nuSize;
}
Points[ptr] = WorkDP;
ptr++;
}
Appstore: https://itunes.apple.com/us/app/jetpack-kurt/id1033348911
GooglePlay: https://play.google.com/store/apps/details?id=com.plbm.jetpack
Android TV: https://play.google.com/store/apps/details?id=com.plbm.jetpacktv
With MonoBehaviours I can’t imagine too many use cases as it’s not like that side of things is very optimized in the first place, but it’s a different story with DOTS ECS as the size of a component affects cache efficiency (cache lines are typically 64 bytes) as well as how many you can keep in a single entity archetype (archetypes are 16KB).