Golang language temporary object pool – sync Pool

Time:2022-6-11

Hello, I’m Frank.
Welcome to click the blue text “golang language development stack” above to follow the official account.

01

introduce

sync.PoolIt is a data type provided by the sync package, also known as the temporary object pool. Its value is used to store a group of temporary objects that can be accessed independently. It reduces the number of new objects applied through pooling and improves the performance of the program.sync.PoolThe type is a struct type. Its value cannot be copied after it is first used. Becausesync.PoolAll objects stored in can be automatically deleted at any time, so thesync.PoolThe value of type must meet two conditions: first, whether the value exists or not will not affect the function of the program; second, the values can be substituted for each other.sync.PoolIt is goroutine concurrency safe and can be safely used by multiple goroutines at the same time;sync.PoolThe purpose of go is to cache allocated but unused objects for future reuse, thus reducing the performance impact of the garbage collector. Because the automatic garbage collection mechanism of go will have an STW time consumption, and a large number of objects are created on the heap, which will also increase the garbage collection mark time.

sync.PoolThe appropriate use of is to manage a set of temporary objects that are silently shared and potentially reused between concurrent independent clients of the package.sync.PoolProvides a way to allocate allocation overhead on many clients.

However, a free list maintained as part of a short-lived object does not apply tosync.Pool, because in this case, the expenses cannot be well allocated.

The standard library FMT package in the golang language usessync.Pool, it will use a dynamic buffer pool as the output cache. When a large number of goroutines are output concurrently, more buffers will be created and recycled when they are not needed.

02

Usage

sync.PollThe type contains two methods:

  • func (p *Pool) Put(x interface{})
  • func (p *Pool) Get() interface{}

Put()Used to store objects in the temporary object pool. It receives ainterface{}Parameters of empty interface type;Get()Used to get objects from the temporary object pool. It returns ainterface{}Return value of empty interface type.

Get()Select an arbitrary object from the temporary object pool, remove it from the temporary object pool, and return it to the caller.  Get()You can choose to ignore the temporary object pool and treat it as empty. The caller should not assume that thePut()Value of andGet()There is any relationship between the returned values.

IfGet()Return nil, andp.NewNot nil, thenGet()Return callp.NewResults of.  sync.PoolTypeNewField, field type is function typefunc() interface{}, representing the function that creates the temporary object. The result value of this function is not stored in the temporary object pool, but directly returned to theGet()Method.

It should be noted that,sync.PoolTypeNewThe value of the field also needs to be given when we initialize the object. Otherwise, theGet()Method, it is possible to get nil.

We have introduced when temporary objects will be created. Now we will introduce when temporary objects will be destroyed. We already knowsync.PoolInitialization is required before use. In fact, during initialization, a cleaning function is registered with the golang runtime to clean up all the created values in the temporary object pool. The golang runtime executes the cleaning function before garbage collection every time.

Example code:

`func main () {`
 `pool := &sync.Pool{`
 `New: func() interface{} {`
 `fmt. Println ("new object")`
 `return 0`
 `},`
 `}`
 `//If there is no data in the temporary object pool, new will be called. New will create a new object and return it directly. It will not be stored in the temporary object pool`
 `val := pool.Get().(int)`
 `fmt.Println(val)`
 `//Save`
 `pool.Put(10)`
 `//After calling GC () manually to validate GC, the objects in the temporary object pool will be emptied`
 `runtime.GC()`
 `//Take`
 `val2 := pool.Get().(int)`
 `fmt.Println(val2)`
`}`

03

Implementation principle

Before go1.13, there was a local pool list in the data structure of the temporary object pool. Each local pool contains three fields, namely, the field private for storing private temporary objects, and the fields shared and sync for sharing the temporary object list Embedded field of type mutex.

Lock contention will reduce the concurrency performance of programs. To optimize the concurrency performance of programs, you need to reduce or avoid the use of locks. In go1.13,sync.PoolThe optimization is to avoid using locks, change the locked queue to a lock free queue, and give the element to be removed another chance to “revive”.

Currentsync.PoolThe data structure of is as follows:

`type Pool struct {`
 `noCopy noCopy`
 `local     unsafe.Pointer // local fixed-size per-P pool, actual type is [P]poolLocal`
 `localSize uintptr        // size of the local array`
 `victim     unsafe.Pointer // local from previous cycle`
 `victimSize uintptr        // size of victims array`
 `// New optionally specifies a function to generate`
 `// a value when Get would otherwise return nil.`
 `// It may not be changed concurrently with calls to Get.`
 `New func() interface{}`
`}`

Local and victim are mainly used to store idle elements. During each GC, the pool will first remove the data in the victim field, and then give the data in the local field to victim. In this way, the local is cleared, and the local data in victim has the opportunity to be deleted againGet()Take away, if notGet()Take the data, and the victim data will be deleted by GC.

Read the following code, which is when GCsync.PoolProcessing logic for.

`func poolCleanup() {`
 `// This function is called with the world stopped, at the beginning of a garbage collection.`
 `// It must not allocate and probably should not call any runtime functions.`
 `// Because the world is stopped, no pool user can be in a`
 `// pinned section (in effect, this has all Ps pinned).`
 `// Drop victim caches from all pools.`
 `for _, p := range oldPools {`
 `p.victim = nil`
 `p.victimSize = 0`
 `}`
 `// Move primary cache to victim cache.`
 `for _, p := range allPools {`
 `p.victim = p.local`
 `p.victimSize = p.localSize`
 `p.local = nil`
 `p.localSize = 0`
 `}`
 `// The pools with non-empty primary caches now have non-empty`
 `// victim caches and no pools have primary caches.`
 `oldPools, allPools = allPools, nil`
`}`

The number of local pools in the local pool list is equal to the number of processors in the golang scheduler, that is, each local pool corresponds to a P. as we mentioned in the GMP article, a goroutine must be associated with a p before it can run. So the temporary object poolPut()AndGet()When a method is called, the local pool it will operate on depends on the P corresponding to the goroutine in which the calling code runs. This is why each local pool corresponds to a P.

`// Local per-P Pool appendix.`
`type poolLocalInternal struct {`
 `private interface{} // Can be used only by the respective P.`
 `shared  poolChain   // Local P can pushHead/popHead; any P can popTail.`
`}`
`type poolLocal struct {`
 `poolLocalInternal`
 `// Prevents false sharing on widespread platforms with`
 `// 128 mod (cache line size) = 0 .`
 `pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte`
`}`

After reading the above code, the poollocalinternal structure contains two fields private and shared. Private represents a cache element and can only be accessed by the goroutine of the current P. because a P can only execute one goroutine at the same time, there will be no concurrency problem. Shared can be accessed by any p, but only local P canpushHead/popHead, other p can onlypopTail, which is implemented using a lockless queue.

Access data:

Put()Method will first store the newly created temporary object in the local private field. If the private field has already stored a value, it will access the shared field and append the new temporary object to the end of the shared temporary object list.

Get()Method gives priority to accessing the private field to obtain data. Because there is no lock, it can obtain elements quickly. If the private field is empty, it will try to access the local shared field. If the local shared field is also empty, it will callgetSlow()Method to traverse the shared field of each local. As long as the shared field of a local is found to have a value, the last value of the shared temporary object list will be obtained and returned. If no value is found after traversing all local, it will try to access victim. First, it will find the value in the private field of victim. If it is not found, it will find the value in the shared field of victim. Finally, if it is not found, it will call the function to create a temporary object given by the new field during initialization to create a new object and return it. If the value of the new field is nil,Get()Method returns nil directly.

getSlow()Method processing logic:

`func (p *Pool) getSlow(pid int) interface{} {`
 `// See the comment in pin regarding ordering of the loads.`
 `size := runtime_LoadAcquintptr(&p.localSize) // load-acquire`
 `locals := p.local                            // load-consume`
 `// Try to steal one element from other procs.`
 `for i := 0; i < int(size); i++ {`
 `l := indexLocal(locals, (pid+i+1)%int(size))`
 `if x, _ := l.shared.popTail(); x != nil {`
 `return x`
 `}`
 `}`
 `// Try the victim cache. We do this after attempting to steal`
 `// from all primary caches because we want objects in the`
 `// victim cache to age out if at all possible.`
 `size = atomic.LoadUintptr(&p.victimSize)`
 `if uintptr(pid) >= size {`
 `return nil`
 `}`
 `locals = p.victim`
 `l := indexLocal(locals, pid)`
 `if x := l.private; x != nil {`
 `l.private = nil`
 `return x`
 `}`
 `for i := 0; i < int(size); i++ {`
 `l := indexLocal(locals, (pid+i)%int(size))`
 `if x, _ := l.shared.popTail(); x != nil {`
 `return x`
 `}`
 `}`
 `// Mark the victim cache as empty for future gets don't bother`
 `// with it.`
 `atomic.StoreUintptr(&p.victimSize, 0)`
 `return nil`
`}`

04

summary

In this article, we mainly introducesync.PoolData type, including its usage and implementation principle, has the advantage of reusing objects and reducing the overhead of object creation and GC. What we need to emphasize again is that,sync.PoolThe life cycle of is affected by GC, so it is not suitable for pooling that needs to manage its own life cycle, such as connection pooling.

Recommended reading:

Go language uses mutex mutex of sync package of standard library to solve data race

How to use the rwmutex read / write mutex of the sync package of the golang language standard library?

How to use the waitgroup of the sync package of the golang language standard library?

How to use cond of sync package in golang language standard library?

How to use once in the sync package of the golang language standard library?

Golang language standard library sync/atomic package atomic operation

Golang language standard library context package control goroutine

Golang language uses channel concurrent programming

The key value type selection of map in golang language. Is it concurrency safe?

reference material:
https://golang.org/pkg/sync/#…
https://golang.org/src/sync/p…

Golang language temporary object pool - sync Pool

Scan QR code and join wechat group

Golang language temporary object pool - sync Pool

Clicking “like” and “watching” is the greatest support👇

Golang language temporary object pool - sync Pool

For more highlights, please clickRead the original text “

Recommended Today

The extended tecdat|r language uses AR, Ma, ARIMA models to predict time series

Original link:http://tecdat.cn/?p=23558  This paper discusses the ARIMA model for prediction. Consider some simple stationary AR (1) simulation time series > for(t in 2:n) X\[t\]=phi*X\[t-1\]+E\[t\] > plot(X,type=”l”) If we fit an AR (1) model. arima(X,order=c(1,0,0), +             include.mean = FALSE) We observe the exponential decay of the predicted value towards 0 and the increased confidence interval (where the variance increases, from the variance of white […]