inefficient enum layout with optionals

i noticed that this enum takes up 9 bytes of storage

  1> enum QuantizationTable
  2. {
  3. case q8 (UnsafeMutablePointer<UInt8>),
  4. q16(UnsafeMutablePointer<UInt16>)
  5. }
  6> MemoryLayout<QuantizationTable>.size
$R0: Int = 9

but when i make it optional, it takes up 10

  7> MemoryLayout<QuantizationTable?>.size
$R1: Int = 10

can’t the compiler just tack the nil case onto the original enum as a third
case? after all, this works fine

  1> enum QuantizationTable
  2. {
  3. case q8 (UnsafeMutablePointer<UInt8>),
  4. q16(UnsafeMutablePointer<UInt16>),
  5. none
  6. }
  7> MemoryLayout<QuantizationTable>.size
$R0: Int = 9

the way i’m using it atm it all gets padded to 16 anyway so i don’t care
that much but is this something that’s being looked at?

We could, yes. Our experience with enum layout optimization is that it's one of those things that doesn't seem to bottom out: you can always keep improving it in one way or another.

It is an important goal of our ABI stability plan that future versions of Swift will be able to improve layouts for types that aren't required to interoperate with older versions, crucially including the private and internal types in a module.

John.

···

On Nov 6, 2017, at 3:46 AM, Kelvin Ma via swift-dev <swift-dev@swift.org> wrote:
i noticed that this enum takes up 9 bytes of storage

  1> enum QuantizationTable
  2. {
  3. case q8 (UnsafeMutablePointer<UInt8>),
  4. q16(UnsafeMutablePointer<UInt16>)
  5. }
  6> MemoryLayout<QuantizationTable>.size
$R0: Int = 9

but when i make it optional, it takes up 10

  7> MemoryLayout<QuantizationTable?>.size
$R1: Int = 10

can’t the compiler just tack the nil case onto the original enum as a third case? after all, this works fine

  1> enum QuantizationTable
  2. {
  3. case q8 (UnsafeMutablePointer<UInt8>),
  4. q16(UnsafeMutablePointer<UInt16>),
  5. none
  6. }
  7> MemoryLayout<QuantizationTable>.size
$R0: Int = 9

the way i’m using it atm it all gets padded to 16 anyway so i don’t care that much but is this something that’s being looked at?

In this specific case, I'm a bit surprised our existing mechanisms didn't avoid spilling a byte here—I thought we considered the tag byte of an enum to have spare bits, which should have let us use a spare bit from QuantizationTable to represent nil in QuantizationTable?. Seems bug-worthy to me. (I would say there's still a good amount of slack in our current implementation to be tightened up before we start invoking the full employment theorem or halting problem anyway.)

-Joe

···

On Nov 6, 2017, at 1:04 AM, John McCall via swift-dev <swift-dev@swift.org> wrote:

On Nov 6, 2017, at 3:46 AM, Kelvin Ma via swift-dev <swift-dev@swift.org> wrote:
i noticed that this enum takes up 9 bytes of storage

  1> enum QuantizationTable
  2. {
  3. case q8 (UnsafeMutablePointer<UInt8>),
  4. q16(UnsafeMutablePointer<UInt16>)
  5. }
  6> MemoryLayout<QuantizationTable>.size
$R0: Int = 9

but when i make it optional, it takes up 10

  7> MemoryLayout<QuantizationTable?>.size
$R1: Int = 10

can’t the compiler just tack the nil case onto the original enum as a third case? after all, this works fine

  1> enum QuantizationTable
  2. {
  3. case q8 (UnsafeMutablePointer<UInt8>),
  4. q16(UnsafeMutablePointer<UInt16>),
  5. none
  6. }
  7> MemoryLayout<QuantizationTable>.size
$R0: Int = 9

the way i’m using it atm it all gets padded to 16 anyway so i don’t care that much but is this something that’s being looked at?

We could, yes. Our experience with enum layout optimization is that it's one of those things that doesn't seem to bottom out: you can always keep improving it in one way or another.

It is an important goal of our ABI stability plan that future versions of Swift will be able to improve layouts for types that aren't required to interoperate with older versions, crucially including the private and internal types in a module.