eclipse-ee4j / metro-jax-ws

metro-jax-ws
https://eclipse-ee4j.github.io/metro-jax-ws/
BSD 3-Clause "New" or "Revised" License
68 stars 40 forks source link

Reducing CPU-usage when intialising multiple proxy-objects #686

Open DaScheid opened 4 weeks ago

DaScheid commented 4 weeks ago

An application, which creates many proxy-objects, slows down doing so when CPU is a limited resource. E.g. parallel initialisation for 5x WSDL-files 80x proxy-objects each (so com.sun.xml.ws.client.WSServiceDelegate::getPort is called 400 times), could take an extended amount of time to finish with sparse CPU. Running the same code on "better" hardware results in much shorter execution time, unfortunatelly this might not always be an option.

Profiling the application showed, that most CPU-time is spend during calls to "getPort" on:

  1. Class- / resourse-loading
  2. Initialising JAXBContext inside MetroConfigLoader

The time spend on the JAXBContext-initialisation is also heaviliy depending on using the glassfish-implementation of JAXBContextFactory or a slower implementation from another package. If someone faces a similar performance-problem, make sure that the glassfish-impl is used by providing -Djakarta.xml.bind.JAXBContextFactory=org.glassfish.jaxb.runtime.v2.JAXBContextFactory to the VM-args. Depending on the implementation used, this can already make a significant difference without any code-changes (in this case a reduction of 60-70 seconds), as the JAXBContext is not cached but re-created for each MetroConfigLoader-construcor-call (see MetroConfigLoader::loadMetroConfig).

The frequent class- and resoure-loadings have two main root causes, which are similar "costly":

  1. MetroConfigLoader is initialised multiple times with the same parameters to search for its config
  2. ServiceFinder is instructed to search for the same service-class with the same classloader (again) after a previous attempt with the exact same classloder and service-class failed to find an implementation

Raised a PR to showcase the changes made to introduce caching for these CPU-hotspots. So e.g. if a ServiceLoader wasn't able to find an implentation for a service-class with a specified classloader on the first try, then it will return a cached iterator instead of trying to find a non-existing service-implementation again with the same combiniation of service-class and classloader.

Profiling results with the proposed changes: Combining all these changes reduces initialisation time from ~30 seconds down to 10-12 seconds, so only 33-40% left of the initial time frame. Applying these changes only partially has a minor effect, e.g. all changes without the MetroConfigLoader-change result in roughly ~22 seconds init-time. Similar timings can be measured if all changes except the ServiceLoader-change are applied. The most remaining time is spend on calling "com.sun.xml.ws.glassfish.JAXBRIContextFactory::newContext", but unsure if caching or something similar can be applied here, as each WSDL needs its own jaxb-context.