Sizeof struct in Go
Asked Answered
M

6

38

I'm having a look at Go, which looks quite promising. I am trying to figure out how to get the size of a go struct, for example something like

type Coord3d struct {
    X, Y, Z int64
}

Of course I know that it's 24 bytes, but I'd like to know it programmatically..

Do you have any ideas how to do this ?

Mur answered 21/1, 2010 at 23:18 Comment(0)
M
46
import unsafe "unsafe"

/* Structure describing an inotify event.  */
type INotifyInfo struct {
    Wd     int32  // Watch descriptor
    Mask   uint32 // Watch mask
    Cookie uint32 // Cookie to synchronize two events
    Len    uint32 // Length (including NULs) of name
}

func doSomething() {
    var info INotifyInfo
    const infoSize = unsafe.Sizeof(info)
    ...
}

NOTE: The OP is mistaken. The unsafe.Sizeof does return 24 on the example Coord3d struct. See comment below.

Monkshood answered 22/1, 2010 at 7:20 Comment(4)
The OP specifically said "I tried unsafe.Sizeof and it didn't work."Irrupt
if not working with 64 bit ints then should be lodged as a bug. The use of unsafe.Sizeof appears in lots of places in the Go library code and is intended as a cannoical way to do this, especially when interfacing to C libraries and OS API calls. At any rate, it certainly works correctly in code I've written using it.Monkshood
So I checked with the code shown below. The OP was mistaken. The unsafe.Sizeof does correctly return 24 for his example data structure. (My Go compiler was updated first week of January 2010.) package main import ( unsafe "unsafe" fmt "fmt" ) type Coord3d struct { X, Y, Z int64 } func main() { var pt Coord3d; const size = unsafe.Sizeof(pt); fmt.Printf("Size of Coord3d: %d\n",size) }Monkshood
You're right, I was mistaken, I did some experiments with binary.TotalSize and mixed that up with my Sizeof experiments.. Thanks for the demo solution, I couldn't really conclude the right use of it, as I just started to use Go a few days ago, and now trying out how to do the 'usual stuff'.. Finding usefull examples isn't that easy yet with Go, although reading the library source turned out to be quite instructive (as with python, for example).Rimose
O
53

Roger already showed how to use Sizeof method from the unsafe package. Make sure you read this before relying on the value returned by the function:

The size does not include any memory possibly referenced by x. For instance, if x is a slice, Sizeof returns the size of the slice descriptor, not the size of the memory referenced by the slice.

In addition to this I wanted to explain how you can easily calculate the size of any struct using a couple of simple rules. And then how to verify your intuition using a helpful service.


The size depends on the types it consists of and the order of the fields in the struct (because different padding will be used). This means that two structs with the same fields can have different size.

For example this struct will have a size of 32

struct {
    a bool
    b string
    c bool
}

and a slight modification will have a size of 24 (a 25% difference just due to a more compact ordering of fields)

struct {
    a bool
    c bool
    b string
}

enter image description here enter image description here

As you see from the pictures, in the second example we removed one of the paddings and moved a field to take advantage of the previous padding. An alignment can be 1, 2, 4, or 8. A padding is the space that was used to fill in the variable to fill the alignment (basically wasted space).

Knowing this rule and remembering that:

  • bool, int8/uint8 take 1 byte
  • int16, uint16 - 2 bytes
  • int32, uint32, float32 - 4 bytes
  • int64, uint64, float64, pointer - 8 bytes
  • string - 16 bytes (2 alignments of 8 bytes)
  • any slice takes 24 bytes (3 alignments of 8 bytes). So []bool, [][][]string are the same (do not forget to reread the citation I added in the beginning)
  • array of length n takes n * type it takes of bytes.

Armed with the knowledge of padding, alignment and sizes in bytes, you can quickly figure out how to improve your struct (but still it makes sense to verify your intuition using the service).

Opportina answered 26/6, 2016 at 1:1 Comment(6)
It is correct. I can see the difference size. {bool, int8} is 2, {bool, int16} is 4, {bool, int32} is 8.Reduplicative
What about a struct holding another struct - something like sync.Mutex? In regards to the ordering.Ginger
The Service linked to in the answer seems to be down.Blemish
The compiler doesn't optimize the ordering of the struct fields for you to reduce memory utilization? That seems like a big oversight to me.Claritaclarity
Apparently the reason is because often today people want to optimize to avoid cache misses by ordering the struct by having related fields together and care less about memory. So they leave the ordering of fields to the programmer. See github.com/golang/go/issues/10014#issuecomment-91436342Claritaclarity
Powerful answerJudi
M
46
import unsafe "unsafe"

/* Structure describing an inotify event.  */
type INotifyInfo struct {
    Wd     int32  // Watch descriptor
    Mask   uint32 // Watch mask
    Cookie uint32 // Cookie to synchronize two events
    Len    uint32 // Length (including NULs) of name
}

func doSomething() {
    var info INotifyInfo
    const infoSize = unsafe.Sizeof(info)
    ...
}

NOTE: The OP is mistaken. The unsafe.Sizeof does return 24 on the example Coord3d struct. See comment below.

Monkshood answered 22/1, 2010 at 7:20 Comment(4)
The OP specifically said "I tried unsafe.Sizeof and it didn't work."Irrupt
if not working with 64 bit ints then should be lodged as a bug. The use of unsafe.Sizeof appears in lots of places in the Go library code and is intended as a cannoical way to do this, especially when interfacing to C libraries and OS API calls. At any rate, it certainly works correctly in code I've written using it.Monkshood
So I checked with the code shown below. The OP was mistaken. The unsafe.Sizeof does correctly return 24 for his example data structure. (My Go compiler was updated first week of January 2010.) package main import ( unsafe "unsafe" fmt "fmt" ) type Coord3d struct { X, Y, Z int64 } func main() { var pt Coord3d; const size = unsafe.Sizeof(pt); fmt.Printf("Size of Coord3d: %d\n",size) }Monkshood
You're right, I was mistaken, I did some experiments with binary.TotalSize and mixed that up with my Sizeof experiments.. Thanks for the demo solution, I couldn't really conclude the right use of it, as I just started to use Go a few days ago, and now trying out how to do the 'usual stuff'.. Finding usefull examples isn't that easy yet with Go, although reading the library source turned out to be quite instructive (as with python, for example).Rimose
A
10

binary.TotalSize is also an option, but note there's a slight difference in behavior between that and unsafe.Sizeof: binary.TotalSize includes the size of the contents of slices, while unsafe.Sizeof only returns the size of the top level descriptor. Here's an example of how to use TotalSize.

package main

import (
    "encoding/binary"
    "fmt"
    "reflect"
)

type T struct {
    a uint32
    b int8
}

func main() {
    var t T
    r := reflect.ValueOf(t)
    s := binary.TotalSize(r)

    fmt.Println(s)
}
Alton answered 23/1, 2010 at 19:28 Comment(3)
the NewValue is renamed to ValueOf in Go release.r60.3Katelyn
binary.Size is completely different from unsafe.Sizeof and exists for a completely different reason. It's not at all what the OP asked for.Shifflett
the function binary.TotalSize is no longer available in go. I think the code should be: var t T; s := binary.Size(t); fmt.Println(s)Tangelatangelo
I
1

This is subject to change but last I looked there is an outstanding compiler bug (bug260.go) related to structure alignment. The end result is that packing a structure might not give the expected results. That was for compiler 6g version 5383 release.2010-04-27 release. It may not be affecting your results, but it's something to be aware of.

UPDATE: The only bug left in go test suite is bug260.go, mentioned above, as of release 2010-05-04.

Hotei

Italianism answered 1/5, 2010 at 6:18 Comment(1)
I believe you're referring to issue/482 which was closed Dec 2010. Since that was way before Go1.0 should this "answer" still exist?Shifflett
A
0

In order to not to incur the overhead of initializing a structure, it would be faster to use a pointer to Coord3d:

package main

import (
    "fmt"
    "unsafe"
)

type Coord3d struct {
    X, Y, Z int64
}

func main() {
    var dummy *Coord3d
    fmt.Printf("sizeof(Coord3d) = %d\n", unsafe.Sizeof(*dummy))
}
Accumulator answered 3/6, 2015 at 22:44 Comment(1)
If you're going to rely on de-referencing a nil pointer (which only works here because of an implementation detail of the current unsafe.Sizeof implementation, I doubt this is legal code), you might as well eliminate any variables at all.Shifflett
L
0
/*
    returns the size of any type of object in bytes
*/

func getRealSizeOf(v interface{}) (int, error) {
    b := new(bytes.Buffer)
    if err := gob.NewEncoder(b).Encode(v); err != nil {
        return 0, err
    }
    return b.Len(), nil
}
Lifesize answered 30/11, 2022 at 16:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.