1 // Copyright 2013 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Pool is no-op under race detector, so all these tests do not work.
20 func TestPool(t
*testing
.T
) {
21 // disable GC so we can control when it happens.
22 defer debug
.SetGCPercent(debug
.SetGCPercent(-1))
25 t
.Fatal("expected empty")
28 // Make sure that the goroutine doesn't migrate to another P
29 // between Put and Get calls.
33 if g
:= p
.Get(); g
!= "a" {
34 t
.Fatalf("got %#v; want a", g
)
36 if g
:= p
.Get(); g
!= "b" {
37 t
.Fatalf("got %#v; want b", g
)
39 if g
:= p
.Get(); g
!= nil {
40 t
.Fatalf("got %#v; want nil", g
)
44 // Put in a large number of objects so they spill into
46 for i
:= 0; i
< 100; i
++ {
49 // After one GC, the victim cache should keep them alive.
51 if g
:= p
.Get(); g
!= "c" {
52 t
.Fatalf("got %#v; want c after GC", g
)
54 // A second GC should drop the victim cache.
56 if g
:= p
.Get(); g
!= nil {
57 t
.Fatalf("got %#v; want nil after second GC", g
)
61 func TestPoolNew(t
*testing
.T
) {
62 // disable GC so we can control when it happens.
63 defer debug
.SetGCPercent(debug
.SetGCPercent(-1))
72 if v
:= p
.Get(); v
!= 1 {
73 t
.Fatalf("got %v; want 1", v
)
75 if v
:= p
.Get(); v
!= 2 {
76 t
.Fatalf("got %v; want 2", v
)
79 // Make sure that the goroutine doesn't migrate to another P
80 // between Put and Get calls.
83 if v
:= p
.Get(); v
!= 42 {
84 t
.Fatalf("got %v; want 42", v
)
88 if v
:= p
.Get(); v
!= 3 {
89 t
.Fatalf("got %v; want 3", v
)
93 // Test that Pool does not hold pointers to previously cached resources.
94 func TestPoolGC(t
*testing
.T
) {
98 // Test that Pool releases resources on GC.
99 func TestPoolRelease(t
*testing
.T
) {
103 func testPool(t
*testing
.T
, drain
bool) {
104 t
.Skip("gccgo imprecise GC breaks this test")
108 for try
:= 0; try
< 3; try
++ {
109 if try
== 1 && testing
.Short() {
113 for i
:= 0; i
< N
; i
++ {
115 runtime
.SetFinalizer(v
, func(vv
*string) {
116 atomic
.AddUint32(&fin
, 1)
121 for i
:= 0; i
< N
; i
++ {
125 for i
:= 0; i
< 5; i
++ {
127 time
.Sleep(time
.Duration(i
*100+10) * time
.Millisecond
)
128 // 1 pointer can remain on stack or elsewhere
129 if fin1
= atomic
.LoadUint32(&fin
); fin1
>= N
-1 {
133 t
.Fatalf("only %v out of %v resources are finalized on try %v", fin1
, N
, try
)
137 func TestPoolStress(t
*testing
.T
) {
144 done
:= make(chan bool)
145 for i
:= 0; i
< P
; i
++ {
148 for j
:= 0; j
< N
; j
++ {
154 if v
!= nil && v
.(int) != 0 {
155 t
.Errorf("expect 0, got %v", v
)
162 for i
:= 0; i
< P
; i
++ {
167 func TestPoolDequeue(t
*testing
.T
) {
168 testPoolDequeue(t
, NewPoolDequeue(16))
171 func TestPoolChain(t
*testing
.T
) {
172 testPoolDequeue(t
, NewPoolChain())
175 func testPoolDequeue(t
*testing
.T
, d PoolDequeue
) {
181 have
:= make([]int32, N
)
184 record
:= func(val
int) {
185 atomic
.AddInt32(&have
[val
], 1)
187 atomic
.StoreInt32(&stop
, 1)
191 // Start P-1 consumers.
192 for i
:= 1; i
< P
; i
++ {
196 for atomic
.LoadInt32(&stop
) == 0 {
197 val
, ok
:= d
.PopTail()
202 // Speed up the test by
203 // allowing the pusher to run.
204 if fail
++; fail%100
== 0 {
217 for j
:= 0; j
< N
; j
++ {
219 // Allow a popper to run.
223 val
, ok
:= d
.PopHead()
235 for i
, count
:= range have
{
237 t
.Errorf("expected have[%d] = 1, got %d", i
, count
)
240 // Check that at least some PopHeads succeeded. We skip this
241 // check in short mode because it's common enough that the
242 // queue will stay nearly empty all the time and a PopTail
243 // will happen during the window between every PushHead and
245 if !testing
.Short() && nPopHead
== 0 {
246 t
.Errorf("popHead never succeeded")
250 func BenchmarkPool(b
*testing
.B
) {
252 b
.RunParallel(func(pb
*testing
.PB
) {
260 func BenchmarkPoolOverflow(b
*testing
.B
) {
262 b
.RunParallel(func(pb
*testing
.PB
) {
264 for b
:= 0; b
< 100; b
++ {
267 for b
:= 0; b
< 100; b
++ {
274 // Simulate object starvation in order to force Ps to steal objects
276 func BenchmarkPoolStarvation(b
*testing
.B
) {
279 // Reduce number of putted objects by 33 %. It creates objects starvation
280 // that force P-local storage to steal objects from other Ps.
281 countStarved
:= count
- int(float32(count
)*0.33)
282 b
.RunParallel(func(pb
*testing
.PB
) {
284 for b
:= 0; b
< countStarved
; b
++ {
287 for b
:= 0; b
< count
; b
++ {
296 func BenchmarkPoolSTW(b
*testing
.B
) {
297 // Take control of GC.
298 defer debug
.SetGCPercent(debug
.SetGCPercent(-1))
300 var mstats runtime
.MemStats
304 for i
:= 0; i
< b
.N
; i
++ {
305 // Put a large number of items into a pool.
308 for i
:= 0; i
< N
; i
++ {
313 // Record pause time.
314 runtime
.ReadMemStats(&mstats
)
315 pauses
= append(pauses
, mstats
.PauseNs
[(mstats
.NumGC
+255)%256
])
318 // Get pause time stats.
319 sort
.Slice(pauses
, func(i
, j
int) bool { return pauses
[i
] < pauses
[j
] })
321 for _
, ns
:= range pauses
{
324 // ns/op for this benchmark is average STW time.
325 b
.ReportMetric(float64(total
)/float64(b
.N
), "ns/op")
326 b
.ReportMetric(float64(pauses
[len(pauses
)*95/100]), "p95-ns/STW")
327 b
.ReportMetric(float64(pauses
[len(pauses
)*50/100]), "p50-ns/STW")
330 func BenchmarkPoolExpensiveNew(b
*testing
.B
) {
331 // Populate a pool with items that are expensive to construct
332 // to stress pool cleanup and subsequent reconstruction.
334 // Create a ballast so the GC has a non-zero heap size and
335 // runs at reasonable times.
336 globalSink
= make([]byte, 8<<20)
337 defer func() { globalSink
= nil }()
339 // Create a pool that's "expensive" to fill.
343 atomic
.AddUint64(&nNew
, 1)
344 time
.Sleep(time
.Millisecond
)
347 var mstats1
, mstats2 runtime
.MemStats
348 runtime
.ReadMemStats(&mstats1
)
349 b
.RunParallel(func(pb
*testing
.PB
) {
350 // Simulate 100X the number of goroutines having items
351 // checked out from the Pool simultaneously.
352 items
:= make([]any
, 100)
356 for i
:= range items
{
358 // Simulate doing some work with this
360 sink
= make([]byte, 32<<10)
362 for i
, v
:= range items
{
369 runtime
.ReadMemStats(&mstats2
)
371 b
.ReportMetric(float64(mstats2
.NumGC
-mstats1
.NumGC
)/float64(b
.N
), "GCs/op")
372 b
.ReportMetric(float64(nNew
)/float64(b
.N
), "New/op")