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 -~----------~----~----~----~------~----~------~--~---