Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Slices of structs vs. slices of pointers to structs

I often work with slices of structs. Here's an example for such a struct:

type MyStruct struct {     val1, val2, val3    int     text1, text2, text3 string     list                []SomeType } 

So I define my slices as follows:

[]MyStruct 

Let's say I have about a million elements in there and I'm working heavily with the slice:

  • I append new elements often. (The total number of elements is unknown.)
  • I sort it every now and then.
  • I also delete elements (although not as much as adding new elements).
  • I read elements often and pass them around (as function arguments).
  • The content of the elements themselves doesn't get changed.

My understanding is that this leads to a lot of shuffling around of the actual struct. The alternative is to create a slice of pointers to the struct:

[]*MyStruct 

Now the structs remain where they are and we only deal with pointers which I assume have a smaller footprint and will therefore make my operations faster. But now I'm giving the garbage collector a lot more work.

  • Can you provide general guidelines of when to work with structs directly vs. when to work with pointers to structs?
  • Should I worry about how much work I leave to the GC?
  • Is the performance overhead of copying a struct vs. copying a pointer negligible?
  • Maybe a million elements is not much. How does all of this change when the slice gets much bigger (but still fits in RAM, of course)?
like image 393
Oliver Avatar asked Dec 23 '14 14:12

Oliver


1 Answers

Just got curious about this myself. Ran some benchmarks:

type MyStruct struct {     F1, F2, F3, F4, F5, F6, F7 string     I1, I2, I3, I4, I5, I6, I7 int64 }  func BenchmarkAppendingStructs(b *testing.B) {     var s []MyStruct      for i := 0; i < b.N; i++ {         s = append(s, MyStruct{})     } }  func BenchmarkAppendingPointers(b *testing.B) {     var s []*MyStruct      for i := 0; i < b.N; i++ {         s = append(s, &MyStruct{})     } } 

Results:

BenchmarkAppendingStructs  1000000        3528 ns/op BenchmarkAppendingPointers 5000000         246 ns/op 

Take aways: we're in nanoseconds. Probably negligible for small slices. But for millions of ops, it's the difference between milliseconds and microseconds.

Btw, I tried running the benchmark again with slices which were pre-allocated (with a capacity of 1000000) to eliminate overhead from append() periodically copying the underlying array. Appending structs dropped 1000ns, appending pointers didn't change at all.

like image 120
Russ Egan Avatar answered Oct 17 '22 08:10

Russ Egan