Skip to content

Commit 07e7716

Browse files
authored
chore(godeltaprof): decouple pprof builder from delta computation with an interface (#110)
1 parent ac49e40 commit 07e7716

File tree

9 files changed

+235
-118
lines changed

9 files changed

+235
-118
lines changed

godeltaprof/block.go

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type BlockProfiler struct {
2828
mutex sync.Mutex
2929
runtimeProfile func([]runtime.BlockProfileRecord) (int, bool)
3030
scaleProfile pprof.MutexProfileScaler
31+
options pprof.ProfileBuilderOptions
3132
}
3233

3334
// NewMutexProfiler creates a new BlockProfiler instance for profiling mutex contention.
@@ -42,11 +43,10 @@ func NewMutexProfiler() *BlockProfiler {
4243
return &BlockProfiler{
4344
runtimeProfile: runtime.MutexProfile,
4445
scaleProfile: pprof.ScalerMutexProfile,
45-
impl: pprof.DeltaMutexProfiler{
46-
Options: pprof.ProfileBuilderOptions{
47-
GenericsFrames: true,
48-
LazyMapping: true,
49-
},
46+
impl: pprof.DeltaMutexProfiler{},
47+
options: pprof.ProfileBuilderOptions{
48+
GenericsFrames: true,
49+
LazyMapping: true,
5050
},
5151
}
5252
}
@@ -55,11 +55,10 @@ func NewMutexProfilerWithOptions(options ProfileOptions) *BlockProfiler {
5555
return &BlockProfiler{
5656
runtimeProfile: runtime.MutexProfile,
5757
scaleProfile: pprof.ScalerMutexProfile,
58-
impl: pprof.DeltaMutexProfiler{
59-
Options: pprof.ProfileBuilderOptions{
60-
GenericsFrames: options.GenericsFrames,
61-
LazyMapping: options.LazyMappings,
62-
},
58+
impl: pprof.DeltaMutexProfiler{},
59+
options: pprof.ProfileBuilderOptions{
60+
GenericsFrames: options.GenericsFrames,
61+
LazyMapping: options.LazyMappings,
6362
},
6463
}
6564
}
@@ -76,11 +75,10 @@ func NewBlockProfiler() *BlockProfiler {
7675
return &BlockProfiler{
7776
runtimeProfile: runtime.BlockProfile,
7877
scaleProfile: pprof.ScalerBlockProfile,
79-
impl: pprof.DeltaMutexProfiler{
80-
Options: pprof.ProfileBuilderOptions{
81-
GenericsFrames: true,
82-
LazyMapping: true,
83-
},
78+
impl: pprof.DeltaMutexProfiler{},
79+
options: pprof.ProfileBuilderOptions{
80+
GenericsFrames: true,
81+
LazyMapping: true,
8482
},
8583
}
8684
}
@@ -89,11 +87,10 @@ func NewBlockProfilerWithOptions(options ProfileOptions) *BlockProfiler {
8987
return &BlockProfiler{
9088
runtimeProfile: runtime.BlockProfile,
9189
scaleProfile: pprof.ScalerBlockProfile,
92-
impl: pprof.DeltaMutexProfiler{
93-
Options: pprof.ProfileBuilderOptions{
94-
GenericsFrames: options.GenericsFrames,
95-
LazyMapping: options.LazyMappings,
96-
},
90+
impl: pprof.DeltaMutexProfiler{},
91+
options: pprof.ProfileBuilderOptions{
92+
GenericsFrames: options.GenericsFrames,
93+
LazyMapping: options.LazyMappings,
9794
},
9895
}
9996
}
@@ -115,5 +112,7 @@ func (d *BlockProfiler) Profile(w io.Writer) error {
115112

116113
sort.Slice(p, func(i, j int) bool { return p[i].Cycles > p[j].Cycles })
117114

118-
return d.impl.PrintCountCycleProfile(w, "contentions", "delay", d.scaleProfile, p)
115+
stc := pprof.MutexProfileConfig()
116+
b := pprof.NewProfileBuilder(w, &d.options, stc)
117+
return d.impl.PrintCountCycleProfile(b, d.scaleProfile, p)
119118
}

godeltaprof/compat/compression_test.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,18 @@ import (
1010
)
1111

1212
func BenchmarkHeapCompression(b *testing.B) {
13-
dh := pprof.DeltaHeapProfiler{}
13+
opt := &pprof.ProfileBuilderOptions{
14+
GenericsFrames: true,
15+
LazyMapping: true,
16+
}
17+
dh := new(pprof.DeltaHeapProfiler)
1418
fs := generateMemProfileRecords(512, 32, 239)
1519
rng := rand.NewSource(239)
1620
objSize := fs[0].AllocBytes / fs[0].AllocObjects
1721
nMutations := int(uint(rng.Int63())) % len(fs)
1822
b.ResetTimer()
1923
for i := 0; i < b.N; i++ {
20-
_ = dh.WriteHeapProto(io.Discard, fs, int64(runtime.MemProfileRate), "")
24+
_ = WriteHeapProto(dh, opt, io.Discard, fs, int64(runtime.MemProfileRate))
2125
for j := 0; j < nMutations; j++ {
2226
idx := int(uint(rng.Int63())) % len(fs)
2327
fs[idx].AllocObjects += 1
@@ -39,15 +43,19 @@ func BenchmarkMutexCompression(b *testing.B) {
3943
runtime.SetMutexProfileFraction(5)
4044
defer runtime.SetMutexProfileFraction(prevMutexProfileFraction)
4145

42-
dh := pprof.DeltaMutexProfiler{}
46+
opt := &pprof.ProfileBuilderOptions{
47+
GenericsFrames: true,
48+
LazyMapping: true,
49+
}
50+
dh := new(pprof.DeltaMutexProfiler)
4351
fs := generateBlockProfileRecords(512, 32, 239)
4452
rng := rand.NewSource(239)
4553
nMutations := int(uint(rng.Int63())) % len(fs)
4654
oneBlockCycles := fs[0].Cycles / fs[0].Count
4755
b.ResetTimer()
4856

4957
for i := 0; i < b.N; i++ {
50-
_ = dh.PrintCountCycleProfile(io.Discard, "contentions", "delay", scaler, fs)
58+
_ = PrintCountCycleProfile(dh, opt, io.Discard, scaler, fs)
5159
for j := 0; j < nMutations; j++ {
5260
idx := int(uint(rng.Int63())) % len(fs)
5361
fs[idx].Count += 1
@@ -58,3 +66,15 @@ func BenchmarkMutexCompression(b *testing.B) {
5866

5967
}
6068
}
69+
70+
func WriteHeapProto(dp *pprof.DeltaHeapProfiler, opt *pprof.ProfileBuilderOptions, w io.Writer, p []runtime.MemProfileRecord, rate int64) error {
71+
stc := pprof.HeapProfileConfig(rate)
72+
b := pprof.NewProfileBuilder(w, opt, stc)
73+
return dp.WriteHeapProto(b, p, rate)
74+
}
75+
76+
func PrintCountCycleProfile(d *pprof.DeltaMutexProfiler, opt *pprof.ProfileBuilderOptions, w io.Writer, scaler pprof.MutexProfileScaler, records []runtime.BlockProfileRecord) error {
77+
stc := pprof.MutexProfileConfig()
78+
b := pprof.NewProfileBuilder(w, opt, stc)
79+
return d.PrintCountCycleProfile(b, scaler, records)
80+
}

godeltaprof/compat/delta_test.go

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package compat
22

33
import (
44
"bytes"
5+
"math/rand"
56
"runtime"
67
"testing"
78

@@ -39,10 +40,11 @@ func TestDeltaHeap(t *testing.T) {
3940
const testMemProfileRate = 524288
4041
const testObjectSize = 327680
4142

42-
dh := pprof.DeltaHeapProfiler{}
43+
dh := new(pprof.DeltaHeapProfiler)
44+
opt := new(pprof.ProfileBuilderOptions)
4345
dump := func(r ...runtime.MemProfileRecord) *bytes.Buffer {
4446
buf := bytes.NewBuffer(nil)
45-
err := dh.WriteHeapProto(buf, r, testMemProfileRate, "")
47+
err := WriteHeapProto(dh, opt, buf, r, testMemProfileRate)
4648
assert.NoError(t, err)
4749
return buf
4850
}
@@ -108,7 +110,8 @@ func TestDeltaBlockProfile(t *testing.T) {
108110
runtime.SetMutexProfileFraction(5)
109111
defer runtime.SetMutexProfileFraction(prevMutexProfileFraction)
110112

111-
dh := pprof.DeltaMutexProfiler{}
113+
dh := new(pprof.DeltaMutexProfiler)
114+
opt := new(pprof.ProfileBuilderOptions)
112115

113116
scale := func(rcount, rcycles int64) (int64, int64) {
114117
count, nanosec := pprof.ScaleMutexProfile(scaler, rcount, float64(rcycles)/cpuGHz)
@@ -117,7 +120,7 @@ func TestDeltaBlockProfile(t *testing.T) {
117120
}
118121
dump := func(r ...runtime.BlockProfileRecord) *bytes.Buffer {
119122
buf := bytes.NewBuffer(nil)
120-
err := dh.PrintCountCycleProfile(buf, "contentions", "delay", scaler, r)
123+
err := PrintCountCycleProfile(dh, opt, buf, scaler, r)
121124
assert.NoError(t, err)
122125
return buf
123126
}
@@ -163,3 +166,69 @@ func TestDeltaBlockProfile(t *testing.T) {
163166
})
164167
}
165168
}
169+
170+
func BenchmarkHeapDelta(b *testing.B) {
171+
dh := new(pprof.DeltaHeapProfiler)
172+
fs := generateMemProfileRecords(512, 32, 239)
173+
rng := rand.NewSource(239)
174+
objSize := fs[0].AllocBytes / fs[0].AllocObjects
175+
nMutations := int(uint(rng.Int63())) % len(fs)
176+
builder := &noopBuilder{}
177+
b.ResetTimer()
178+
for i := 0; i < b.N; i++ {
179+
_ = dh.WriteHeapProto(builder, fs, int64(runtime.MemProfileRate))
180+
for j := 0; j < nMutations; j++ {
181+
idx := int(uint(rng.Int63())) % len(fs)
182+
fs[idx].AllocObjects += 1
183+
fs[idx].AllocBytes += objSize
184+
fs[idx].FreeObjects += 1
185+
fs[idx].FreeBytes += objSize
186+
}
187+
}
188+
}
189+
190+
func BenchmarkMutexDelta(b *testing.B) {
191+
for i, scaler := range mutexProfileScalers {
192+
name := "ScalerMutexProfile"
193+
if i == 1 {
194+
name = "ScalerBlockProfile"
195+
}
196+
b.Run(name, func(b *testing.B) {
197+
prevMutexProfileFraction := runtime.SetMutexProfileFraction(-1)
198+
runtime.SetMutexProfileFraction(5)
199+
defer runtime.SetMutexProfileFraction(prevMutexProfileFraction)
200+
201+
dh := new(pprof.DeltaMutexProfiler)
202+
fs := generateBlockProfileRecords(512, 32, 239)
203+
rng := rand.NewSource(239)
204+
nMutations := int(uint(rng.Int63())) % len(fs)
205+
oneBlockCycles := fs[0].Cycles / fs[0].Count
206+
builder := &noopBuilder{}
207+
b.ResetTimer()
208+
209+
for i := 0; i < b.N; i++ {
210+
_ = dh.PrintCountCycleProfile(builder, scaler, fs)
211+
for j := 0; j < nMutations; j++ {
212+
idx := int(uint(rng.Int63())) % len(fs)
213+
fs[idx].Count += 1
214+
fs[idx].Cycles += oneBlockCycles
215+
}
216+
}
217+
})
218+
219+
}
220+
}
221+
222+
type noopBuilder struct {
223+
}
224+
225+
func (b *noopBuilder) LocsForStack(_ []uintptr) []uint64 {
226+
return nil
227+
}
228+
func (b *noopBuilder) Sample(_ []int64, _ []uint64, _ int64) {
229+
230+
}
231+
232+
func (b *noopBuilder) Build() {
233+
234+
}

godeltaprof/compat/reject_order_test.go

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ import (
1313
)
1414

1515
func TestHeapReject(t *testing.T) {
16-
dh := pprof.DeltaHeapProfiler{}
16+
dh := new(pprof.DeltaHeapProfiler)
17+
opt := new(pprof.ProfileBuilderOptions)
1718
fs := generateMemProfileRecords(512, 32, 239)
1819
p1 := bytes.NewBuffer(nil)
19-
err := dh.WriteHeapProto(p1, fs, int64(runtime.MemProfileRate), "")
20+
err := WriteHeapProto(dh, opt, p1, fs, int64(runtime.MemProfileRate))
2021
assert.NoError(t, err)
2122
p1Size := p1.Len()
2223
profile, err := gprofile.Parse(p1)
@@ -27,7 +28,7 @@ func TestHeapReject(t *testing.T) {
2728
t.Log("p1 size", p1Size)
2829

2930
p2 := bytes.NewBuffer(nil)
30-
err = dh.WriteHeapProto(p2, fs, int64(runtime.MemProfileRate), "")
31+
err = WriteHeapProto(dh, opt, p2, fs, int64(runtime.MemProfileRate))
3132
assert.NoError(t, err)
3233
p2Size := p2.Len()
3334
assert.Less(t, p2Size, 1000)
@@ -40,16 +41,15 @@ func TestHeapReject(t *testing.T) {
4041
}
4142

4243
func BenchmarkHeapRejectOrder(b *testing.B) {
43-
dh := pprof.DeltaHeapProfiler{
44-
Options: pprof.ProfileBuilderOptions{
45-
GenericsFrames: false,
46-
LazyMapping: true,
47-
},
44+
opt := &pprof.ProfileBuilderOptions{
45+
GenericsFrames: false,
46+
LazyMapping: true,
4847
}
48+
dh := &pprof.DeltaHeapProfiler{}
4949
fs := generateMemProfileRecords(512, 32, 239)
5050
b.ResetTimer()
5151
for i := 0; i < b.N; i++ {
52-
dh.WriteHeapProto(io.Discard, fs, int64(runtime.MemProfileRate), "")
52+
WriteHeapProto(dh, opt, io.Discard, fs, int64(runtime.MemProfileRate))
5353
}
5454
}
5555

@@ -69,10 +69,11 @@ func TestMutexReject(t *testing.T) {
6969
runtime.SetMutexProfileFraction(5)
7070
defer runtime.SetMutexProfileFraction(prevMutexProfileFraction)
7171

72-
dh := pprof.DeltaMutexProfiler{}
72+
dh := new(pprof.DeltaMutexProfiler)
73+
opt := new(pprof.ProfileBuilderOptions)
7374
fs := generateBlockProfileRecords(512, 32, 239)
7475
p1 := bytes.NewBuffer(nil)
75-
err := dh.PrintCountCycleProfile(p1, "contentions", "delay", scaler, fs)
76+
err := PrintCountCycleProfile(dh, opt, p1, scaler, fs)
7677
assert.NoError(t, err)
7778
p1Size := p1.Len()
7879
profile, err := gprofile.Parse(p1)
@@ -83,7 +84,7 @@ func TestMutexReject(t *testing.T) {
8384
t.Log("p1 size", p1Size)
8485

8586
p2 := bytes.NewBuffer(nil)
86-
err = dh.PrintCountCycleProfile(p2, "contentions", "delay", scaler, fs)
87+
err = PrintCountCycleProfile(dh, opt, p2, scaler, fs)
8788
assert.NoError(t, err)
8889
p2Size := p2.Len()
8990
assert.Less(t, p2Size, 1000)
@@ -107,18 +108,16 @@ func BenchmarkMutexRejectOrder(b *testing.B) {
107108
prevMutexProfileFraction := runtime.SetMutexProfileFraction(-1)
108109
runtime.SetMutexProfileFraction(5)
109110
defer runtime.SetMutexProfileFraction(prevMutexProfileFraction)
110-
111-
dh := pprof.DeltaMutexProfiler{
112-
Options: pprof.ProfileBuilderOptions{
113-
GenericsFrames: false,
114-
LazyMapping: true,
115-
},
111+
opt := &pprof.ProfileBuilderOptions{
112+
GenericsFrames: false,
113+
LazyMapping: true,
116114
}
115+
dh := &pprof.DeltaMutexProfiler{}
117116
fs := generateBlockProfileRecords(512, 32, 239)
118117
b.ResetTimer()
119118

120119
for i := 0; i < b.N; i++ {
121-
dh.PrintCountCycleProfile(io.Discard, "contentions", "delay", scaler, fs)
120+
PrintCountCycleProfile(dh, opt, io.Discard, scaler, fs)
122121
}
123122
})
124123

godeltaprof/heap.go

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,28 +28,28 @@ import (
2828
// ...
2929
// err := hp.Profile(someWriter)
3030
type HeapProfiler struct {
31-
impl pprof.DeltaHeapProfiler
32-
mutex sync.Mutex
31+
impl pprof.DeltaHeapProfiler
32+
mutex sync.Mutex
33+
options pprof.ProfileBuilderOptions
3334
}
3435

3536
func NewHeapProfiler() *HeapProfiler {
3637
return &HeapProfiler{
37-
impl: pprof.DeltaHeapProfiler{
38-
Options: pprof.ProfileBuilderOptions{
39-
GenericsFrames: true,
40-
LazyMapping: true,
41-
},
38+
impl: pprof.DeltaHeapProfiler{},
39+
options: pprof.ProfileBuilderOptions{
40+
GenericsFrames: true,
41+
LazyMapping: true,
4242
}}
4343
}
4444

4545
func NewHeapProfilerWithOptions(options ProfileOptions) *HeapProfiler {
4646
return &HeapProfiler{
47-
impl: pprof.DeltaHeapProfiler{
48-
Options: pprof.ProfileBuilderOptions{
49-
GenericsFrames: options.GenericsFrames,
50-
LazyMapping: options.LazyMappings,
51-
},
52-
}}
47+
impl: pprof.DeltaHeapProfiler{},
48+
options: pprof.ProfileBuilderOptions{
49+
GenericsFrames: options.GenericsFrames,
50+
LazyMapping: options.LazyMappings,
51+
},
52+
}
5353
}
5454

5555
func (d *HeapProfiler) Profile(w io.Writer) error {
@@ -76,6 +76,7 @@ func (d *HeapProfiler) Profile(w io.Writer) error {
7676
}
7777
// Profile grew; try again.
7878
}
79-
80-
return d.impl.WriteHeapProto(w, p, int64(runtime.MemProfileRate), "")
79+
rate := int64(runtime.MemProfileRate)
80+
b := pprof.NewProfileBuilder(w, &d.options, pprof.HeapProfileConfig(rate))
81+
return d.impl.WriteHeapProto(b, p, rate)
8182
}

0 commit comments

Comments
 (0)