> On Oct 6, 2015, at 4:38 AM, Claes Redestad <claes.redes...@oracle.com> wrote: > > Hi Mandy, > > On 2015-10-05 23:13, Mandy Chung wrote: >> I have been experimenting a jlink plugin to improve the module system >> startup time. On a Quad-Core Intel Xeon E5 @3.7 GHz, 16G memory (Mac Pro) >> machine, the module system startup takes 110 ms and 75% of it is to >> reconstitute 64 module descriptors with 2388 packages altogether. >> >> This high overhead includes initializing lambda forms (class loading, >> linking, initialization), parsing of module-info.class and compilation of >> many methods during startup. >> >> The attached charts shows the summary of module system startup >> 1) jake-b83 (as of Sep 29) >> module bootstrap time 110 ms >> module descriptor reconstitution 82 ms >> VM startup time is 3x of jdk9-b83 vm startup time (148ms vs 47 ms) >> >> 2) No lambda at startup >> module bootstrap time 66 ms (saved 44 ms compared to #1) >> module descriptor reconstitution 33.6 ms (saved 48.4ms) >> VM startup time is 2.2x of jdk9-b83 (105ms vs 47ms) >> >> >> 3) jlink plugin to generate a .class file to build module descriptors at >> link time >> module bootstrap time 50.7 ms (saved 15.3ms compared to #2) >> module descriptor reconstitution 16.8 ms (saved 16.8ms) >> VM startup time is 1.9x of jdk9-b83 (90ms vs 47ms) > > Is it safe to assume that #3 include the changes in #2?
#2 and #3 are different changes. ModuleDescriptor.Requires::modsValue is one use of lambda that is changed in both #2 and #3. Other change of Stream::forEach in ModuleDescriptor in #2 are not in #3. #3 has its own builder to create ModuleDescriptor. Stream::forEach is used in 3 places in ModuleDescriptor in the loop validating the strings passing in the Exports and Provides constructor for example that is skipped in this installed module descriptor reconstitution. #2 is mainly to measure the overhead due to lambda at startup to help understand what attributes to 82ms. > >> >> For #3, it parses module-info.class at link time and validates all names. >> It generates a .class file to call a custom Builder to create >> ModuleDescriptor for the installed modules. At runtime, the generated class >> will construct the Builder and ModuleDescriptor objects will skip name >> validation, no defensive copy of the input set/map and skip reading and >> parsing of module-info.class (this is done by a special module descriptor >> builder class that doesn’t use lambda. This special builder is only used by >> installed modules). > > Since you're generating and loading classes that would otherwise not be > loaded, have you done any footprint measurements? > The charts show the number of loaded classes and heap usage for these 3 runs: http://cr.openjdk.java.net/~mchung/jigsaw/jake-startup.20151005.pdf 692 classes are loaded in #1 and 503 classes are loaded in #3. Heap usage after GC in #1 and #3 is 1443 KB 1380 KB respectively. > Alternatively, wouldn't it be possible for the plugin to modify the > module-info.class directly? > Yes it’s possible but I don’t see how this can improve the startup time. >> >> It saves 15.3 ms (23% of the module system bootstrap time in #2). The >> downside of this optimization is a little harder to make change and diagnose >> (this plugin implementation is straight forward though). There may be >> other small optimization to explore that could be done at jlink time (e.g. >> BuiltinClassLoader maintains a package to module map that can be constructed >> with a specific size to avoid map resizing). >> >> What’s your thought/opinion to integrate this jlink plugin into jake? > > In general I think improving startup by staving off the first use of lambda > isn't very helpful except for trivial applications (which we often > over-emphasize when testing startup), while moving module validation to link > time seems more like a real gain. It's a bit hard to tell if it's worth it > without having seen a patch for the prototype, though. I hope that LambdaForm initialization can be speed up so that it won’t incur significant overhead at startup time. As I explain above, ModuleDescriptor.Requires::modsValue is the only method changed from Stream::forEach to foreach loop. Mandy