1
2
3
4
5 package base
6
7 import (
8 "fmt"
9 "os"
10 "runtime"
11 "runtime/debug"
12 "runtime/metrics"
13 )
14
15 var atExitFuncs []func()
16
17 func AtExit(f func()) {
18 atExitFuncs = append(atExitFuncs, f)
19 }
20
21 func Exit(code int) {
22 for i := len(atExitFuncs) - 1; i >= 0; i-- {
23 f := atExitFuncs[i]
24 atExitFuncs = atExitFuncs[:i]
25 f()
26 }
27 os.Exit(code)
28 }
29
30
31 const EnableTrace = false
32
33
34 func forEachGC(fn func() bool) {
35 type T [32]byte
36
37 var finalizer func(*T)
38 finalizer = func(p *T) {
39 if fn() {
40 runtime.SetFinalizer(p, finalizer)
41 }
42 }
43
44 finalizer(new(T))
45 }
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 func AdjustStartingHeap(requestedHeapGoal uint64) {
64 logHeapTweaks := Debug.GCAdjust == 1
65 mp := runtime.GOMAXPROCS(0)
66 gcConcurrency := Flag.LowerC
67
68 const (
69 goal = "/gc/heap/goal:bytes"
70 count = "/gc/cycles/total:gc-cycles"
71 allocs = "/gc/heap/allocs:bytes"
72 frees = "/gc/heap/frees:bytes"
73 )
74
75 sample := []metrics.Sample{{Name: goal}, {Name: count}, {Name: allocs}, {Name: frees}}
76 const (
77 GOAL = 0
78 COUNT = 1
79 ALLOCS = 2
80 FREES = 3
81 )
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 metrics.Read(sample)
123 for _, s := range sample {
124 if s.Value.Kind() == metrics.KindBad {
125
126 if logHeapTweaks {
127 fmt.Fprintf(os.Stderr, "GCAdjust: Regret unexpected KindBad for metric %s\n", s.Name)
128 }
129 return
130 }
131 }
132
133
134 currentGoal := sample[GOAL].Value.Uint64()
135 myGogc := 100 * requestedHeapGoal / currentGoal
136 if myGogc <= 150 {
137 return
138 }
139
140 if logHeapTweaks {
141 sample := append([]metrics.Sample(nil), sample...)
142 AtExit(func() {
143 metrics.Read(sample)
144 goal := sample[GOAL].Value.Uint64()
145 count := sample[COUNT].Value.Uint64()
146 oldGogc := debug.SetGCPercent(100)
147 if oldGogc == 100 {
148 fmt.Fprintf(os.Stderr, "GCAdjust: AtExit goal %d gogc %d count %d maxprocs %d gcConcurrency %d\n",
149 goal, oldGogc, count, mp, gcConcurrency)
150 } else {
151 inUse := sample[ALLOCS].Value.Uint64() - sample[FREES].Value.Uint64()
152 overPct := 100 * (int(inUse) - int(requestedHeapGoal)) / int(requestedHeapGoal)
153 fmt.Fprintf(os.Stderr, "GCAdjust: AtExit goal %d gogc %d count %d maxprocs %d gcConcurrency %d overPct %d\n",
154 goal, oldGogc, count, mp, gcConcurrency, overPct)
155
156 }
157 })
158 }
159
160 debug.SetGCPercent(int(myGogc))
161
162 adjustFunc := func() bool {
163
164 metrics.Read(sample)
165 goal := sample[GOAL].Value.Uint64()
166 count := sample[COUNT].Value.Uint64()
167
168 if goal <= requestedHeapGoal {
169 if logHeapTweaks {
170 fmt.Fprintf(os.Stderr, "GCAdjust: Reuse GOGC adjust, current goal %d, count is %d, current gogc %d\n",
171 goal, count, myGogc)
172 }
173 return true
174 }
175
176
177 calcLive := 100 * goal / (100 + myGogc)
178
179 if 2*calcLive < requestedHeapGoal {
180 myGogc = 100*requestedHeapGoal/calcLive - 100
181
182 if myGogc > 125 {
183
184 oldGogc := debug.SetGCPercent(int(myGogc))
185
186 if logHeapTweaks {
187
188 inUse := sample[ALLOCS].Value.Uint64() - sample[FREES].Value.Uint64()
189 metrics.Read(sample)
190 newGoal := sample[GOAL].Value.Uint64()
191 pctOff := 100 * (int64(newGoal) - int64(requestedHeapGoal)) / int64(requestedHeapGoal)
192
193 if pctOff < 2 {
194 fmt.Fprintf(os.Stderr, "GCAdjust: Retry GOGC adjust, current goal %d, count is %d, gogc was %d, is now %d, calcLive %d pctOff %d\n",
195 goal, count, oldGogc, myGogc, calcLive, pctOff)
196 } else {
197
198 fmt.Fprintf(os.Stderr, "GCAdjust: Retry GOGC adjust, current goal %d, count is %d, gogc was %d, is now %d, calcLive %d pctOff %d inUse %d\n",
199 goal, count, oldGogc, myGogc, calcLive, pctOff, inUse)
200 }
201 }
202 return true
203 }
204 }
205
206
207 oldGogc := debug.SetGCPercent(100)
208
209
210
211 inUse := sample[ALLOCS].Value.Uint64() - sample[FREES].Value.Uint64()
212 overPct := 100 * (int(inUse) - int(requestedHeapGoal)) / int(requestedHeapGoal)
213 if logHeapTweaks {
214 fmt.Fprintf(os.Stderr, "GCAdjust: Reset GOGC adjust, old goal %d, count is %d, gogc was %d, calcLive %d inUse %d overPct %d\n",
215 goal, count, oldGogc, calcLive, inUse, overPct)
216 }
217 return false
218 }
219
220 forEachGC(adjustFunc)
221 }
222
View as plain text