Base struct as pointer or sub structs as pointers

Hi,

I was wondering is there is any difference (in terms of efficient memory usage) between versions below. First one is returned as pointer but not its inner objects. Second version uses inner objects as pointer but not the base one.

Note: Objects can get big, I kept them small for now. FYI - BulkResponse is not meant to be mutated at all. It will just be returned from one package to another, that’s it.

Thanks

V1

type Response struct {
	Duration   int 
	Success    bool
	Operations []Operation
}

type Operation struct {
	Create Item
	Update Item
	Delete Item
	List   Item
}

type Item struct {
	ID     string 
	Errors []Error
}

type Error struct {
	Code    int
	Reason  string
	Message string
}

func Version1() *BulkResponse {
	res := &BulkResponse{}
	// Build response ....
	return res
}

V2

type Response struct {
	Duration   int 
	Success    bool
	Operations []*Operation
}

type Operation struct {
	Create Item
	Update Item
	Delete Item
	List   Item
}

type Item struct {
	ID     string 
	Errors []*Error
}

type Error struct {
	Code    int
	Reason  string
	Message string
}

func Version2() BulkResponse {
	res := BulkResponse{}
	// Build response ....
	return res
}

This is similar in nature to this question where I posted a bunch of information that you might find helpful:

And in that thread I told the OP to benchmark it:

Which is exactly what I think you should do. Write some benchmarks and test memory usage. Also if you really want to get into memory optimization of your structs, you could take a look at padding/alignment. There’s a tool you can use to analyze your project:

1 Like

Here are some thoughts if you want to reason about it (but only benchmarks will give you the practical truth for your case):

  • An additional layer of pointers will increase memory usage, since you save another value (the pointer) in addition to the actual data. And having a pointer for each slice object will create a lot of additional memory consumption if you have a lot of tiny items.
  • Your structs are quite tiny (slices are just pointers with length, strings are just slices)
  • overall memory footprint will highly depend on your strings. If your strings are dynamic and long they will probably dominate your memory footprint by far (making string interning the most promising avenue to reduce memory usage)
  • If you change the contents of the slice (add/remove items) having pointers of structs will make a difference, since pure structs will be inlined to a single continuous block of memory, while with pointers the slice will only cotain pointers with all the items flying around in the heap as individually allocated pieces of memory (which might increase memory footprint a lot, depending the size/alignment of each memory allocation)

A good rule of thumb is usually to avoid pointers, if you don’t have a solid use case why you need them, or why they are faster. If you create these structs once and don’t change them, having them in a single continuous memory block (maybe even on the stack) will usually be preferable to having them sprinkled as fragments around the heap.

2 Likes