Home > Enterprise >  Make C# enums memory efficient?
Make C# enums memory efficient?

Time:07-02

According to this question C# will assign 4 byte size to field of type Fruits whether it is defined like this:

enum Fruits : byte { Apple, Orange, Banana }

or like this:

enum Fruits { Apple, Orange, Banana }

I'm still curious if there is any way of sidesteping this and making the size of enum smaller than 4 bytes. I know that this probably wouldn't be very efficient or desirable but it's still interesting to know if it's possible at all.

CodePudding user response:

Data alignment (typically on 1, 2, 4 byte border) is used for the faster access to the data (int should be aligned on 4 bytes border).

For instance (let me use byte and int instead of enum for readability and struct instead of class - it's an easy way to get size of struct with a help of sizeof):

// sizeof() == 8 == 1   3 (padding)   4
public struct MyDemo {
  public byte A; // Padded with 3 unused bytes 
  public int B;  // Aligned on 4 byte
}

// sizeof() == 8 == 1   1   2 (padding)   4
public struct MyDemo {
  public byte A; // Bytes should be aligned on 1 Byte Border 
  public byte B; // Padded with 2 unused bytes
  public int C;  // Aligned on 4 byte
}

// sizeof() == 2 == 1   1 
public struct MyDemo {
  public byte A; // Bytes should be aligned on 1 Byte Border 
  public byte B; // Bytes should be aligned on 1 Byte Border 
}

So far so good you can have an effect even in case of fields within class (struct), e.g.

public MyClass {
  // 4 Byte in total: 1   1   2 (we are lucky: no padding here)
  private Fruits m_Fruits; // Aligned on 1 Byte border
  private byte m_MyByte    // Aligned on 1 Byte border
  private short m_NyShort; // Aligned on 2 Byte border
}

In case of a collection (array) all the values are of the same type which should be aligned in the same way, that's why no padding is required:

// Length * 1Byte == Length byte in total
byte[] array = new [] {
  byte1, // 1 Byte alignment
  byte2, // 1 Byte alignment
  byte3, // 1 Byte alignment
  ...
  byteN, // 1 Byte alignment
} 

CodePudding user response:

For the vast majority of applications the size overhead will not matter at all. For some specialized applications, like image processing, it may make sense to use constant byte values and do bit-manipulations instead. This can also be a way to pack multiple values into a single byte, or combine flag-bits with values:

const byte Apple = 0x01;
const byte Orange= 0x02;
const byte Banana= 0x03;
const byte FruitMask = 0x0f; // bits 0-3 represent the fruit value
const byte Red = 0x10;
const byte Green = 0x20;
const byte ColorMask = 0x70; // bits 4-6 represents color
const byte IsValidFlag = 0x80; // bit 7 represent value flag

...
var fruitValue = myBytes[i] & FruitMask;
var IsRed = (myBytes[i] & ColorMask) == Red ;
var isValid = myBytes[i] & IsValidFlag > 0;

CodePudding user response:

According to this question C# will assign 4 byte size to field of type Fruits whether it is defined like this

I would say that this is not what actually is written there. The post describes the memory alignment on stack which seems to align 4 bytes for byte variable too (can be platform depended):

byte b = 1;

results in the same IL_0000: ldc.i4.1 instruction as the var fb1 = FruitsByte.Apple and int i = 1; (see at sharplab.io) and the same 4 bytes difference (Core CLR 6.0.322.12309 on x86) in the move instructions.

Though using corresponding enum as struct fields will result in them being aligned to corresponding borders:

Console.WriteLine(Unsafe.SizeOf<C>()); // prints 2
Console.WriteLine(Unsafe.SizeOf<C1>()); // prints 8

public enum Fruits : byte { Apple, Orange, Banana }
public enum Fruits1 { Apple, Orange, Banana }
public struct C {
    public Fruits f1;
    public Fruits f2;
}
public struct C1 {
    public Fruits1 f1;
    public Fruits1 f2;
}

The same will happen for arrays, which will allocate continuous region of memory without aligning different elements.

Useful reading:

  • Related