Skip to content

Commit b209828

Browse files
committed
Use string([]byte) as a cache key in decoder
Go has a special case for `string([]byte)` that doesn't allocate: * golang/go@f5f5a8b6209f8 Before and after: ``` BenchmarkCache/cached-10 132218238 9.292 ns/op 0 B/op 0 allocs/op BenchmarkCache/cached-10 212642139 5.694 ns/op 0 B/op 0 allocs/op ```
1 parent 5ed2045 commit b209828

File tree

1 file changed

+7
-18
lines changed

1 file changed

+7
-18
lines changed

decoder/decoder.go

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package decoder
22

33
import (
4-
"bytes"
54
"errors"
65
"fmt"
7-
"hash/maphash"
86
"sync"
97

108
"github.com/cloudflare/ebpf_exporter/v2/config"
@@ -14,11 +12,6 @@ import (
1412
// ErrSkipLabelSet instructs exporter to skip label set
1513
var ErrSkipLabelSet = errors.New("this label set should be skipped")
1614

17-
type cacheValue struct {
18-
in []byte
19-
out []string
20-
}
21-
2215
// Decoder transforms byte field value into a byte value representing string
2316
// to either use as an input for another Decoder or to use as the final
2417
// label value for Prometheus metrics
@@ -30,8 +23,7 @@ type Decoder interface {
3023
type Set struct {
3124
mu sync.Mutex
3225
decoders map[string]Decoder
33-
cache map[uint64]cacheValue
34-
seed maphash.Seed
26+
cache map[string][]string
3527
}
3628

3729
// NewSet creates a Set with all known decoders
@@ -65,8 +57,7 @@ func NewSet() (*Set, error) {
6557
"syscall": &Syscall{},
6658
"uint": &UInt{},
6759
},
68-
cache: map[uint64]cacheValue{},
69-
seed: maphash.MakeSeed(),
60+
cache: map[string][]string{},
7061
}, nil
7162
}
7263

@@ -98,20 +89,18 @@ func (s *Set) Decode(in []byte, label config.Label) ([]byte, error) {
9889
// DecodeLabels transforms eBPF map key bytes into a list of label values
9990
// according to configuration
10091
func (s *Set) DecodeLabels(in []byte, labels []config.Label) ([]string, error) {
101-
cacheKey := maphash.Bytes(s.seed, in)
102-
if cached, ok := s.cache[cacheKey]; ok {
103-
// Make sure that there is no collision here
104-
if bytes.Equal(cached.in, in) {
105-
return cached.out, nil
106-
}
92+
// string(in) must not be a variable to avoid allocation:
93+
// * https://github.com/golang/go/commit/f5f5a8b6209f8
94+
if cached, ok := s.cache[string(in)]; ok {
95+
return cached, nil
10796
}
10897

10998
values, err := s.decodeLabels(in, labels)
11099
if err != nil {
111100
return nil, err
112101
}
113102

114-
s.cache[cacheKey] = cacheValue{in: in, out: values}
103+
s.cache[string(in)] = values
115104

116105
return values, nil
117106
}

0 commit comments

Comments
 (0)