> In my other project, FreeMarker, I implemented optional caching of
> wrappers. By default, it is turned off. I heard of some people that
> turned it on, but in most use cases, there are no noticeable
> performance differences.
>
> While you should test every performance hypothesis with a profiler,
> the common presumption these days is that these objects are
> sufficiently light to create that their repeated creation and
> collection (they'll likely not even survive the first eden space
> collection) is too insignificant burden to the GC to justify keeping
> them around.
>
> They're simply too light to cache.
>
>
My concern was not memory use but the expense of method lookups via
reflection. I imagine Rhino does a lot of caching of the lookups, then?
--
Daryl Stultz
_____________________________________
6 Degrees Software and Consulting, Inc.
http://www.6degrees.com
mailto:da...@6degrees.com
> On Mon, Jan 4, 2010 at 7:13 AM, Attila Szegedi <szeg...@gmail.com> wrote:
>
>> In my other project, FreeMarker, I implemented optional caching of
>> wrappers. By default, it is turned off. I heard of some people that
>> turned it on, but in most use cases, there are no noticeable
>> performance differences.
>>
>> While you should test every performance hypothesis with a profiler,
>> the common presumption these days is that these objects are
>> sufficiently light to create that their repeated creation and
>> collection (they'll likely not even survive the first eden space
>> collection) is too insignificant burden to the GC to justify keeping
>> them around.
>>
>> They're simply too light to cache.
>>
>>
> My concern was not memory use but the expense of method lookups via
> reflection. I imagine Rhino does a lot of caching of the lookups, then?
Caching is performed once per class and top-level scope, and results stored in an instance of ClassCache that is associated with the topmost scope.
As an implication, if you use a new top-level scope for every script execution (which you normally do), you'll end up with the cache being recreated on a per-scope basis. If you use a shared-prototype top-level scope, you'll avoid this (but then you have to use shared-prototype top-level scope pattern, which I generally dislike).
The problem is that every Java method needs to be exposed as a JavaScript Function, and as such it needs to have its internal "prototype" property set to the Function() constructor, which is found in a properly initialized (initStandardObjects) top-level scope. Hence, the created and cached reflective data depends upon a Function constructor instance in a standardly-initialized scope.
Your choices are basically:
a) use one non-shared top-level scope per script execution, suffer from repeated reflection lookups between script executions
b) use one shared scope per script execution as a prototype of non-shared per-execution scopes. Drawbacks: need to use dynamic scoping (nonstandard JS), and scripts have shared state *that is mutable*.
c) have one ClassCache and assign it to every scope you create - see ClassCache#associate() and ClassCache#get- you'll have to do it on the scope *before* calling initStandardObjects() as it'll install an empty new ClassCache into it. Drawback: Java methods and constructors mirrored into your scope will use Function prototype from another scope (the one ClassCache was first associated with) - I'm not sure if this can cause any problems (aside from never releasing that first scope from memory, which is a minor, fixed size memory usage increase), it might actually work in your scenario.
Attila.
--
home: http://www.szegedi.org
twitter: http://twitter.com/szegedi
weblog: http://constc.blogspot.com
On Mon, Jan 4, 2010 at 14:54, Attila Szegedi <szeg...@gmail.com> wrote:
> On 2010.01.04., at 14:19, Daryl Stultz wrote:
>
> Your choices are basically:
> a) use one non-shared top-level scope per script execution, suffer from
> repeated reflection lookups between script executions
>
> b) use one shared scope per script execution as a prototype of non-shared
> per-execution scopes. Drawbacks: need to use dynamic scoping (nonstandard
> JS), and scripts have shared state *that is mutable*.
>
> c) have one ClassCache and assign it to every scope you create - see
> ClassCache#associate() and ClassCache#get- you'll have to do it on the scope
> *before* calling initStandardObjects() as it'll install an empty new
> ClassCache into it. Drawback: Java methods and constructors mirrored into
> your scope will use Function prototype from another scope (the one
> ClassCache was first associated with) - I'm not sure if this can cause any
> problems (aside from never releasing that first scope from memory, which is
> a minor, fixed size memory usage increase), it might actually work in your
> scenario.
>
> Attila.
>
> --
> home: http://www.szegedi.org
> twitter: http://twitter.com/szegedi
> weblog: http://constc.blogspot.com
>
> >
> > --
> > Daryl Stultz
> > _____________________________________
> > 6 Degrees Software and Consulting, Inc.
> > http://www.6degrees.com
> > mailto:da...@6degrees.com
>
>
> _______________________________________________
> dev-tech-js-engine-rhino mailing list
> dev-tech-js-...@lists.mozilla.org
> https://lists.mozilla.org/listinfo/dev-tech-js-engine-rhino
>
> the thing is that JavaMembers could cache
Could it, should it, does it? Is this a feature/implementation request?
> As an implication, if you use a new top-level scope for every script
> execution (which you normally do), you'll end up with the cache being
> recreated on a per-scope basis. If you use a shared-prototype top-level
> scope, you'll avoid this (but then you have to use shared-prototype
> top-level scope pattern, which I generally dislike).
>
This page:
https://developer.mozilla.org/En/Rhino_documentation/Scopes_and_Contexts
under "Sharing Scopes" states "for our purposes it gives us an easy way to
share a set of read-only variables across multiple scopes". Is this section
describing the "shared-prototype top-level scope pattern" you dislike? Your
drawbacks state it is mutable but the line I cited says "read-only".
Perhaps you can describe how it is "non-standard". How would someone writing
the JavaScript get tripped up by this?
Thanks.
But then if we cache that reflection in a static weakhashmap<Class,xxxx>
we could just not get it from the top level scope ClassCache but directly
internally so what JavaMembers do have a private static
WeakHashmap<Class,JavaMembers> member...
But i guess the ClassCache attached to a scope is in purpose?
johan
On Mon, Jan 4, 2010 at 17:02, Daryl Stultz <da...@6degrees.com> wrote:
> On Mon, Jan 4, 2010 at 10:38 AM, Johan Compagner <jcomp...@gmail.com
> >wrote:
>
> > the thing is that JavaMembers could cache
>
>
> Could it, should it, does it? Is this a feature/implementation request?
>
> --
> Daryl Stultz
> _____________________________________
> 6 Degrees Software and Consulting, Inc.
> http://www.6degrees.com
> mailto:da...@6degrees.com
Yes, ClassCache attached to a scope is on purpose since it has the Class->JavaMembers map. JavaMembers has a bunch of NativeJavaMethod instances, which are JavaScript functions (they extend BaseFunction), and as such need to answer true to "instanceof Function", hence they are scope specific, as they bind their prototype to the object returned by the expression "Function.prototype" in that scope.
So, yes, this could be refined by separating the caching in two - MemberBox[] objects are scope-independent, and more expensive to create, while JavaMembers are scope-specific and less expensive to create.
Attila.
On 2010.01.04., at 17:07, Johan Compagner wrote:
> it could do it (doesnt do it)
> At least not the code i look at currently (still using 1.6R7)
>
> But then if we cache that reflection in a static weakhashmap<Class,xxxx>
> we could just not get it from the top level scope ClassCache but directly
> internally so what JavaMembers do have a private static
> WeakHashmap<Class,JavaMembers> member...
>
> But i guess the ClassCache attached to a scope is in purpose?
>
> johan
>
>
> On Mon, Jan 4, 2010 at 17:02, Daryl Stultz <da...@6degrees.com> wrote:
>
>> On Mon, Jan 4, 2010 at 10:38 AM, Johan Compagner <jcomp...@gmail.com
>>> wrote:
>>
>>> the thing is that JavaMembers could cache
>>
>>
>> Could it, should it, does it? Is this a feature/implementation request?
>>
>> --
>> Daryl Stultz
Attila.
> On Mon, Jan 4, 2010 at 8:54 AM, Attila Szegedi <szeg...@gmail.com> wrote:
>
>> As an implication, if you use a new top-level scope for every script
>> execution (which you normally do), you'll end up with the cache being
>> recreated on a per-scope basis. If you use a shared-prototype top-level
>> scope, you'll avoid this (but then you have to use shared-prototype
>> top-level scope pattern, which I generally dislike).
>>
>
> This page:
> https://developer.mozilla.org/En/Rhino_documentation/Scopes_and_Contexts
> under "Sharing Scopes" states "for our purposes it gives us an easy way to
> share a set of read-only variables across multiple scopes". Is this section
> describing the "shared-prototype top-level scope pattern" you dislike? Your
> drawbacks state it is mutable but the line I cited says "read-only".
>
> Perhaps you can describe how it is "non-standard". How would someone writing
> the JavaScript get tripped up by this?
Normally, it doesn't. I myself have a system that uses this approach that churns over a million script executions per day and encounters no problems. The problem is, you'll hardly be able to completely lock down the shared scope - while you can not easily put anything into it, you can, say, have one script extend or replace functions of standard objects (i.e. Array.push = function() { ... } - you get the idea) and have it be observed by all others. You can, of course, go through the full reachable JS object graph in the shared scope by recursively calling getIds() and then call ScriptableObject.seal() on every one of them.
Then in turn, you'll have a problem if you (or users of your system) will actually expect to be able to modify built-in objects (i.e. by adopting a JS library that attempts to do it) and failing. Now, of course, you could "install" that library into the shared scope by executing its function-defining scripts in that scope, and that'll solve the problem. However, you then might run into the problem of that library using "x" instead of "this.x" to refer to some globally defined "x" - it'll fail if "x" is actually defined in the individual non-shared scopes. Witness:
shared scope has these functions defined:
function getGlobalX() {
return x;
}
function getThisX() {
return this.x;
}
a script then runs against individual scope that has the shared scope as the prototype:
var x = 1;
print(getThisX());
print(getGlobalX());
getThisX() will succeed, but getGlobalX() will fail. If you can, fix the functions to use explicit "this." (or not rely on global identifiers - even better). If you can not, then you need to turn on Rhino's "dynamic scopes" feature, which introduces non-standard scoping resolution rules (as JS language specification prescribes use of lexical scoping), and can then create subtle bugs with libraries that use lexical scopes to implement private members.
So, yes, you can do it, and I'm not speaking against it, just making sure you know the potential pitfalls - if they don't apply to your system, all the better.
Attila.
--
home: http://www.szegedi.org
twitter: http://twitter.com/szegedi
weblog: http://constc.blogspot.com
>
> Thanks.
> On 2010.01.04., at 17:07, Daryl Stultz wrote:
> You can, of course... Then in turn, ...of course, you could ... then might
>
Wow, this is a bit deeper than I thought. Thanks for the details. What's
your opinion of this:
http://mxr.mozilla.org/mozilla/source/js/rhino/examples/DynamicScopes.java
Is this an implementation of what you consider problematic/challenging or is
this something different?