Skip to content

Commit

Permalink
btf: Optimize string table for globally increasing offsets
Browse files Browse the repository at this point in the history
After doing some observations while parsing VMLinux, it seems that the
lookup access pattern for strings is mostly globally increasing with
with some patches of seemingly unpridicable offsets.

This is a sample of the access pattern, numbers are the index into the
string table, not the offset:
```
1112
1113
1114
348
78
1115
1116
372
1117
1118
1119
```

This commit adds logic to track this globally increasing offset and
checks if the if the offset at the next expected index matches the
offset we are looking for. If it does, we can skip the binary search.
In VMLinux this fast path is taken about 50% of the time, resulting in
about a 7% speedup.

```
goos: linux
goarch: amd64
pkg: github.com/cilium/ebpf/btf
cpu: 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
                │ before.txt  │          after.txt          │
                │   sec/op    │   sec/op     vs base        │
ParseVmlinux-16   48.52m ± 1%   44.79m ± 1%  -7.69% (n=100)

                │  before.txt  │            after.txt            │
                │     B/op     │     B/op      vs base           │
ParseVmlinux-16   31.45Mi ± 0%   31.45Mi ± 0%  ~ (p=0.127 n=100)

                │ before.txt  │           after.txt            │
                │  allocs/op  │  allocs/op   vs base           │
ParseVmlinux-16   534.1k ± 0%   534.1k ± 0%  ~ (p=0.130 n=100)
```

Signed-off-by: Dylan Reimerink <dylan.reimerink@isovalent.com>
  • Loading branch information
dylandreimerink committed Nov 7, 2023
1 parent c2563e4 commit fd47833
Show file tree
Hide file tree
Showing 2 changed files with 16 additions and 2 deletions.
16 changes: 15 additions & 1 deletion btf/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
type stringTable struct {
base *stringTable
offsets []uint32
prevIdx int
strings []string
}

Expand Down Expand Up @@ -61,7 +62,7 @@ func readStringTable(r sizedReader, base *stringTable) (*stringTable, error) {
return nil, errors.New("first item in string table is non-empty")
}

return &stringTable{base, offsets, strings}, nil
return &stringTable{base, offsets, 0, strings}, nil
}

func splitNull(data []byte, atEOF bool) (advance int, token []byte, err error) {
Expand All @@ -84,15 +85,28 @@ func (st *stringTable) Lookup(offset uint32) (string, error) {
}

func (st *stringTable) lookup(offset uint32) (string, error) {
// Fast path: zero offset is the empty string, looked up frequently.
if offset == 0 && st.base == nil {
return "", nil
}

// Accesses tend to be globally increasing, so check if the next string is
// the one we want. This skips the binary search in about 50% of cases.
if st.prevIdx+1 < len(st.offsets) && st.offsets[st.prevIdx+1] == offset {
st.prevIdx++
return st.strings[st.prevIdx], nil
}

i, found := slices.BinarySearch(st.offsets, offset)
if !found {
return "", fmt.Errorf("offset %d isn't start of a string", offset)
}

// Set the new increment index, but only if its greater than the current.
if i > st.prevIdx+1 {
st.prevIdx = i
}

return st.strings[i], nil
}

Expand Down
2 changes: 1 addition & 1 deletion btf/strings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,5 @@ func newStringTable(strings ...string) *stringTable {
offset += uint32(len(str)) + 1 // account for NUL
}

return &stringTable{nil, offsets, strings}
return &stringTable{nil, offsets, 0, strings}
}

0 comments on commit fd47833

Please sign in to comment.