Skip to content

Commit

Permalink
Load kernel BTF spec once when creating a new collection
Browse files Browse the repository at this point in the history
When creating a new collection without specifying the `KernelTypes` in
the `ProgramOptions`, the kernel BTF is implicitly loaded. For this the
`btf.LoadKernelSpec()` helper is used. Even though the spec is only
loaded once, this operation cause a lot of memory churn, as the kernel
spec is being copied each time the function get called. This becomes an
issue when a collection with lots of programs is loaded in a resource
limited environment.

This commit initializes an missing `KernelTypes` field with the BTF
kernel spec, getting rid of the subsequent calls of `BTF.Copy()`.

This greatly reduces the number of allocations/op when running the
`BenchmarkNewCollectionManyProgs` benchmark. Here are the results:

Previous:
BenchmarkNewCollectionManyProgs-4  12 98443414 ns/op 116779863 B/op 403964 allocs/op

With optimization:
BenchmarkNewCollectionManyProgs-4 184  5807742 ns/op   4134444 B/op  17325 allocs/op

Signed-off-by: Patrick Pichler <[email protected]>
  • Loading branch information
Patrick Pichler committed Feb 17, 2025
1 parent 8b4b96a commit fed5496
Showing 1 changed file with 14 additions and 6 deletions.
20 changes: 14 additions & 6 deletions collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,11 @@ func (cs *CollectionSpec) Assign(to interface{}) error {
// Returns an error if any of the fields can't be found, or
// if the same Map or Program is assigned multiple times.
func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error {
loader, err := newCollectionLoader(cs, opts)
if opts == nil {
opts = &CollectionOptions{}
}

loader, err := newCollectionLoader(cs, *opts)
if err != nil {
return err
}
Expand Down Expand Up @@ -351,7 +355,7 @@ func NewCollection(spec *CollectionSpec) (*Collection, error) {
// Omitting Collection.Close() during application shutdown is an error.
// See the package documentation for details around Map and Program lifecycle.
func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) {
loader, err := newCollectionLoader(spec, &opts)
loader, err := newCollectionLoader(spec, opts)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -405,9 +409,13 @@ type collectionLoader struct {
vars map[string]*Variable
}

func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collectionLoader, error) {
if opts == nil {
opts = &CollectionOptions{}
func newCollectionLoader(coll *CollectionSpec, opts CollectionOptions) (*collectionLoader, error) {
if opts.Programs.KernelTypes == nil {
kernelSpec, err := btf.LoadKernelSpec()
if err != nil {
return nil, fmt.Errorf("cannot load kernel spec: %w", err)
}
opts.Programs.KernelTypes = kernelSpec
}

// Check for existing MapSpecs in the CollectionSpec for all provided replacement maps.
Expand All @@ -423,7 +431,7 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec

return &collectionLoader{
coll,
opts,
&opts,
make(map[string]*Map),
make(map[string]*Program),
make(map[string]*Variable),
Expand Down

0 comments on commit fed5496

Please sign in to comment.