John Cowan wrote:
> On Jan 17, 2008 6:30 AM, John Wilson <[EMAIL PROTECTED]> wrote:
> 
>> One thing I have considered is aggregating all these
>> closures/lambda/etc in a compilation unit as static methods on a
>> single class. This means that you amortise the cost of a classloader
>> over several methods. Instances of the function object delegate to the
>> static method. The cost is, obviously, delayed GC of the "mothership"
>> class. Of course, if you can dynamically create new functions this
>> won't help.
> 
> That's what I'm doing.  Function is an abstract class rather than a
> marker interface, and it provides a field named _index.  If Foo is a
> subclass of Function that has five static methods, then there are also
> five static fields initialized to five instances of Foo with _index
> values from 0 to 4; these represent at source-language level the five
> possible functions (in general, there may be more than one Foo per
> source-code module, but one is typical).

JRuby has done this and many others, and the fastest way turns out to be 
the most permgen-impactful way.

* First, JRuby used reflection everywhere. It was simple, but it's 
always slower than non-reflective options
* Then we hand-wrote method objects as anonymous classes and used those 
as bindings. But it doesn't scale, and doesn't work at all for code 
loaded at runtime.
* Then we used a hand-written indexed method handle like you describe. 
Again, it worked (albeit a bit slower than individual methods), but it 
was too much effort to implement by hand and wouldn't work for generated 
code.
* Then we started generating small method handle classes for all 
methods. This allowed us to generate everything, so runtime code could 
use the same model. The handles were wrapped in a DynamicMethod object. 
This was faster than any of the above options because the invocation 
code was monomorphic. But the DynamicMethod wrapper was generic and 
megamorphic.
* Then we started generating DynamicMethod implementations, eliminating 
that megamorphic call site from the picture. This is how it works today 
in JRuby, with the call handle being only a single hop to the actual 
method being invoked. But it's a load on permgen...each handle is a 
class, in some cases with their own classloaders.
* JRuby also supports generating indexed DynamicMethods, but there's a 
noticeable performance hit adding the extra decision.

The current compiler has its own strategy:

* One .rb script is compiled into exactly one .class file. Every body of 
code is a method on that class. The methods are instance methods, 
because there are various cache fields on the class, and we want to be 
able to reuse the same compiled class across JRuby instances. So at 
runtime, the bodies of code in the script are either executed directly 
(main script, class bodies) or bound to methods using any of the 
techniques above. But there's only ever one .class file for one .rb 
file, which I believe is far cleaner than Groovy, Scala, XRuby, and 
others generating dozens of .class files.
* In JIT mode, a single-method class is generated per compiled 
method...each with its own classloader, so they can GC. In this mode, 
the existing interpreted DynamicMethod object aggregates an instance of 
the newly jitted method, and invokes against that instead of the 
interpreter.

Perhaps this helps show why we need to reduce the cost of generating 
single-method classes. The next trick we want to explore was suggested 
by John Rose:

1. Define a set of numbered "invoker" interfaces with numbered methods
2. Define a parallel set of method handle instances that invoke exactly 
one of those interfaces
3. Generate method handles or jitted methods in n-sized batches, with 
the compiled object implementing the n invoker interfaces for the n 
methods contained therein.
4. Bind each of the n methods using the specific method handle necessary.

The idea behind this is that while you have many implementations of the 
method handle interface, callers to them are still fairly monomorphic. 
At the same time you've eliminate the indexed switch, so from call site 
to target method is a straight-through affair.

We'll probably implement this after 1.1, since we've managed to reduce 
our permgen load in other ways.

- Charlie

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "JVM 
Languages" group.
To post to this group, send email to jvm-languages@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/jvm-languages?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to