Skip to content

Commit

Permalink
btf: Optimizing BTF parsing by merging readTypes and inflateRawTypes
Browse files Browse the repository at this point in the history
After profiling the BTF parsing code, it became apparent that a lot of
time was spent in `readTypes` was spent allocating rawTypes. These
rawTypes are only used as an intermediate step to create the final
inflated types so all of the allocation work gets thrown away.

This commit merges `readTypes` and `inflateRawTypes` into a single
function. This allows us to re-use the intermediate objects and only
allocate the final inflated types.

This results in the following performance improvements:

```
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   49.05m ± 1%   46.08m ± 1%  -6.06% (n=100)

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

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

Signed-off-by: Dylan Reimerink <[email protected]>
  • Loading branch information
dylandreimerink authored and lmb committed Nov 9, 2023
1 parent 36f6316 commit 7146ce3
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 135 deletions.
15 changes: 5 additions & 10 deletions btf/btf.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,7 @@ func loadRawSpec(btf io.ReaderAt, bo binary.ByteOrder, base *Spec) (*Spec, error
}
}

rawTypes, rawStrings, err := parseBTF(btf, bo, baseStrings)
if err != nil {
return nil, err
}

types, err := inflateRawTypes(rawTypes, rawStrings, base)
types, rawStrings, err := parseBTF(btf, bo, baseStrings, base)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -373,7 +368,7 @@ func guessRawBTFByteOrder(r io.ReaderAt) binary.ByteOrder {

// parseBTF reads a .BTF section into memory and parses it into a list of
// raw types and a string table.
func parseBTF(btf io.ReaderAt, bo binary.ByteOrder, baseStrings *stringTable) ([]rawType, *stringTable, error) {
func parseBTF(btf io.ReaderAt, bo binary.ByteOrder, baseStrings *stringTable, base *Spec) ([]Type, *stringTable, error) {
buf := internal.NewBufferedSectionReader(btf, 0, math.MaxInt64)
header, err := parseBTFHeader(buf, bo)
if err != nil {
Expand All @@ -387,12 +382,12 @@ func parseBTF(btf io.ReaderAt, bo binary.ByteOrder, baseStrings *stringTable) ([
}

buf.Reset(io.NewSectionReader(btf, header.typeStart(), int64(header.TypeLen)))
rawTypes, err := readTypes(buf, bo, header.TypeLen)
types, err := readAndInflateTypes(buf, bo, header.TypeLen, rawStrings, base)
if err != nil {
return nil, nil, fmt.Errorf("can't read types: %w", err)
return nil, nil, err
}

return rawTypes, rawStrings, nil
return types, rawStrings, nil
}

type symbol struct {
Expand Down
64 changes: 0 additions & 64 deletions btf/btf_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,67 +365,3 @@ type btfParam struct {
type btfDeclTag struct {
ComponentIdx uint32
}

func readTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32) ([]rawType, error) {
var header btfType
// because of the interleaving between types and struct members it is difficult to
// precompute the numbers of raw types this will parse
// this "guess" is a good first estimation
sizeOfbtfType := uintptr(btfTypeLen)
tyMaxCount := uintptr(typeLen) / sizeOfbtfType / 2
types := make([]rawType, 0, tyMaxCount)

for id := TypeID(1); ; id++ {
if err := binary.Read(r, bo, &header); err == io.EOF {
return types, nil
} else if err != nil {
return nil, fmt.Errorf("can't read type info for id %v: %v", id, err)
}

var data interface{}
switch header.Kind() {
case kindInt:
data = new(btfInt)
case kindPointer:
case kindArray:
data = new(btfArray)
case kindStruct:
fallthrough
case kindUnion:
data = make([]btfMember, header.Vlen())
case kindEnum:
data = make([]btfEnum, header.Vlen())
case kindForward:
case kindTypedef:
case kindVolatile:
case kindConst:
case kindRestrict:
case kindFunc:
case kindFuncProto:
data = make([]btfParam, header.Vlen())
case kindVar:
data = new(btfVariable)
case kindDatasec:
data = make([]btfVarSecinfo, header.Vlen())
case kindFloat:
case kindDeclTag:
data = new(btfDeclTag)
case kindTypeTag:
case kindEnum64:
data = make([]btfEnum64, header.Vlen())
default:
return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind())
}

if data == nil {
types = append(types, rawType{header, nil})
continue
}

if err := binary.Read(r, bo, data); err != nil {
return nil, fmt.Errorf("type id %d: kind %v: can't read %T: %v", id, header.Kind(), data, err)
}

types = append(types, rawType{header, data})
}
}
Loading

0 comments on commit 7146ce3

Please sign in to comment.