Memory used by enums


(Jon Hull) #1

I had a quick question on the memory used by enums with associated values in the current implementation. If I have an enum like the following:

enum MyEnum {
  case mostCommonlyUsed
  case somewhatCommon (Int,Int)
  case prettyRare (Int,Int,Int,Int,Int)
}

If this enum is going to be used by tens/hundreds of thousands of structs, am I actually saving any space by breaking out the rarer cases which store more data or is the footprint just equal to the largest case?

Thanks,
Jon


(Jens Alfke) #2

Enums are implemented a lot like C unions, so the size is equal to that of the largest case, plus a byte for the tag.

It’s interesting that the size in Daniel’s example is 41 bytes. That implies that the tag is placed at the end of the data, because putting a 1-byte field at the beginning would require 7 extra bytes of padding before the first Int. But the stride still needs to be 48 bytes to preserve the 8-byte-alignment of subsequent values.

This implies that you might be able to make an enum use up to 7 fewer bytes in practice, if you can make its largest case be one byte smaller than a multiple of a power of 2. Obviously not always attainable, but if you’re really groveling for space improvements you might be able to squeeze out that extra byte.

—Jens

···

On Apr 4, 2016, at 4:57 PM, Jonathan Hull via swift-users <swift-users@swift.org> wrote:

If this enum is going to be used by tens/hundreds of thousands of structs, am I actually saving any space by breaking out the rarer cases which store more data or is the footprint just equal to the largest case?


(Joe Groff) #3

The payloads are stored in-line by default, so yeah, the enum will be the size of its largest payload, possibly with extra space for tag bits if there's nowhere to cram the bits in the payload otherwise. You can mark your large, rare cases `indirect`, which will make the inline storage pointer-width, referencing a separate allocation:

enum MyEnum {
  case mostCommonlyUsed
  case somewhatCommon (Int,Int)
  indirect case prettyRare (Int,Int,Int,Int,Int)
}

-Joe

···

On Apr 4, 2016, at 4:57 PM, Jonathan Hull via swift-users <swift-users@swift.org> wrote:

I had a quick question on the memory used by enums with associated values in the current implementation. If I have an enum like the following:

enum MyEnum {
  case mostCommonlyUsed
  case somewhatCommon (Int,Int)
  case prettyRare (Int,Int,Int,Int,Int)
}

If this enum is going to be used by tens/hundreds of thousands of structs, am I actually saving any space by breaking out the rarer cases which store more data or is the footprint just equal to the largest case?


(Daniel Eggert) #4

1> enum MyEnum {
  2. case mostCommonlyUsed
  3. case somewhatCommon (Int,Int)
  4. case prettyRare (Int,Int,Int,Int,Int)
  5. }
  6> sizeof(MyEnum)
$R0: Int = 41
  7> strideof(MyEnum)
$R1: Int = 48

It depends on what you're doing. The pure "size" is 41 bytes, but the alignment of these would be 48 bytes.

Those numbers are very platform dependent -- these are on 64 bit OS X.

/Daniel

···

On 05 Apr 2016, at 01:57, Jonathan Hull via swift-users <swift-users@swift.org> wrote:

I had a quick question on the memory used by enums with associated values in the current implementation. If I have an enum like the following:

enum MyEnum {
  case mostCommonlyUsed
  case somewhatCommon (Int,Int)
  case prettyRare (Int,Int,Int,Int,Int)
}

If this enum is going to be used by tens/hundreds of thousands of structs, am I actually saving any space by breaking out the rarer cases which store more data or is the footprint just equal to the largest case?