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

Reply via email to