About sync.Pool and what changed in go1.13

Huy Do

Software Engineer, Observability Team / LINE Corp

Today goal

2

Do you know about sync.Pool?

type Pool struct {
        New func() interface{}
}

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

func (p *Pool) Get() interface{} {}
3

"Appropriate" use

4

"Appropriate" use

5

"Appropriate" use

6

A little bit of history

7

A little bit of history

func (p *Pool) Get() interface{} 

func (p *Pool) Put(v interface{}) bool 

func (p *Pool) SetCapacity(local, global int) 

func (p *Pool) Drain() []interface{} 
8

A little bit of history

Finally

9

A little bit of history

10

A little bit of history

type Pool struct {
    list []interface{} // offset known to runtime
    mu   Mutex         // guards list
}
func runtime_registerPool(*Pool)
func (p *Pool) Put(x interface{}) {
    ...    
    p.mu.Lock()
    p.list = append(p.list, x)
    p.mu.Unlock()
}
func (p *Pool) Get() interface{} {
    ...
    p.mu.Lock()
    var x interface{}
    x = p.list[n-1]
    p.list = p.list[:n-1]
    p.mu.Unlock()
    ...    
    return x
}
11

A little bit of history

// pool.go
func init() {
   runtime_registerPoolCleanup(poolCleanup)
}

func clearpools() {
     poolcleanup()
}

func poolCleanup() {
    // assign pool to nil, size to 0
}

// mgc.go
func gcStart(trigger gcTrigger) {
   [...]
   // clearpools before we start the GC
   clearpools()
     [...]
 }
12

What's wrong

13

A little bit of history

14

A little bit of history

15

How much it improved?

16

What we learned

17

Problem (before 1.13)

18

Now come 1.13 changes!

19

1. Make Pool stealing lock free!!

20

1. Make Pool stealing lock free!!

21

1. Make Pool stealing lock free!!

22

2. Make GC less spiky

23

2. Make GC less spiky

> Currently, every Pool is cleared completely at the start of each GC.
This is a problem for heavy users of Pool because it causes an
allocation spike immediately after Pools are clear, which impacts both
throughput and latency.

24

2. Make GC less spiky

func poolCleanup() {
[...]
// 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
[...]
}
25

2. Make GC less spiky

26

Finally

27

Thank you

Huy Do

Software Engineer, Observability Team / LINE Corp

Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)