Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(godeltaprof): decouple pprof builder from delta computation with an interface #110

Merged
merged 6 commits into from
May 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading