That's a big unknown for us. It does not seem to be heavily IO-driven, since using nailgun does help "pure load" scenarios speed up significantly. For example:
INIT OF JRUBY ALONE system ~/projects/jruby $ jruby bench/bench_jruby_init.rb 5 user system total real in-process `jruby ` 0.043000 0.000000 0.043000 ( 0.027000) user system total real in-process `jruby ` 0.045000 0.000000 0.045000 ( 0.045000) user system total real in-process `jruby ` 0.018000 0.000000 0.018000 ( 0.018000) user system total real in-process `jruby ` 0.014000 0.000000 0.014000 ( 0.014000) user system total real in-process `jruby ` 0.014000 0.000000 0.014000 ( 0.014000) INIT OF JRUBY PLUS -rubygems system ~/projects/jruby $ jruby bench/bench_jruby_init.rb 5 -rubygems user system total real in-process `jruby -rubygems` 0.193000 0.000000 0.193000 ( 0.177000) user system total real in-process `jruby -rubygems` 0.085000 0.000000 0.085000 ( 0.085000) user system total real in-process `jruby -rubygems` 0.085000 0.000000 0.085000 ( 0.085000) user system total real in-process `jruby -rubygems` 0.071000 0.000000 0.071000 ( 0.071000) user system total real in-process `jruby -rubygems` 0.076000 0.000000 0.076000 ( 0.076000) ...PLUS require 'activerecord' system ~/projects/jruby $ jruby bench/bench_jruby_init.rb 5 "-rubygems -e \"require 'activerecord'\"" user system total real in-process `jruby -rubygems -e "require 'activerecord'"` 0.192000 0.000000 0.192000 ( 0.176000) user system total real in-process `jruby -rubygems -e "require 'activerecord'"` 0.087000 0.000000 0.087000 ( 0.087000) user system total real in-process `jruby -rubygems -e "require 'activerecord'"` 0.087000 0.000000 0.087000 ( 0.087000) user system total real in-process `jruby -rubygems -e "require 'activerecord'"` 0.069000 0.000000 0.069000 ( 0.069000) user system total real in-process `jruby -rubygems -e "require 'activerecord'"` 0.078000 0.000000 0.078000 ( 0.078000) Note how much startup improves for subsequent runs in the -rubygems and -r activerecord cases. If it were solely IO-bound, we wouldn't see that much improvement. Startup time issues are a combination of factors: * IO, including filesystem searching and the actual read of the file * Parsing and AST building * JVM being cold; our parser, interpreter, core classes are all running at their slowest * Internal caches getting vigorously flushed at boot, since there's so many methods and constants being created My parallelizing patch helps the first three but didn't make a big difference in actual execution of commands like "rake test" in a Rails app. I'm going to poke at startup a bit more today and see if I can figure out how much time in "rake test" is *actually* booting versus execution. - Charlie On Mon, Oct 24, 2011 at 11:56 PM, Andrew Cholakian <and...@andrewvc.com> wrote: > I'm wondering how much of the issue is IO and how much is CPU time required > to parse. Would it be easiest to just do a quick scan for module > dependencies and cache all the files ASAP, then parse serially? I'm not sure > if it'd be possible to do a quick parse for just 'require'. > > On Mon, Oct 24, 2011 at 9:47 PM, Jonathan Coveney <jcove...@gmail.com> > wrote: >> >> I was thinking about the case below, and I think that this is an >> interesting idea, but I'm wondering how you would resolve certain >> difficulties. Imagine: >> >> require 'ALib' >> a = 10+2 >> require 'BLib' >> b=a/2 >> >> where ALib is a lot of random stuff, then: >> class Fixnum >> def +(other) >> self*other >> end >> end >> >> and BLib is a lot of random stuff, then: >> class Fixnum >> def /(other) >> self*other*other >> end >> end >> >> How would you know how to resolve these various pieces? I guess you >> mention eager interpreting and then a cache, but given that any module can >> change any other module's functionality, you would have to keep track of >> everything that you eagerly interpreted, and possibly go back depending on >> what your module declares. How else would you know that a module that >> doesn't depend on any other modules is going to actually execute in a >> radically different way because of another module that you have included? >> The only way I can think of would be if the thread executing any given piece >> of code kept track of the calls that it made and where, and then went back >> to the earliest piece it had to in the case that anything was >> rewritten...but then you could imagine an even more convoluted case where >> module A changes an earlier piece of module B such that it changes how a >> later piece of itself works...and so on. >> >> Perhaps this is incoherent, but I think the question of how you deal with >> the fact that separately running pieces of code can change the fundamental >> underlying state of the world. >> >> 2011/10/24 Charles Oliver Nutter <head...@headius.com> >>> >>> Nahi planted an interesting seed on Twitter...what if we could >>> parallelize parsing of Ruby files when loading a large application? >>> >>> At a naive level, parallelizing the parse of an individual file is >>> tricky to impossible; the parser state is very much straight-line. But >>> perhaps it's possible to parallelize loading of many files? >>> >>> I started playing with parallelizing calls to the parser, but that >>> doesn't really help anything; every call to the parser blocks waiting >>> for it to complete, and the contents are not interpreted until after >>> that point. That means that "require" lines remain totally opaque, >>> preventing us from proactively starting threaded parses of additional >>> files. But there lies the opportunity: what if load/require requests >>> were done as Futures, require/load lines were eagerly interpreted by >>> submitting load/require requests to a thread pool, and child requires >>> could be loading and parsing at the same time as the parent >>> file...without conflicting. >>> >>> In order to do this, I think we would need to make the following >>> modifications: >>> >>> * LoadService would need to explose Future-based versions of "load" >>> and "require". The initial file loaded as the "main" script would be >>> synchronous, but subsequent requires and loads could be shunted to a >>> thread pool. >>> * The parser would need to initiate eager load+parser of files >>> encountered in require-like and load-like lines. This load+parse would >>> encompass filesystem searching plus content parsing, so all the heavy >>> lifting of booting a file would be pushed into the thread pool. >>> * Somewhere (perhaps in LoadService) we would maintain an LRU cache >>> mapping from file paths to ASTs. The cache would contain Futures; >>> getting the actual parsed library would then simply be a matter of >>> Future.get, allowing many of the load+parses to be done >>> asynchronously. >>> >>> For a system like Rails, where there might be hundreds of files >>> loaded, this could definitely improve startup performance. >>> >>> Thoughts? >>> >>> - Charlie >>> >>> --------------------------------------------------------------------- >>> To unsubscribe from this list, please visit: >>> >>> http://xircles.codehaus.org/manage_email >>> >>> >> > > --------------------------------------------------------------------- To unsubscribe from this list, please visit: http://xircles.codehaus.org/manage_email