DEV Community

Pavel Sanikovich
Pavel Sanikovich

Posted on

Golang sync.Pool benefits + Benchmarks

#go

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:

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
}
Enter fullscreen mode Exit fullscreen mode

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

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() {
}

Enter fullscreen mode Exit fullscreen mode

main_test.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()
}

Enter fullscreen mode Exit fullscreen mode

Benchmark result

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

Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
metal3d profile image
Patrice Ferlet

Please, use Markdown syntax to propose source code instead of using image.