Skip to content

Commit a5ae62f

Browse files
proc_maps: Parse address and device without allocating (#572)
In our project we need to parse proc maps pretty frequently and we've noticed that there are lots of small allocations coming from parsing the device and addresses from procfs' maps file. The rough split of memory allocated is: - bufio.(*Scanner).Text: 25% - strings.Split: 50% - string.Fields: 25% The two callers of strings.Split are the two parsing functions that we are optimising here. I've added some benchmarks to show the improvements. Before ====== ``` $ go test -benchmem -run=^$ -bench ^BenchmarkParse.*$ github.com/prometheus/procfs goos: linux goarch: amd64 pkg: github.com/prometheus/procfs cpu: Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz BenchmarkParseAddress-12 12218004 123.0 ns/op 32 B/op 1 allocs/op BenchmarkParseDevice-12 15074881 85.11 ns/op 32 B/op 1 allocs/op PASS ok github.com/prometheus/procfs 2.978s ``` After ===== ``` $ go test -benchmem -run=^$ -bench ^BenchmarkParse.*$ github.com/prometheus/procfs goos: linux goarch: amd64 pkg: github.com/prometheus/procfs cpu: Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz BenchmarkParseAddress-12 28619314 50.45 ns/op 0 B/op 0 allocs/op BenchmarkParseDevice-12 49721935 29.66 ns/op 0 B/op 0 allocs/op PASS ok github.com/prometheus/procfs 2.991s ``` Signed-off-by: Francisco Javier Honduvilla Coto <[email protected]> Co-authored-by: Ben Kochie <[email protected]>
1 parent 5056707 commit a5ae62f

File tree

2 files changed

+47
-10
lines changed

2 files changed

+47
-10
lines changed

proc_maps.go

+10-10
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,17 @@ type ProcMap struct {
6363
// parseDevice parses the device token of a line and converts it to a dev_t
6464
// (mkdev) like structure.
6565
func parseDevice(s string) (uint64, error) {
66-
toks := strings.Split(s, ":")
67-
if len(toks) < 2 {
68-
return 0, fmt.Errorf("%w: unexpected number of fields, expected: 2, got: %q", ErrFileParse, len(toks))
66+
i := strings.Index(s, ":")
67+
if i == -1 {
68+
return 0, fmt.Errorf("%w: expected separator `:` in %s", ErrFileParse, s)
6969
}
7070

71-
major, err := strconv.ParseUint(toks[0], 16, 0)
71+
major, err := strconv.ParseUint(s[0:i], 16, 0)
7272
if err != nil {
7373
return 0, err
7474
}
7575

76-
minor, err := strconv.ParseUint(toks[1], 16, 0)
76+
minor, err := strconv.ParseUint(s[i+1:], 16, 0)
7777
if err != nil {
7878
return 0, err
7979
}
@@ -93,17 +93,17 @@ func parseAddress(s string) (uintptr, error) {
9393

9494
// parseAddresses parses the start-end address.
9595
func parseAddresses(s string) (uintptr, uintptr, error) {
96-
toks := strings.Split(s, "-")
97-
if len(toks) < 2 {
98-
return 0, 0, fmt.Errorf("%w: invalid address", ErrFileParse)
96+
idx := strings.Index(s, "-")
97+
if idx == -1 {
98+
return 0, 0, fmt.Errorf("%w: expected separator `-` in %s", ErrFileParse, s)
9999
}
100100

101-
saddr, err := parseAddress(toks[0])
101+
saddr, err := parseAddress(s[0:idx])
102102
if err != nil {
103103
return 0, 0, err
104104
}
105105

106-
eaddr, err := parseAddress(toks[1])
106+
eaddr, err := parseAddress(s[idx+1:])
107107
if err != nil {
108108
return 0, 0, err
109109
}

proc_maps64_test.go

+37
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,40 @@ func TestProcMaps(t *testing.T) {
138138
}
139139

140140
}
141+
142+
var start, end uintptr
143+
144+
func BenchmarkParseAddress(b *testing.B) {
145+
b.ReportAllocs()
146+
var (
147+
s, e uintptr
148+
err error
149+
)
150+
for i := 0; i < b.N; i++ {
151+
s, e, err = parseAddresses("7f7d7469e000-7f7d746a0000")
152+
if err != nil {
153+
b.Fatal(err)
154+
}
155+
}
156+
// Prevent the compiler from optimizing away benchmark code.
157+
start = s
158+
end = e
159+
}
160+
161+
var device uint64
162+
163+
func BenchmarkParseDevice(b *testing.B) {
164+
b.ReportAllocs()
165+
var (
166+
d uint64
167+
err error
168+
)
169+
for i := 0; i < b.N; i++ {
170+
d, err = parseDevice("00:22")
171+
if err != nil {
172+
b.Fatal(err)
173+
}
174+
}
175+
// Prevent the compiler from optimizing away benchmark code.
176+
device = d
177+
}

0 commit comments

Comments
 (0)