Skip to content

Commit fcbe18a

Browse files
authored
perf: add gzip writer reuse (#163)
1 parent 8003d94 commit fcbe18a

File tree

11 files changed

+109
-45
lines changed

11 files changed

+109
-45
lines changed

godeltaprof/block.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type BlockProfiler struct {
3030
runtimeProfile func([]runtime.BlockProfileRecord) (int, bool)
3131
scaleProfile pprof.MutexProfileScaler
3232
options pprof.ProfileBuilderOptions
33+
gz gz
3334
}
3435

3536
// NewMutexProfiler creates a new BlockProfiler instance for profiling mutex contention.
@@ -115,8 +116,9 @@ func (d *BlockProfiler) Profile(w io.Writer) error {
115116

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

119+
zw := d.gz.get(w)
118120
stc := pprof.MutexProfileConfig()
119-
b := pprof.NewProfileBuilder(w, &d.options, stc)
121+
b := pprof.NewProfileBuilder(w, zw, &d.options, stc)
120122

121123
return d.impl.PrintCountCycleProfile(b, d.scaleProfile, p)
122124
}

godeltaprof/compat/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.18
55
require (
66
github.com/google/pprof v0.0.0-20231127191134-f3a68a39ae15
77
github.com/grafana/pyroscope-go/godeltaprof v0.1.5
8+
github.com/klauspost/compress v1.17.8
89
github.com/stretchr/testify v1.10.0
910
golang.org/x/tools v0.16.0
1011
)

godeltaprof/compat/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ github.com/google/pprof v0.0.0-20231127191134-f3a68a39ae15 h1:t2sLhFuGXwoomaKLTu
55
github.com/google/pprof v0.0.0-20231127191134-f3a68a39ae15/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
66
github.com/grafana/pyroscope-go/godeltaprof v0.1.5 h1:gkFVqihFRL1Nro2FCC0u6mW47jclef96Zu8I/ykq+4E=
77
github.com/grafana/pyroscope-go/godeltaprof v0.1.5/go.mod h1:1HSPtjU8vLG0jE9JrTdzjgFqdJ/VgN7fvxBNq3luJko=
8+
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
9+
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
810
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
911
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
1012
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=

godeltaprof/compat/testdata.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"reflect"
88
"runtime"
99

10+
"github.com/klauspost/compress/gzip"
1011
"github.com/stretchr/testify/assert"
1112

1213
"github.com/grafana/pyroscope-go/godeltaprof/internal/pprof"
@@ -320,15 +321,17 @@ func (h *heapTestHelper) mutate(nmutations int, fs []runtime.MemProfileRecord) {
320321
func WriteHeapProto(dp *pprof.DeltaHeapProfiler, opt *pprof.ProfileBuilderOptions, w io.Writer,
321322
p []runtime.MemProfileRecord, rate int64) error {
322323
stc := pprof.HeapProfileConfig(rate)
323-
b := pprof.NewProfileBuilder(w, opt, stc)
324+
zw, _ := gzip.NewWriterLevel(w, gzip.BestSpeed)
325+
b := pprof.NewProfileBuilder(w, zw, opt, stc)
324326

325327
return dp.WriteHeapProto(b, p, rate)
326328
}
327329

328330
func PrintCountCycleProfile(d *pprof.DeltaMutexProfiler, opt *pprof.ProfileBuilderOptions, w io.Writer,
329331
scaler pprof.MutexProfileScaler, records []runtime.BlockProfileRecord) error {
330332
stc := pprof.MutexProfileConfig()
331-
b := pprof.NewProfileBuilder(w, opt, stc)
333+
zw, _ := gzip.NewWriterLevel(w, gzip.BestSpeed)
334+
b := pprof.NewProfileBuilder(w, zw, opt, stc)
332335

333336
return d.PrintCountCycleProfile(b, scaler, records)
334337
}

godeltaprof/gzip.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package godeltaprof
2+
3+
import (
4+
"io"
5+
6+
"github.com/klauspost/compress/gzip"
7+
)
8+
9+
type gz struct {
10+
w *gzip.Writer
11+
}
12+
13+
func (g *gz) get(w io.Writer) *gzip.Writer {
14+
if g.w == nil {
15+
zw, _ := gzip.NewWriterLevel(w, gzip.BestSpeed)
16+
g.w = zw
17+
}
18+
g.w.Reset(w)
19+
20+
return g.w
21+
}

godeltaprof/gzip_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package godeltaprof
2+
3+
import (
4+
"bytes"
5+
"compress/gzip"
6+
"io"
7+
"testing"
8+
)
9+
10+
func TestGz(t *testing.T) {
11+
blobs := [][]byte{
12+
[]byte("Hello, World! This is the first test blob with some data to compress."),
13+
[]byte("This is a second blob with different content for compression testing."),
14+
}
15+
16+
var g gz
17+
var bufs []bytes.Buffer
18+
19+
for i, blob := range blobs {
20+
var buf bytes.Buffer
21+
gzw := g.get(&buf)
22+
if _, err := gzw.Write(blob); err != nil {
23+
t.Fatalf("Failed to write blob %d: %v", i, err)
24+
}
25+
if err := gzw.Close(); err != nil {
26+
t.Fatalf("Failed to close gzip writer %d: %v", i, err)
27+
}
28+
bufs = append(bufs, buf)
29+
}
30+
31+
for i, blob := range blobs {
32+
gzr, err := gzip.NewReader(&bufs[i])
33+
if err != nil {
34+
t.Fatalf("Failed to create gzip reader for blob %d: %v", i, err)
35+
}
36+
37+
decompressed, err := io.ReadAll(gzr)
38+
if err != nil {
39+
gzr.Close()
40+
t.Fatalf("Failed to decompress blob %d: %v", i, err)
41+
}
42+
gzr.Close()
43+
44+
if !bytes.Equal(blob, decompressed) {
45+
t.Errorf("Blob %d mismatch:\nOriginal: %q\nDecompressed: %q", i, blob, decompressed)
46+
}
47+
48+
if bytes.Equal(blob, bufs[i].Bytes()) {
49+
t.Errorf("Buffer %d should contain compressed data, not original", i)
50+
}
51+
}
52+
}

godeltaprof/heap.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type HeapProfiler struct {
3131
impl pprof.DeltaHeapProfiler
3232
mutex sync.Mutex
3333
options pprof.ProfileBuilderOptions
34+
gz gz
3435
}
3536

3637
func NewHeapProfiler() *HeapProfiler {
@@ -79,7 +80,9 @@ func (d *HeapProfiler) Profile(w io.Writer) error {
7980
// Profile grew; try again.
8081
}
8182
rate := int64(runtime.MemProfileRate)
82-
b := pprof.NewProfileBuilder(w, &d.options, pprof.HeapProfileConfig(rate))
83+
84+
zw := d.gz.get(w)
85+
b := pprof.NewProfileBuilder(w, zw, &d.options, pprof.HeapProfileConfig(rate))
8386

8487
return d.impl.WriteHeapProto(b, p, rate)
8588
}

godeltaprof/heap_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package godeltaprof
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
)
7+
8+
func BenchmarkHeap(b *testing.B) {
9+
p := NewHeapProfiler()
10+
buf := bytes.NewBuffer(nil)
11+
for i := 0; i < b.N; i++ {
12+
err := p.Profile(buf)
13+
if err != nil {
14+
b.Fatal(err)
15+
}
16+
}
17+
}

godeltaprof/internal/pprof/gzip_go16.go

Lines changed: 0 additions & 18 deletions
This file was deleted.

godeltaprof/internal/pprof/gzip_go17.go

Lines changed: 0 additions & 20 deletions
This file was deleted.

0 commit comments

Comments
 (0)