Skip to content

Latest commit

 

History

History
81 lines (69 loc) · 17.1 KB

File metadata and controls

81 lines (69 loc) · 17.1 KB

When will it happen?

So the question you may be asking is when will this happen? When does storing a value in an interface qualify for not needing a memory allocation on the heap? Well, there are a few criteria:

  • Zero values, including 0, nil, and an empty string "" all qualify.
  • Any value that is type which is a single byte wide, such as bool, int8, and uint8.
  • Any integer type with a value that is in the inclusive range of 0-255.
  • In some cases if T is subject to an optimization, so too will struct{a T}.

In order to produce a more comprehensive dataset for when this behavior could occur, we can run the following command:

docker run -it --rm go-interface-values:latest \
  bash -c 'cd ./tests/mem && \
  go tool compile -S -wb=false *.go | \
  python3 ../../hack/asm2md.py --no-echo'

The output will be a markdown table that indicates the assembly instruction used to store each of the following types, both their zero and non-zero values, in an interface. Some notes about the following information:

  • The table was generated on a linux/amd64 system.

  • The functions to store the zero and non-zero values are the same for both assembly and Go, but I had already written the parser to figure out both, so I kept them in.

  • For some types Go uses the same conversion function for T and struct{a T}, including:

    • int, int16, int32, int64
    • uint, uint16, uint32, uint64
    • float32, float64
    • rune (which has an underlying type of int32)
    • string and struct{a string}

    This means if Go has an optimization for T in the above list, the same optimization applies to struct{a T}.

  • Strangely this behavior does not extend to struct{a T} when T is bool, int8, or uint8, all single-byte wide values.

Type Line no Op to store zero value in asm ...in go Op to store non-zero, random value in asm ...in go
int 32 CALL runtime.convT64 CALL runtime.convT64
int8 60 LEAQ runtime.staticuint64s LEAQ runtime.staticuint64s
int16 88 CALL runtime.convT16 CALL runtime.convT16
int32 116 CALL runtime.convT32 CALL runtime.convT32
int64 144 CALL runtime.convT64 CALL runtime.convT64
uint 172 CALL runtime.convT64 CALL runtime.convT64
uint8 200 LEAQ runtime.staticuint64s LEAQ runtime.staticuint64s
uint16 228 CALL runtime.convT16 CALL runtime.convT16
uint32 256 CALL runtime.convT32 CALL runtime.convT32
uint64 284 CALL runtime.convT64 CALL runtime.convT64
float32 312 CALL runtime.convT32 CALL runtime.convT32
float64 340 CALL runtime.convT64 CALL runtime.convT64
complex64 368 CALL runtime.convTnoptr CALL runtime.convTnoptr
complex128 396 CALL runtime.convTnoptr CALL runtime.convTnoptr
byte 424 LEAQ runtime.staticuint64s LEAQ runtime.staticuint64s
bool 452 LEAQ runtime.staticuint64s LEAQ runtime.staticuint64s
rune 480 CALL runtime.convT32 CALL runtime.convT32
string 508 CALL runtime.convTstring CALL runtime.convTstring
struct_int 536 CALL runtime.convT64 CALL runtime.convT64
struct_int8 564 CALL runtime.convTnoptr CALL runtime.convTnoptr
struct_int16 592 CALL runtime.convT16 CALL runtime.convT16
struct_int32 620 CALL runtime.convT32 CALL runtime.convT32
struct_int64 648 CALL runtime.convT64 CALL runtime.convT64
struct_uint 676 CALL runtime.convT64 CALL runtime.convT64
struct_uint8 704 CALL runtime.convTnoptr CALL runtime.convTnoptr
struct_uint16 732 CALL runtime.convT16 CALL runtime.convT16
struct_uint32 760 CALL runtime.convT32 CALL runtime.convT32
struct_uint64 788 CALL runtime.convT64 CALL runtime.convT64
struct_float32 816 CALL runtime.convT32 CALL runtime.convT32
struct_float64 844 CALL runtime.convT64 CALL runtime.convT64
struct_complex64 872 CALL runtime.convTnoptr CALL runtime.convTnoptr
struct_complex128 900 CALL runtime.convTnoptr CALL runtime.convTnoptr
struct_byte 928 CALL runtime.convTnoptr CALL runtime.convTnoptr
struct_bool 956 CALL runtime.convTnoptr CALL runtime.convTnoptr
struct_rune 984 CALL runtime.convT32 CALL runtime.convT32
struct_string 1012 CALL runtime.convTstring CALL runtime.convTstring
struct_int32_int32 1040 CALL runtime.convTnoptr CALL runtime.convTnoptr
struct_int32_int64 1068 CALL runtime.convTnoptr CALL runtime.convTnoptr
struct_array_bytes_7 1096 CALL runtime.convTnoptr CALL runtime.convTnoptr
struct_byte_7 1124 CALL runtime.convTnoptr CALL runtime.convTnoptr

Okay, now we know what each function is used when storing specific types in interfaces, but we don't have a clear picture of when those functions malloc and when they do not. Well, I have a clear idea, and you will too once you click to the next page 😃


Next: Overall impact