Sync. Pool is a memory Pool, but I don’t know how to use it. Sync. Pool is a memory Pool. Because sync.Pool source code involves go scheduling knowledge, if not clear go scheduling, do not understand is normal. Therefore, it is suggested to learn about go scheduling first
What is the
Pool if look from the name, is the meaning of the pool, but the pool is used to describe is not appropriate, should be called a cache, because no notice by the pool is filled with objects can be recycled, so the pool is not suitable for when a socket connection pool
What’s the use of
First look at the source code comments
// Pool's purpose is to cache allocated but unused items for later reuse,
// relieving pressure on the garbage collector. That is, it makes it easy to
// build efficient, thread-safe free lists. However, it is not suitable for all
// free lists.
Copy the code
Sync. Pool is a good solution for many places that need to re-allocate and reclaim memory, because frequently allocating and reclaiming memory can put a certain amount of pressure on the GC. Sync. Pool can cache objects that are not used for the time being and can be used for the next time without having to re-allocate memory. Reuse object memory, reduce GC pressure, improve system performance.
How to use
Let’s start with an example
package main
import(
"fmt"
"sync"
)
func main(a) {
// Create a pool that caches int objects
p := &sync.Pool{
New: func(a) interface{} {
return 0
},
}
a := p.Get().(int)
p.Put(1)
b := p.Get().(int)
fmt.Println(a, b)
}
Copy the code
The output is 0, 1, you can specify a New function when you create it, and when you get an object how can you not find the cached object in the pool it will create a return using the specified New function, or return nil if there is no New function, it’s very simple to use.
After reading the source code comments, there are a few points to note:
-
There is no relationship between the data that gets () and the data that gets ()
-
Pool is thread-safe
-
The size of the cache pool is unlimited (limited to memory)
-
The poolCleanup function is registered when the pool package is initialized, which clears all cached objects in all pools. This function is called before each GC, so the sync. pool cache only lasts between gc and GC
func init(a) { runtime_registerPoolCleanup(poolCleanup) } Copy the code
The overhead of caching objects
How do you efficiently use the same pool across multiple Goroutines? The official approach is to minimize contention because sync.pool assigns a subpool to each P. As shown below.
When put and GET are performed, the Goroutine is pinned to a subpool of P and then operated on. Private objects are only used by the current P and do not need to be locked. Shared list objects are shared with other P’s, so they need to be locked
Get process:
- Be fixed at
P
If the private object is not empty, return the value obtained, and then empty the private object - If the private object is empty, get it from the shared object list (lock required)
- If the list of shared objects is empty, go elsewhere
P
List of shared objectsstealOne (need to lock) - If the other
P
The list of shared objects is also empty, thennew
A out
You can see that a get operation is locked at least 0 times and a maximum of N (N equals MAXPROCS) times.
The process of the put
-
Fixed to some P, if the private object is empty, put it on the private object;
-
Otherwise, it is added to the shared list of the P subpool (lock required).
You can see that a PUT operation is locked at least 0 times and a maximum of 1 times.
In general, pools reduce the burden of the GC, but they are also expensive