I've added a mode to the compiler that allows it to selectively box
individual local variables only if they are used in contained scopes.
This means that if you have a method like the following:
def foo
a = 1
1.times { puts 'here' }
a += 2
return a
end
The "a" local variable will not be in a heap-based scope, but instead be
a normal Java local variable, since the contained closure never
references it.
The boxed mode is disabled by default. Use property
jruby.compile.boxed=true to turn it on.
Any scope-aware methods like eval disables this behavior, since it's
impossible to know ahead of time whether an eval might need access to a
contained variable. There's also nothing built-in yet to detect evals
within containing scopes, since the AST inspector doesn't walk into
closures to look for them:
$ jruby -J-Djruby.compile.boxed=true -e "def foo; a = 1; 1.times { eval
'puts a' }; end; foo"
nil
Here eval doesn't see the set value for a because a is not being boxed.
That's a bug, and I'll fix it later.
Note also that this static optimization is incompatible with Ruby 1.8's
feature that allows a proc to be used as a binding, since any closure
could conceivably be used to eval code that accesses variables that
otherwise look uncaptured. Ruby 1.9 currently does not support that feature.
Perf numbers aren't substantially improved by this, which gives us an
answer to groovy, ironruby, xruby, and ruby.net claiming it's worth
doing. It seems that if you have to allocate *any* structure for
heap-based variables in a method, you've already destroyed any gains
you'd get by using stack-based variables.
~/NetBeansProjects/jruby $ jruby -J-server bench_uncaptured_local_vars.rb
1.735000 0.000000 1.735000 ( 1.735000)
1.792000 0.000000 1.792000 ( 1.791000)
1.443000 0.000000 1.443000 ( 1.444000)
1.747000 0.000000 1.747000 ( 1.747000)
1.687000 0.000000 1.687000 ( 1.688000)
1.716000 0.000000 1.716000 ( 1.716000)
1.420000 0.000000 1.420000 ( 1.420000)
1.402000 0.000000 1.402000 ( 1.402000)
1.438000 0.000000 1.438000 ( 1.437000)
1.550000 0.000000 1.550000 ( 1.550000)
~/NetBeansProjects/jruby $ jruby -J-server -J-Djruby.compile.boxed=true
bench_uncaptured_local_vars.rb
1.804000 0.000000 1.804000 ( 1.803000)
1.591000 0.000000 1.591000 ( 1.591000)
1.502000 0.000000 1.502000 ( 1.501000)
1.426000 0.000000 1.426000 ( 1.427000)
1.318000 0.000000 1.318000 ( 1.318000)
1.491000 0.000000 1.491000 ( 1.492000)
1.334000 0.000000 1.334000 ( 1.335000)
1.324000 0.000000 1.324000 ( 1.324000)
1.407000 0.000000 1.407000 ( 1.407000)
1.338000 0.000000 1.338000 ( 1.338000)
So, left to do on this:
- make it possible for scopes with contained closures but *no* captured
variables to use super-lightweight scope objects. This may or may not be
necessary, since an unused scope is already pretty light.
- fix the "eval within closure" issue
- say "nyah" to all other implementations that claimed they'd be a lot
faster because of selective variable boxing
- disable the "proc-as-binding" feature when boxed variable compilation
is enabled (i.e. raise a type error in eval)
- Charlie
---------------------------------------------------------------------
To unsubscribe from this list please visit:
http://xircles.codehaus.org/manage_email