The trick in all these optimizations is to allow the HotSpot to inline
as much as possible of the call path. The default dispatch logic Rhino
has now all passes through only few methods, making them megamorphic.
Just to highlight some other work in the area:
John Rose was experimenting with a MethodHandle (the invokedynamic
stuff slated for Java 7 as per JSR-292) based approach with Rhino, he
forwarded me some of his code doodles for it (I gave them to Norris
since, I'll send them to you too so you can take a look).
I'm of course thinking that next time we'd need to overhaul Rhino
would be when Java 7 is widespread, and actually rewrite it in terms
of my invokedynamic-based linker framework, see slides of the
presentation I gave at JVM Language Summit here: <http://wiki.jvmlangsummit.com/MOP_and_Invokedynamic
>; the slides have a link to the SVN repository too. That'll make it
possible to emit bytecode of the form "invoke method named
dyn:getProp:foo" and have the linker framework resolve it to a fully
optimizable (inlinable) method call on the object of appropriate type
at the call site just before it is invoked.
You can also get a working OpenJDK build with invokedynamic in it for
Mac OS X at <http://www.szegedi.org/mlvm-macosx.html> (for Windows and
Linux, you can just download the latest OpenJDK beta from Sun)
Of course, that's all well and good, but we'll need Java 7 for it to
work :-) (Or, alternatively, Rémi Forax's backport of JSR-292)
As for Rhino internals relying on ScriptableObject, I believe these
should be considered bad practice. All Rhino internals should ideally
only rely on Scriptable interface. I think you can liberally change
code from using ScriptableObject to Scriptable whereever you can.
In any case, I'll make sure to take a look at your effort sometime
when I get a bit of time (maybe over weekend).
Attila.
On 2009.11.03., at 10:48, Hannes Wallnoefer wrote:
Inspired by Norris' recent remark about low hanging fruit in the Rhino
garden I've started to experiment with mapping individual JS Objects
directly to Java classes with property access implemented by auto-
generated bytecode. I've created a project on github with some initial
proof-of-concept code:
http://github.com/hns/rhino-8
I called it rhino-8 because I started out on the idea of native JS
property access implemented by Google's V8 engine described here:
http://code.google.com/apis/v8/design.html#prop_access
Obviously, the JVM poses some restrictions on what you can do (for
example, to invoke a method or access a field in a class from Java
bytecode, the target interface or class must be "baked" into the local
class so it can't really be generated on the fly). So I went for a
more conservative approach which has the benefit of also being more
compatible with the Rhino script runtime.
What I am currently doing is to generate a Java class implementing
Rhino's Scriptable interface for each individual set of properties,
store property values in a dense object array, and generate switch
statements in the get(), put(), has(), and delete() methods similar to
those created by the IdSwitch tool to directly map the object's
properties into the value array *). When put() is called with an
unknown property id, it throws an UnknownFieldError that is caught by
a wrapper which transparently "morphes" the object to a newly
generated class that includes the new property id.
*) Note: I previously used individual native Java fields to store
property values but changed to the array representation for easier
transition between classes. I may go back to individual fields if it
benefits performance.
In other words, I'm providing a faster implementation of Scriptable by
implementing property access methods using a switch statement for a
well-known set of properties similar to the one used for NativeObject,
NativeString, NativeArray etc. prototypes instead of the generic slot
hashing implemented by ScriptableObject.
The code for this is in the org.mozilla.javascript.adaptive package:
http://github.com/hns/rhino-8/tree/master/src/org/mozilla/javascript/adaptive
You can use the Rhino shell's defineClass() function to play with it:
js> defineClass(org.mozilla.javascript.adaptive.AdaptiveObject);
js> a = new AdaptiveObject();
On initial benchmarks, this yields something like 30% - 50%
improvement in property access time compared to the current
ScriptableObject property implementation (quite a bit when using the
server HotSpot VM, which obviously does its own optimization magic on
ScriptableObject). Of course the approach won't work for all
situations. If you create an object and subsequently add properties to
it, a new Java class is generated in each step, and the object is
"morphed" to the new class in each step. So this isn't a magic pill
that will bring Rhino performance on par with V8 (not even close), but
I think it's an approach worth following. The good thing is that it
should be relatively easy to use different object implementations for
different situations, or switch between them as needed.
For example, one area where this would most probably make immediate
sense is object literals. Object literals have a set of properties
that is known beforehand, and they are often read-only and used over
and over again, so creating an optimized Java class for them should be
worth the overhead most of the time. Other ideas I'm playing with may
be to initially use a hashed object and watch usage patterns to decide
when and if to switch to compiled, "switched" state, or more simply to
delay class generation until the first invocation of get(), which
might work well for many common JS programming patterns.
Unfortunately, a lot of Rhino code currently assumes things to be
ScriptableObject instances, so it's not easy to test other object
implementations "in the wild". For example, Object.defineProperty
assumes not only the target object to be an instance of
ScriptableObject, but also the property descriptor argument. So one of
the next steps will be to factor the "advanced" property access
methods implemented by ScriptableObject into a new sub-interface of
Scriptable (I'll probably call it
org.mozilla.javascript.ExtendedScriptable - any other suggestions?)
and use that wherever ScriptableObject is used. That won't be fun, so
maybe as a short-term hack I'll just extend ScriptableObject in
AdaptiveObject and override the required methods.
I'm looking forward to your feedback and suggestions!
Hannes
_______________________________________________
dev-tech-js-engine-rhino mailing list
[email protected]
https://lists.mozilla.org/listinfo/dev-tech-js-engine-rhino