Skip to content

Commit

Permalink
chore(godeltaprof): decouple pprof builder from delta computation wit…
Browse files Browse the repository at this point in the history
…h an interface (#110)
  • Loading branch information
korniltsev authored May 24, 2024
1 parent ac49e40 commit 07e7716
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 118 deletions.
41 changes: 20 additions & 21 deletions godeltaprof/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type BlockProfiler struct {
mutex sync.Mutex
runtimeProfile func([]runtime.BlockProfileRecord) (int, bool)
scaleProfile pprof.MutexProfileScaler
options pprof.ProfileBuilderOptions
}

// NewMutexProfiler creates a new BlockProfiler instance for profiling mutex contention.
Expand All @@ -42,11 +43,10 @@ func NewMutexProfiler() *BlockProfiler {
return &BlockProfiler{
runtimeProfile: runtime.MutexProfile,
scaleProfile: pprof.ScalerMutexProfile,
impl: pprof.DeltaMutexProfiler{
Options: pprof.ProfileBuilderOptions{
GenericsFrames: true,
LazyMapping: true,
},
impl: pprof.DeltaMutexProfiler{},
options: pprof.ProfileBuilderOptions{
GenericsFrames: true,
LazyMapping: true,
},
}
}
Expand All @@ -55,11 +55,10 @@ func NewMutexProfilerWithOptions(options ProfileOptions) *BlockProfiler {
return &BlockProfiler{
runtimeProfile: runtime.MutexProfile,
scaleProfile: pprof.ScalerMutexProfile,
impl: pprof.DeltaMutexProfiler{
Options: pprof.ProfileBuilderOptions{
GenericsFrames: options.GenericsFrames,
LazyMapping: options.LazyMappings,
},
impl: pprof.DeltaMutexProfiler{},
options: pprof.ProfileBuilderOptions{
GenericsFrames: options.GenericsFrames,
LazyMapping: options.LazyMappings,
},
}
}
Expand All @@ -76,11 +75,10 @@ func NewBlockProfiler() *BlockProfiler {
return &BlockProfiler{
runtimeProfile: runtime.BlockProfile,
scaleProfile: pprof.ScalerBlockProfile,
impl: pprof.DeltaMutexProfiler{
Options: pprof.ProfileBuilderOptions{
GenericsFrames: true,
LazyMapping: true,
},
impl: pprof.DeltaMutexProfiler{},
options: pprof.ProfileBuilderOptions{
GenericsFrames: true,
LazyMapping: true,
},
}
}
Expand All @@ -89,11 +87,10 @@ func NewBlockProfilerWithOptions(options ProfileOptions) *BlockProfiler {
return &BlockProfiler{
runtimeProfile: runtime.BlockProfile,
scaleProfile: pprof.ScalerBlockProfile,
impl: pprof.DeltaMutexProfiler{
Options: pprof.ProfileBuilderOptions{
GenericsFrames: options.GenericsFrames,
LazyMapping: options.LazyMappings,
},
impl: pprof.DeltaMutexProfiler{},
options: pprof.ProfileBuilderOptions{
GenericsFrames: options.GenericsFrames,
LazyMapping: options.LazyMappings,
},
}
}
Expand All @@ -115,5 +112,7 @@ func (d *BlockProfiler) Profile(w io.Writer) error {

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

return d.impl.PrintCountCycleProfile(w, "contentions", "delay", d.scaleProfile, p)
stc := pprof.MutexProfileConfig()
b := pprof.NewProfileBuilder(w, &d.options, stc)
return d.impl.PrintCountCycleProfile(b, d.scaleProfile, p)
}
28 changes: 24 additions & 4 deletions godeltaprof/compat/compression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@ import (
)

func BenchmarkHeapCompression(b *testing.B) {
dh := pprof.DeltaHeapProfiler{}
opt := &pprof.ProfileBuilderOptions{
GenericsFrames: true,
LazyMapping: true,
}
dh := new(pprof.DeltaHeapProfiler)
fs := generateMemProfileRecords(512, 32, 239)
rng := rand.NewSource(239)
objSize := fs[0].AllocBytes / fs[0].AllocObjects
nMutations := int(uint(rng.Int63())) % len(fs)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dh.WriteHeapProto(io.Discard, fs, int64(runtime.MemProfileRate), "")
_ = WriteHeapProto(dh, opt, io.Discard, fs, int64(runtime.MemProfileRate))
for j := 0; j < nMutations; j++ {
idx := int(uint(rng.Int63())) % len(fs)
fs[idx].AllocObjects += 1
Expand All @@ -39,15 +43,19 @@ func BenchmarkMutexCompression(b *testing.B) {
runtime.SetMutexProfileFraction(5)
defer runtime.SetMutexProfileFraction(prevMutexProfileFraction)

dh := pprof.DeltaMutexProfiler{}
opt := &pprof.ProfileBuilderOptions{
GenericsFrames: true,
LazyMapping: true,
}
dh := new(pprof.DeltaMutexProfiler)
fs := generateBlockProfileRecords(512, 32, 239)
rng := rand.NewSource(239)
nMutations := int(uint(rng.Int63())) % len(fs)
oneBlockCycles := fs[0].Cycles / fs[0].Count
b.ResetTimer()

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

}
}

func WriteHeapProto(dp *pprof.DeltaHeapProfiler, opt *pprof.ProfileBuilderOptions, w io.Writer, p []runtime.MemProfileRecord, rate int64) error {
stc := pprof.HeapProfileConfig(rate)
b := pprof.NewProfileBuilder(w, opt, stc)
return dp.WriteHeapProto(b, p, rate)
}

func PrintCountCycleProfile(d *pprof.DeltaMutexProfiler, opt *pprof.ProfileBuilderOptions, w io.Writer, scaler pprof.MutexProfileScaler, records []runtime.BlockProfileRecord) error {
stc := pprof.MutexProfileConfig()
b := pprof.NewProfileBuilder(w, opt, stc)
return d.PrintCountCycleProfile(b, scaler, records)
}
77 changes: 73 additions & 4 deletions godeltaprof/compat/delta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package compat

import (
"bytes"
"math/rand"
"runtime"
"testing"

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

dh := pprof.DeltaHeapProfiler{}
dh := new(pprof.DeltaHeapProfiler)
opt := new(pprof.ProfileBuilderOptions)
dump := func(r ...runtime.MemProfileRecord) *bytes.Buffer {
buf := bytes.NewBuffer(nil)
err := dh.WriteHeapProto(buf, r, testMemProfileRate, "")
err := WriteHeapProto(dh, opt, buf, r, testMemProfileRate)
assert.NoError(t, err)
return buf
}
Expand Down Expand Up @@ -108,7 +110,8 @@ func TestDeltaBlockProfile(t *testing.T) {
runtime.SetMutexProfileFraction(5)
defer runtime.SetMutexProfileFraction(prevMutexProfileFraction)

dh := pprof.DeltaMutexProfiler{}
dh := new(pprof.DeltaMutexProfiler)
opt := new(pprof.ProfileBuilderOptions)

scale := func(rcount, rcycles int64) (int64, int64) {
count, nanosec := pprof.ScaleMutexProfile(scaler, rcount, float64(rcycles)/cpuGHz)
Expand All @@ -117,7 +120,7 @@ func TestDeltaBlockProfile(t *testing.T) {
}
dump := func(r ...runtime.BlockProfileRecord) *bytes.Buffer {
buf := bytes.NewBuffer(nil)
err := dh.PrintCountCycleProfile(buf, "contentions", "delay", scaler, r)
err := PrintCountCycleProfile(dh, opt, buf, scaler, r)
assert.NoError(t, err)
return buf
}
Expand Down Expand Up @@ -163,3 +166,69 @@ func TestDeltaBlockProfile(t *testing.T) {
})
}
}

func BenchmarkHeapDelta(b *testing.B) {
dh := new(pprof.DeltaHeapProfiler)
fs := generateMemProfileRecords(512, 32, 239)
rng := rand.NewSource(239)
objSize := fs[0].AllocBytes / fs[0].AllocObjects
nMutations := int(uint(rng.Int63())) % len(fs)
builder := &noopBuilder{}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = dh.WriteHeapProto(builder, fs, int64(runtime.MemProfileRate))
for j := 0; j < nMutations; j++ {
idx := int(uint(rng.Int63())) % len(fs)
fs[idx].AllocObjects += 1
fs[idx].AllocBytes += objSize
fs[idx].FreeObjects += 1
fs[idx].FreeBytes += objSize
}
}
}

func BenchmarkMutexDelta(b *testing.B) {
for i, scaler := range mutexProfileScalers {
name := "ScalerMutexProfile"
if i == 1 {
name = "ScalerBlockProfile"
}
b.Run(name, func(b *testing.B) {
prevMutexProfileFraction := runtime.SetMutexProfileFraction(-1)
runtime.SetMutexProfileFraction(5)
defer runtime.SetMutexProfileFraction(prevMutexProfileFraction)

dh := new(pprof.DeltaMutexProfiler)
fs := generateBlockProfileRecords(512, 32, 239)
rng := rand.NewSource(239)
nMutations := int(uint(rng.Int63())) % len(fs)
oneBlockCycles := fs[0].Cycles / fs[0].Count
builder := &noopBuilder{}
b.ResetTimer()

for i := 0; i < b.N; i++ {
_ = dh.PrintCountCycleProfile(builder, scaler, fs)
for j := 0; j < nMutations; j++ {
idx := int(uint(rng.Int63())) % len(fs)
fs[idx].Count += 1
fs[idx].Cycles += oneBlockCycles
}
}
})

}
}

type noopBuilder struct {
}

func (b *noopBuilder) LocsForStack(_ []uintptr) []uint64 {
return nil
}
func (b *noopBuilder) Sample(_ []int64, _ []uint64, _ int64) {

}

func (b *noopBuilder) Build() {

}
37 changes: 18 additions & 19 deletions godeltaprof/compat/reject_order_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ import (
)

func TestHeapReject(t *testing.T) {
dh := pprof.DeltaHeapProfiler{}
dh := new(pprof.DeltaHeapProfiler)
opt := new(pprof.ProfileBuilderOptions)
fs := generateMemProfileRecords(512, 32, 239)
p1 := bytes.NewBuffer(nil)
err := dh.WriteHeapProto(p1, fs, int64(runtime.MemProfileRate), "")
err := WriteHeapProto(dh, opt, p1, fs, int64(runtime.MemProfileRate))
assert.NoError(t, err)
p1Size := p1.Len()
profile, err := gprofile.Parse(p1)
Expand All @@ -27,7 +28,7 @@ func TestHeapReject(t *testing.T) {
t.Log("p1 size", p1Size)

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

func BenchmarkHeapRejectOrder(b *testing.B) {
dh := pprof.DeltaHeapProfiler{
Options: pprof.ProfileBuilderOptions{
GenericsFrames: false,
LazyMapping: true,
},
opt := &pprof.ProfileBuilderOptions{
GenericsFrames: false,
LazyMapping: true,
}
dh := &pprof.DeltaHeapProfiler{}
fs := generateMemProfileRecords(512, 32, 239)
b.ResetTimer()
for i := 0; i < b.N; i++ {
dh.WriteHeapProto(io.Discard, fs, int64(runtime.MemProfileRate), "")
WriteHeapProto(dh, opt, io.Discard, fs, int64(runtime.MemProfileRate))
}
}

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

dh := pprof.DeltaMutexProfiler{}
dh := new(pprof.DeltaMutexProfiler)
opt := new(pprof.ProfileBuilderOptions)
fs := generateBlockProfileRecords(512, 32, 239)
p1 := bytes.NewBuffer(nil)
err := dh.PrintCountCycleProfile(p1, "contentions", "delay", scaler, fs)
err := PrintCountCycleProfile(dh, opt, p1, scaler, fs)
assert.NoError(t, err)
p1Size := p1.Len()
profile, err := gprofile.Parse(p1)
Expand All @@ -83,7 +84,7 @@ func TestMutexReject(t *testing.T) {
t.Log("p1 size", p1Size)

p2 := bytes.NewBuffer(nil)
err = dh.PrintCountCycleProfile(p2, "contentions", "delay", scaler, fs)
err = PrintCountCycleProfile(dh, opt, p2, scaler, fs)
assert.NoError(t, err)
p2Size := p2.Len()
assert.Less(t, p2Size, 1000)
Expand All @@ -107,18 +108,16 @@ func BenchmarkMutexRejectOrder(b *testing.B) {
prevMutexProfileFraction := runtime.SetMutexProfileFraction(-1)
runtime.SetMutexProfileFraction(5)
defer runtime.SetMutexProfileFraction(prevMutexProfileFraction)

dh := pprof.DeltaMutexProfiler{
Options: pprof.ProfileBuilderOptions{
GenericsFrames: false,
LazyMapping: true,
},
opt := &pprof.ProfileBuilderOptions{
GenericsFrames: false,
LazyMapping: true,
}
dh := &pprof.DeltaMutexProfiler{}
fs := generateBlockProfileRecords(512, 32, 239)
b.ResetTimer()

for i := 0; i < b.N; i++ {
dh.PrintCountCycleProfile(io.Discard, "contentions", "delay", scaler, fs)
PrintCountCycleProfile(dh, opt, io.Discard, scaler, fs)
}
})

Expand Down
31 changes: 16 additions & 15 deletions godeltaprof/heap.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,28 +28,28 @@ import (
// ...
// err := hp.Profile(someWriter)
type HeapProfiler struct {
impl pprof.DeltaHeapProfiler
mutex sync.Mutex
impl pprof.DeltaHeapProfiler
mutex sync.Mutex
options pprof.ProfileBuilderOptions
}

func NewHeapProfiler() *HeapProfiler {
return &HeapProfiler{
impl: pprof.DeltaHeapProfiler{
Options: pprof.ProfileBuilderOptions{
GenericsFrames: true,
LazyMapping: true,
},
impl: pprof.DeltaHeapProfiler{},
options: pprof.ProfileBuilderOptions{
GenericsFrames: true,
LazyMapping: true,
}}
}

func NewHeapProfilerWithOptions(options ProfileOptions) *HeapProfiler {
return &HeapProfiler{
impl: pprof.DeltaHeapProfiler{
Options: pprof.ProfileBuilderOptions{
GenericsFrames: options.GenericsFrames,
LazyMapping: options.LazyMappings,
},
}}
impl: pprof.DeltaHeapProfiler{},
options: pprof.ProfileBuilderOptions{
GenericsFrames: options.GenericsFrames,
LazyMapping: options.LazyMappings,
},
}
}

func (d *HeapProfiler) Profile(w io.Writer) error {
Expand All @@ -76,6 +76,7 @@ func (d *HeapProfiler) Profile(w io.Writer) error {
}
// Profile grew; try again.
}

return d.impl.WriteHeapProto(w, p, int64(runtime.MemProfileRate), "")
rate := int64(runtime.MemProfileRate)
b := pprof.NewProfileBuilder(w, &d.options, pprof.HeapProfileConfig(rate))
return d.impl.WriteHeapProto(b, p, rate)
}
Loading

0 comments on commit 07e7716

Please sign in to comment.