[Performance] Improve build performance by narrowing annotation scanning to specific packages

This post can be seen as a continuation of Improve build speed in bndtools by using -compression: STORE (maybe I consider combining them in the future in a Performance tuning post)

TL;DR

The following describes how you can use 4 instructions to reduce bnd build time by telling bnd plugins which classes contain important annotations and which do not. If you know which packages are relevant for this annotations processing you can instruct bnd to do less work and build faster. Faster in our case meant 1.9 seconds vs. 4 seconds for a single bundle.

Annotation related bnd instructions

bnd has a couple of plugins which do various things during the build like scanning various kinds of OSGi related annotations.

and also some instructions to control scanning of those.

The default of each is * (star - as in ALL / everything)
This default works great and most people never even think about changing them. But since I am notoriously trying to squeeze every second of our build time (especially in Eclipse during development where each additional second of build time is annoying when you save a file) , I stumbled over those annotations, during a debugging session in bnd where I placed various duration measurements in the Analyzer and Builder code. I found that some plugins scan more than necessary.

Bundle Classpath (BCP) is scanned too - not just your own code!

I found out * means that bnd scans every .class - every means also classes inside .jar files on the BCP (Bundle Class Path).

The BCP is something we use in couple of places because e.g. we use Spring and Hibernate, and since Spring and Hibernate do not play well with OSGi, both dependencies need to be inside the same bundle as the Entity classes to have the same classloader.

So we are doing a:

-includeresource: ${repo;org.hibernate.orm:hibernate-core;latest}; lib:=true

Which embeds the hibernate-core-6.6.23.Final.jar inside our bundle and puts in in the MANIFEST.MF’s Bundle-ClassPath

Hibernate was the main culprit with a big fat hibernate-core-6.6.23.Final.jar of 11,51MB containing hundreds if not thousands of classes which added around 2 seconds to the build time of this bundle.

Solution

We have put the following in our cnf/build.bnd (you can also put it in a bnd.bnd of a single bundle)

## Speed up the build,
## because bnd needs to scan less classes
## by only scanning our packages or not scanning at all (empty).
## This avoids e.g. scanning .jars via -includeresource lib:=true
## which are on the BCP (bundle class path)
-bundleannotations: *com.our.packages*
-dsannotations: *com.our.packages*
-metatypeannotations: 
-cdiannotations: 

What it does: It tells bnd to

  • only scan our own code packages for -bundleannotations and -dsannotations e.g. com.our.packages.foo , test.com.our.packages.bar etc. All other packages are not scanned.
  • do not scan anything (basically skip the plugin) for -metatypeannotations and -cdiannotations because we do not use it.

Warning - know what you are doing!

By doing this you trade the comfort of bnd’s “it just works” defaults for performance.

This has worked for us, because:

  • we do not have annotations scanned by -metatypeannotations and -cdiannotations so we are fine to disable that
  • we know that we want only our own bundle’s code (our .class files) to be scanned for annotations scanned by -bundleannotations and -dsannotations
    • we have a package structure with a common prefix which makes this easy
  • we use quite a lot of jar-wrapping (with and without BCP) but where we are sure that those jars are not containing any relevant annotations - so we want bnd to ignore jars on the BCP

In case of doubt, just start with a single bundle’s bnd.bnd

Conclusion

For the problematic bundle we could reduce build time:

  • from 4 seconds
  • down to 1.9 seconds

image

  • The bottom row shows the build time without the 4 instructions above (4.034s : everything is scanned)
  • the first row shows with the 4 instructions but with *com.our.packages* on each instruction (2.4s: only our code is scanned)

Now I set -metatypeannotations: and -cdiannotations: to empty (no value), to basically disable it completely.

This brought the build time down to roughly 1.8s.

It all comes down to this: bnd has the plugins above, which are all are executed during a build for every bundle. Not all are relevant for your code base. It is up to you to find the right knobs to tell each plugin what to do or more important what not to do.

1 Like