We usually use Golang to build applications in high concurrency scenarios, but because Golang's built-in GC mechanism can affect the performance of the application, in order to reduce Gc,golang provides a mechanism for object reuse, which is sync. Pool object. Sync. Pool is scalable, concurrency-safe. Its size is limited only by the size of the memory and can be seen as a container for values that hold reusable objects. The purpose of the design is to store objects that have already been allocated but are temporarily unused, and to be taken directly from the pool when needed.
Any storage area where the value can be deleted at any time without notice, under high load can be dynamically expanded, the object pool will shrink when inactive.
Sync. The pool first declares two structural bodies
// Local per-P Pool appendix.
type poolLocalInternal struct {
private interface{} // Can be used only by the respective P.
shared []interface{} // Can be used by any P.
Mutex // Protects shared.
}
type poolLocal struct {
poolLocalInternal
// Prevents false sharing on widespread platforms with
// 128 mod (cache line size) = 0 .
pad [128 - unsafe.Sizeof(poolLocalInternal{})%128]byte
}
In order to make efficient use of goroutine,sync in multiple goroutine. The pool assigns each p (corresponding CPU) a local pools, and when a get or put operation is performed, the Goroutine and the child pool of a p are associated before the child pool is manipulated. Each P's sub-pool is divided into private objects and shared list objects, and private objects can be accessed only by a specific p, and the shared list object may be accessed by any p. Because a p can only perform one goroutine at a time, it does not need to be locked, but when it operates on a shared list object, it is possible to have more than one goroutine simultaneous operation, so lock is required.
It is noteworthy that there is a pad member in the poollocal structure in order to prevent false sharing. One common problem with cache usage is false sharing. False sharing can occur when different threads read and write different data on the same cache line at the same time. False sharing can cause severe system performance degradation on multicore processors. Specifically, you can refer to pseudo-sharing (false sharing).
Type sync. Pool has two public methods, one is get, the other is put, let's take a look at the source of the put.
// Put adds x to the pool.
func (p *Pool) Put(x interface{}) {
if x == nil {
return
}
if race.Enabled {
if fastrand()%4 == 0 {
// Randomly drop x on floor.
return
}
race.ReleaseMerge(poolRaceAddr(x))
race.Disable()
}
l := p.pin()
if l.private == nil {
l.private = x
x = nil
}
runtime_procUnpin()
if x != nil {
l.Lock()
l.shared = append(l.shared, x)
l.Unlock()
}
if race.Enabled {
race.Enable()
}
}
- If the value you put in is empty, return directly.
- Checks whether the current Goroutine sets the object pool private value, and if not, assigns X to its private member and sets X to nil.
- If the current Goroutine private value is already set, the value is appended to the shared list.
func (p *Pool) Get() interface{} {
if race.Enabled {
race.Disable()
}
l := p.pin()
x := l.private
l.private = nil
runtime_procUnpin()
if x == nil {
l.Lock()
last := len(l.shared) - 1
if last >= 0 {
x = l.shared[last]
l.shared = l.shared[:last]
}
l.Unlock()
if x == nil {
x = p.getSlow()
}
}
if race.Enabled {
race.Enable()
if x != nil {
race.Acquire(poolRaceAddr(x))
}
}
if x == nil && p.New != nil {
x = p.New()
}
return x
}
- Try to get an object value from the local pool corresponding to the local p and flush the value from the local pool.
- If it fails, it gets from the shared pool and removes the value from the Shared queue.
- If the acquisition fails, then steal one from the other P's shared pool and delete the value in the shared pool (P.getslow ()).
- If it still fails, assign a return value directly through new (), noting that the assigned value is not put into the pool. New () Returns the value of the user-registered new function and returns nil if the user is not registered with new.
Let's take a look at the init function.
func init() {
runtime_registerPoolCleanup(poolCleanup)
}
You can see that a poolcleanup function was registered at INIT, and he erased the sync. All cached objects in the pool, this registration function will run at each GC, so sync. The value in the pool is valid only for the period of time between two GC.
package main
import ( "sync" "time" "fmt" ) var bytePool = sync.Pool{
New: func() interface{} {
b := make([]byte, 1024) return &b
},
}
func main() { //defer //debug.SetGCPercent(debug.SetGCPercent(-1)) a := time.Now().Unix() for i:=0;i<1000000000;i++{
obj := make([]byte, 1024)
_ = obj
}
b := time.Now().Unix() for j:=0;j<1000000000;j++ {
obj := bytePool.Get().(*[]byte)
_ = obj
bytePool.Put(obj)
}
c := time.Now().Unix()
fmt.Println("without pool ", b - a, "s")
fmt.Println("with pool ", c - b, "s")
}
The visible GC has little impact on performance because the shared list is too long and time consuming.
Summarize:
From the above interpretation, we can see that the GET method does not guarantee the obtained object value, because the values placed in the local pool may be deleted at any time, but the caller is not notified. Values placed in the shared pool may be stolen by other goroutine. Therefore, the object pool is suitable for storing some temporary tangent state-independent data, but it is not suitable to store the database connection instance, because the value that is stored in the object pool may be deleted during garbage collection, which violates the original intention of database connection pool establishment.
According to the above, Golang's object pool is strictly a temporary object pool for storing some temporary objects that will be shared between Goroutine. The main role is to reduce GC and improve performance. The most common usage scenario in Golang is the output buffer in the FMT package.
In Golang, if you want to achieve the effect of the connection pool, you can use container/list to achieve, the open source community also has some ready-made implementations, such as Go-commons-pool, the specific reader can go to their own understanding.
Resources:
- The official Go Language Pack sync. The implementation principle and application scenario of pool
- Sync. Pool Source
In-depth Golang's sync.pool detailed