Golang sync.Pool benefits + Benchmarks
Tags: go
Published: 2023-10-11 12:59:00

The sync.Pool package in Go provides a simple object pool implementation. It can be used to improve the performance of allocating and releasing memory for frequently used objects. Here are some benefits of using sync.Pool and samples of usage with big structs:

1. Object Reuse: sync.Pool allows you to reuse allocated objects instead of constantly creating and garbage collecting them, which can be an expensive process. This reuse can significantly reduce the overhead of memory allocation and deallocation, resulting in improved performance.

2. Reduced Memory Pressure: By reusing objects from the pool, you can reduce the overall memory pressure on the system. This is especially useful when dealing with big structs or objects that consume a significant amount of memory. Reusing such objects helps to minimize the number of allocations and garbage collections.

3. Better Locality of Reference: Objects stored in a sync.Pool are typically kept in the local cache of the calling goroutine, improving the cache locality and reducing memory access latency. This can lead to better CPU cache utilization and faster processing.

Here's an example of using sync.Pool with big structs:


```go
package main

import (
	"fmt"
	"sync"
)

type BigStruct struct {
	// fields of the big struct
}

func main() {
	var bigStructPool = sync.Pool{
		New: func() interface{} {
			return &BigStruct{} 
           // Create a new instance of the big struct
		},
	}

	// Acquire a big struct from the pool
	bigStruct := bigStructPool.Get().(*BigStruct)
	defer bigStructPool.Put(bigStruct) 
   // Release the big struct back to the pool

// Reset the fields of the big struct before using it
// This step is necessary if the struct has
// internal state that should be reset
// bigStruct.Reset() or manually reset the fields

// Use the big struct
// ...

fmt.Println(bigStruct)

// After the big struct is used,
// it is returned to the pool for reuse
}
```

In this example, a sync.Pool is created with a New function that creates a new instance of the big struct. To acquire a big struct from the pool, we call Get() on the pool and cast the result to the appropriate type. After using the big struct, we defer the call to Put() on the pool to return it to the pool for reuse.

Make sure to properly reset the fields of the big struct before using it if it has any internal state that needs to be reset between uses. This ensures that any lingering state from previous uses is cleared.

By utilizing sync.Pool with big structs, you can minimize the overhead of memory allocations and garbage collections, thereby improving the overall performance of your Go application.

**Some Benhcmarks to rpeat**

_main.go_

```go
package main

import (
	"encoding/json"
	"sync"
)

type message struct {
	Field1    int64
	Filed2    string
	Filed3    []string
	Filed5    bool
	Field11   int64
	Filed22   string
	Filed33   []string
	Filed54   bool
	Field133  int64
	Filed244  string
	Filed355  []string
	Filed5555 bool
}

func (m *message) Reset() {
	m.Field1 = 0
	m.Filed2 = ""
	m.Filed3 = nil
	m.Filed5 = false
	m.Field11 = 0
	m.Filed22 = ""
	m.Filed33 = nil
	m.Filed54 = false
	m.Field133 = 0
	m.Filed244 = ""
	m.Filed355 = nil
	m.Filed5555 = false
}

type (
	NoPool   struct{}
	WithPool struct {
		msgPool *sync.Pool
	}
)

func (n *NoPool) Process() {
	m := message{
		Field1: 3832,
		Filed2: "some string",
		Filed3: []string{"some", "another"},
		Filed5: true,
	}
	json.Marshal(m)
}

func (n *WithPool) Process() {
	m := n.msgPool.Get().(*message)
	n.msgPool.Put(m)

	m.Field1 = 3832
	m.Filed2 = "some string"
	m.Filed3 = []string{"some", "another"}
	m.Filed5 = true

	json.Marshal(m)

	m.Reset()

}

func main() {
}

```
_main_test.go_

```go
package main

import (
	"sync"
	"testing"
)

func Benchmark_withoutPool(b *testing.B) {
	p := NoPool{}
	for i := 0; i < b.N; i++ {
		p.Process()
	}
	b.ReportAllocs()
}

func Benchmark_withPool(b *testing.B) {
	msgPool := sync.Pool{
		New: func() interface{} { return &message{} },
	}
	p := WithPool{
		msgPool: &msgPool,
	}
	for i := 0; i < b.N; i++ {
		p.Process()
	}
	b.ReportAllocs()
}

```

**Benchmark result**

```bash
Benchmark_withoutPool-4   	  732031	      2557 ns/op	     416 B/op	       3 allocs/op
Benchmark_withPool-4      	  834178	      1796 ns/op	     240 B/op	       2 allocs/op

```