Boost Go Performance with Object Pools: sync.Pool, Channels, and Third‑Party Libraries

This article explains the concept of object pools in Go, outlines their performance benefits, demonstrates how to implement pools using the standard sync.Pool and channel approaches with full code examples, and reviews several third‑party libraries for advanced pooling needs.

FunTester
FunTester
FunTester
Boost Go Performance with Object Pools: sync.Pool, Channels, and Third‑Party Libraries

What Is an Object Pool?

An object pool pre‑creates a set of reusable objects at application start and recycles them instead of repeatedly allocating and freeing memory, which reduces allocation overhead and improves performance, especially for short‑lived objects.

Why Use Object Pools in High‑Performance Go Applications?

Go’s concurrent mark‑and‑sweep garbage collector can pause the program when many objects are created and destroyed. Frequent allocation leads to memory fragmentation, longer GC pauses, and higher system‑resource consumption. Object pools mitigate these issues by reusing existing objects, lowering GC pressure and improving memory utilization.

Implementing a Pool with sync.Pool

sync.Pool

is part of Go’s standard library and provides a thread‑safe pool for any object type. It offers Get to retrieve an object and Put to return it. Objects may be reclaimed by the GC at any time, so callers must be prepared to re‑initialize after Get.

package pool

import (
    "funtester/ftool"
    "log"
    "sync"
    "testing"
)

// PooledObject represents the pooled data structure
type PooledObject struct {
    Name    string
    Age     int
    Address string
}

func NewObject() *PooledObject {
    log.Println("创建对象")
    return &PooledObject{Name: "", Age: 0, Address: ""}
}

func (m *PooledObject) Reset() {
    m.Name = ""
    m.Age = 0
    m.Address = ""
    log.Println("重置对象")
}

type ObjectPool struct {
    ObjPool sync.Pool
    Name    string
}

func NewPool(size int) *ObjectPool {
    return &ObjectPool{
        Name: "FunTester测试",
        ObjPool: sync.Pool{New: func() interface{} { return NewObject() }},
    }
}

func (p *ObjectPool) Get() *PooledObject {
    return p.ObjPool.Get().(*PooledObject)
}

func (p *ObjectPool) Back(obj *PooledObject) {
    obj.Reset()
    p.ObjPool.Put(obj)
}

func TestPool1(t *testing.T) {
    pool := NewPool(1)
    get := pool.Get()
    get.Name = "FunTester"
    get.Age = 18
    get.Address = "地球"
    log.Printf("%T %s", get, ftool.ToString(get))
    pool.Back(get)
    get2 := pool.Get()
    log.Printf("%T %s", get2, ftool.ToString(get2))
}

Console output demonstrates object creation, reuse, and reset:

=== RUN   TestPool1
2024/01/19 23:05:17 创建对象
2024/01/19 23:05:17 *pool.PooledObject &{FunTester 18 地球}
2024/01/19 23:05:17 重置对象
2024/01/19 23:05:17 *pool.PooledObject &{ 0 }
--- PASS: TestPool1 (0.00s)
PASS

Implementing a Pool with chan

Using a buffered channel to store objects provides atomic, thread‑safe borrowing and returning. The channel capacity controls the maximum pool size, and the operation blocks when the pool is empty or full, enabling back‑pressure handling.

package pool

import (
    "log"
    "reflect"
    "testing"
)

type ObjectPool2 struct {
    objects chan *PooledObject
    Name    string
}

func NewPool2(size int) *ObjectPool2 {
    return &ObjectPool2{objects: make(chan *PooledObject, size), Name: "FunTester测试"}
}

func (p *ObjectPool2) Get2() *PooledObject {
    select {
    case obj := <-p.objects:
        return obj
    default:
        log.Println("额外创建对象")
        return NewObject()
    }
}

func (p *ObjectPool2) Back(obj *PooledObject) {
    obj.Reset()
    select {
    case p.objects <- obj:
    default:
        obj = nil
        log.Println("丢弃对象")
    }
}

func TestPool2(t *testing.T) {
    pool := NewPool2(1)
    get := pool.Get2()
    object := pool.Get2()
    log.Printf("%T", get)
    log.Println(reflect.TypeOf(get))
    pool.Back(get)
    pool.Back(object)
}

Sample console output shows extra object creation, reuse, and discarding when the channel is full:

=== RUN   TestPool2
2024/01/19 23:19:42 额外创建对象
2024/01/19 23:19:42 创建对象
2024/01/19 23:19:42 额外创建对象
2024/01/19 23:19:42 创建对象
2024/01/19 23:19:42 *pool.PooledObject
2024/01/19 23:19:42 *pool.PooledObject
2024/01/19 23:19:42 重置对象
2024/01/19 23:19:42 重置对象
2024/01/19 23:19:42 丢弃对象
--- PASS: TestPool2 (0.00s)
PASS

Third‑Party Object‑Pool Libraries

Several open‑source Go libraries provide more feature‑rich pooling mechanisms: github.com/fatih/pool – a generic object pool with customizable creation, destruction, and validation logic. github.com/panjf2000/ants/v2 – a high‑performance goroutine pool that can also serve as a generic object pool. github.com/jolestar/go-commons-pool – a versatile pool supporting extensive configuration options. github.com/avast/retry-go – offers a flexible pool with retry strategies for acquiring objects.

These libraries extend the capabilities of sync.Pool and channel‑based pools, allowing developers to choose the most suitable implementation for their specific performance and flexibility requirements.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

performanceconcurrencyGosync.Poolobject pool
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.