1// Copyright 2014 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.
4
5package context_test
6
7import (
8	. "context"
9	"fmt"
10	"runtime"
11	"sync"
12	"testing"
13	"time"
14)
15
16func BenchmarkCommonParentCancel(b *testing.B) {
17	root := WithValue(Background(), "key", "value")
18	shared, sharedcancel := WithCancel(root)
19	defer sharedcancel()
20
21	b.ResetTimer()
22	b.RunParallel(func(pb *testing.PB) {
23		x := 0
24		for pb.Next() {
25			ctx, cancel := WithCancel(shared)
26			if ctx.Value("key").(string) != "value" {
27				b.Fatal("should not be reached")
28			}
29			for i := 0; i < 100; i++ {
30				x /= x + 1
31			}
32			cancel()
33			for i := 0; i < 100; i++ {
34				x /= x + 1
35			}
36		}
37	})
38}
39
40func BenchmarkWithTimeout(b *testing.B) {
41	for concurrency := 40; concurrency <= 4e5; concurrency *= 100 {
42		name := fmt.Sprintf("concurrency=%d", concurrency)
43		b.Run(name, func(b *testing.B) {
44			benchmarkWithTimeout(b, concurrency)
45		})
46	}
47}
48
49func benchmarkWithTimeout(b *testing.B, concurrentContexts int) {
50	gomaxprocs := runtime.GOMAXPROCS(0)
51	perPContexts := concurrentContexts / gomaxprocs
52	root := Background()
53
54	// Generate concurrent contexts.
55	var wg sync.WaitGroup
56	ccf := make([][]CancelFunc, gomaxprocs)
57	for i := range ccf {
58		wg.Add(1)
59		go func(i int) {
60			defer wg.Done()
61			cf := make([]CancelFunc, perPContexts)
62			for j := range cf {
63				_, cf[j] = WithTimeout(root, time.Hour)
64			}
65			ccf[i] = cf
66		}(i)
67	}
68	wg.Wait()
69
70	b.ResetTimer()
71	b.RunParallel(func(pb *testing.PB) {
72		wcf := make([]CancelFunc, 10)
73		for pb.Next() {
74			for i := range wcf {
75				_, wcf[i] = WithTimeout(root, time.Hour)
76			}
77			for _, f := range wcf {
78				f()
79			}
80		}
81	})
82	b.StopTimer()
83
84	for _, cf := range ccf {
85		for _, f := range cf {
86			f()
87		}
88	}
89}
90
91func BenchmarkCancelTree(b *testing.B) {
92	depths := []int{1, 10, 100, 1000}
93	for _, d := range depths {
94		b.Run(fmt.Sprintf("depth=%d", d), func(b *testing.B) {
95			b.Run("Root=Background", func(b *testing.B) {
96				for i := 0; i < b.N; i++ {
97					buildContextTree(Background(), d)
98				}
99			})
100			b.Run("Root=OpenCanceler", func(b *testing.B) {
101				for i := 0; i < b.N; i++ {
102					ctx, cancel := WithCancel(Background())
103					buildContextTree(ctx, d)
104					cancel()
105				}
106			})
107			b.Run("Root=ClosedCanceler", func(b *testing.B) {
108				for i := 0; i < b.N; i++ {
109					ctx, cancel := WithCancel(Background())
110					cancel()
111					buildContextTree(ctx, d)
112				}
113			})
114		})
115	}
116}
117
118func buildContextTree(root Context, depth int) {
119	for d := 0; d < depth; d++ {
120		root, _ = WithCancel(root)
121	}
122}
123
124func BenchmarkCheckCanceled(b *testing.B) {
125	ctx, cancel := WithCancel(Background())
126	cancel()
127	b.Run("Err", func(b *testing.B) {
128		for i := 0; i < b.N; i++ {
129			ctx.Err()
130		}
131	})
132	b.Run("Done", func(b *testing.B) {
133		for i := 0; i < b.N; i++ {
134			select {
135			case <-ctx.Done():
136			default:
137			}
138		}
139	})
140}
141
142func BenchmarkContextCancelDone(b *testing.B) {
143	ctx, cancel := WithCancel(Background())
144	defer cancel()
145
146	b.RunParallel(func(pb *testing.PB) {
147		for pb.Next() {
148			select {
149			case <-ctx.Done():
150			default:
151			}
152		}
153	})
154}
155
156func BenchmarkDeepValueNewGoRoutine(b *testing.B) {
157	for _, depth := range []int{10, 20, 30, 50, 100} {
158		ctx := Background()
159		for i := 0; i < depth; i++ {
160			ctx = WithValue(ctx, i, i)
161		}
162
163		b.Run(fmt.Sprintf("depth=%d", depth), func(b *testing.B) {
164			for i := 0; i < b.N; i++ {
165				var wg sync.WaitGroup
166				wg.Add(1)
167				go func() {
168					defer wg.Done()
169					ctx.Value(-1)
170				}()
171				wg.Wait()
172			}
173		})
174	}
175}
176
177func BenchmarkDeepValueSameGoRoutine(b *testing.B) {
178	for _, depth := range []int{10, 20, 30, 50, 100} {
179		ctx := Background()
180		for i := 0; i < depth; i++ {
181			ctx = WithValue(ctx, i, i)
182		}
183
184		b.Run(fmt.Sprintf("depth=%d", depth), func(b *testing.B) {
185			for i := 0; i < b.N; i++ {
186				ctx.Value(-1)
187			}
188		})
189	}
190}
191