57

Given the following enum:

public enum Operations_PerHourType : byte
{
    Holes = 1,
    Pieces = 2,
    Sheets = 3,
    Strips = 4,
    Studs = 5
}

When I run the Microsoft code analysis tool, it tells me:

CA1028 : Microsoft.Design : If possible, make the underlying type of 'Enums.Operations_PerHourType' System.Int32 instead of 'byte'.

It will never have more than a couple possible values, so I declared it as a byte. Why would they recommend using int32? More values for future scalability? Or is there a performance improvement?

Jason Down
  • 21,731
  • 12
  • 83
  • 117
Kevin
  • 1,940
  • 3
  • 24
  • 38
  • 5
    Why do you care whether or not the storage is a byte or an int? What's your motivation in trying to force use of a byte? – David Heffernan Apr 18 '12 at 19:52
  • 5
    I didn't, until I ran the Microsoft Code Analysis tool. It recommended using an Int32. I am curious why. I used byte initially because I assumed it would be better for performance. Less space. – Kevin Apr 18 '12 at 19:53
  • 1
    http://stackoverflow.com/questions/5005319/why-cant-i-declare-an-enum-inheriting-from-byte-but-i-can-from-byte – Nesim Razon Apr 18 '12 at 19:54
  • 23
    @DavidHeffernan Why should you not care? What's wrong with being curious? – Mr Lister Apr 18 '12 at 19:54
  • 8
    @MrLister That's beside the point. What I was trying to elicit was the motivations for the explicit specification of `byte`. I didn't write that so what I care about, or don't, is neither here nor there. Clearly Kevin cares. He wrote `byte`. I didn't say he should not care. I did not say it was wrong to care. My comment passed no judgement. I merely asked why? What's wrong with being curious? – David Heffernan Apr 18 '12 at 19:56
  • 12
    I would expect `int` to perform better than `byte` because it has better alignment properties – David Heffernan Apr 18 '12 at 20:02
  • The size of the assembly registers used in a 32bit application is a DWORD, which is 32 bit wide. An 8 bit value will most likely be stored in an aligned manner too (i.e. as four bytes where the first 3 bytes are zeroed). So there is not really any space performance improvement (as in less space needed for storage - if that would really matter). Concerning speed performance I suppose it would rather degrade actually (marginally), as 32bit assembly is optimized for 32bit and additional assembly instructions would have to be performed to read the 8bit value into a 32bit register, etc. – Sascha Hennig Apr 19 '12 at 10:33
  • Note: that it is possible to store the bytes unaligned and several high level languages might even do it - I dont know. If so, the speed performance would degrade even more, because even more CPU cycles will have to be spent loading the data into the registers. Whilst I am sure that the performance hit will be low, it does not make sense to go out of your way to try and improve performance by NOT using INT32s. Always use 32 bit wide values except you really cannot use them. For 64 bit applications I am not quite sure yet :P I am still working on my 64 bit assembly skills ... – Sascha Hennig Apr 19 '12 at 10:41
  • Very close: [c-sharp-underlying-types-of-enums](http://stackoverflow.com/questions/2650555/c-sharp-underlying-types-of-enums) – nawfal Dec 01 '13 at 12:25
  • On one code base, we converted some enums to byte and saved a huge amount of heap space, because the structures and classes were instantiated so many times. So there are advantages and good reasons to ignore this warning, if you are using too much heap memory. – Frank Hileman Feb 28 '14 at 16:25
  • Another usecase where this might matter (at least it does currently for me ^^) is transferring lots of enum values via network. You can save up 75% of bandwith by sending those enums that have the type `byte` as single `byte` instead of an `int` (4 bytes). – derHugo Sep 11 '20 at 10:10

3 Answers3

41

Have a look on MSDN for the reason.

Here is an excerpt:

An enumeration is a value type that defines a set of related named constants. By default, the System.Int32 data type is used to store the constant value. Even though you can change this underlying type, it is not necessary or recommended for most scenarios. Note that no significant performance gain is achieved by using a data type that is smaller than Int32. If you cannot use the default data type, you should use one of the Common Language System (CLS)-compliant integral types, Byte, Int16, Int32, or Int64 to make sure that all values of the enumeration can be represented in CLS-compliant programming languages.

Jason Down
  • 21,731
  • 12
  • 83
  • 117
29

There are specific situations where narrowing the underlying type brings some advantages, for example performance related or forcing a particular memory layout when interfacing to unmanaged code.

Consider this sample:

using System;

public enum Operations_PerHourType //   : byte
{
    Holes = 1,
    Pieces = 2,
    Sheets = 3,
    Strips = 4,
    Studs = 5
}

class Program
{
    static void Main()
    {
        long before = GC.GetTotalMemory(false);
        var enums = new Operations_PerHourType[10000];
        long after = GC.GetTotalMemory(false);

        Console.WriteLine(after - before);
        // output  (byte): 12218 (I'm using Mono 2.8)
        // output (Int32): 40960
    }
}

This code consumes roughly 40 KB of the heap. Now specify (uncomment) the underlying type as byte and recompile. Wow. Suddenly we only need roughly 10 KB.

Compacting memory like this may sometimes make a program slower, not faster, depending on particular access patterns and data sizes. There is no way to know for sure than to make some measurements and attempt to generalize to other possible circumstances. Sequential traversal of smaller data is usually faster.

However, developing a habit of specifying narrow types just because it is usually possible and sometimes crucial, is not a good idea. Memory savings rarely materialize due to memory alignment of surrounding wider data types. Performance is then either the same or slightly worse due to additional instructions needed to mask away padding bytes.

As another answer has already put it well, follow the Int32 crowd that the runtime is optimized for, until you have to start profiling and addressing real memory hogs in your application.

Jirka Hanika
  • 13,301
  • 3
  • 46
  • 75
21

According to the documentation, there is no performance gain from using a byte instead of INT32. Unless there is a reason to do so, they recommend not changing it. The underlying idea, is that .NET is optimized for using INT32 in many scenarios, and they selected that for enums for a reason. You don't get anything in your scenario by changing it, so why bother.

http://msdn.microsoft.com/en-us/library/ms182147.aspx

This also talks about how .NET is optimized to use 32 bit integers: .NET Optimized Int32

Community
  • 1
  • 1
kemiller2002
  • 113,795
  • 27
  • 197
  • 251
  • 2
    It is not .NET that is optimized for using INT32, but rather the underlying CPU, most notably because of the register size/size of the assembly instructions operands - which happens to be 32 bit on a 32 bit system. If the size of the value is different it needs to be aligned/padded and extra cpu cycles are needed for just that. Interestingly this is mentioned in the thread you linked. Most likely the overhead is minimal. The point is tho, that using BYTE instead of INT32 for performance reasons will actually have the opposite effect. Thats the case for every HL language. – Sascha Hennig Apr 19 '12 at 10:32
  • 1
    @SaschaHennig - That thread gives good, but incomplete background. Read also this one: http://stackoverflow.com/questions/1054657/memory-alignment-on-a-32-bit-intel-processor to learn why your Int32 based enum will sometimes take up as much as 8 bytes in a struct layout, and that's a Microsoft made performance related, double edged decision that you can override if you know what you are doing. It's not framework or hardware who is in charge. They work together. – Jirka Hanika Apr 19 '12 at 16:12
  • @Jirka Hanika - interesting read, thanks. Any HL language can only optimize the use of variables (or anything really) in the context of the hardware it is running. Still it is neither framework or hardware thats in charge, ultimatively it is the coder. – Sascha Hennig Apr 20 '12 at 13:12