Hi Sundar,
Thanks for the follow up. I'm hoping for something along the lines of what you
described. The code I see seems to suggest different behavior (described
below), but it could be that I'm looking at older code (I'm using [1]) or that
I'm just looking at the wrong code.
Let's assume we'll use the same URLReader for each call to engine.eval. The
call to engine.eval will then call makeSource:
@Override
public Object eval(final Reader reader, final ScriptContext ctxt) throws
ScriptException {
return evalImpl(makeSource(reader, ctxt), ctxt);
}
This in turn creates a new Source - at least in the version I'm looking at:
private static Source makeSource(final Reader reader, final ScriptContext
ctxt) throws ScriptException {
try {
if (reader instanceof URLReader) {
final URL url = ((URLReader)reader).getURL();
final Charset cs = ((URLReader)reader).getCharset();
return new Source(url.toString(), url, cs);
}
return new Source(getScriptName(ctxt), Source.readFully(reader));
} catch (final IOException e) {
throw new ScriptException(e);
}
}
Since we created a new object for the Source, the reference check in
Source.equals will return false, and we'll fall back to doing a full array
comparison:
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Source)) {
return false;
}
final Source src = (Source)obj;
// Only compare content as a last resort measure
return length == src.length && Objects.equals(url, src.url) &&
Objects.equals(name, src.name) && Arrays.equals(content, src.content);
}
Also, by virtue of calling Source(url.toString(), url, cs) we'll end up
re-reading the entire source each time we call eval:
public Source(final String name, final URL url, final Charset cs) throws
IOException {
this(name, baseURL(url, null), readFully(url, cs), url);
}
What do you think? Am I looking in the wrong place, out-of-date
code, or is my understanding flawed?
Thanks again to both you and Hannes for your patience and help.
[1]: http://hg.openjdk.java.net/jdk8u/jdk8u
Best,
Chris
________________________________________
From: nashorn-dev [[email protected]] on behalf of A.
Sundararajan [[email protected]]
Sent: Thursday, March 19, 2015 06:32
To: [email protected]
Subject: Re: 8u20 with multi-threaded class cache
Hi Chris,
If you use Nashorn's URLReader (
http://docs.oracle.com/javase/8/docs/jdk/api/nashorn/jdk/nashorn/api/scripting/URLReader.html
- a Reader wrapping a script URL object), then nashorn's Source would be
cached against that URL could be re-used (and the compiled Class object
as well via findCachedClass).
engine.eval(new URLReader(myScriptURL));
Also, any script loaded via "load(url)" built-in could also be
compiled/cached and re-used (against that URL + last modified since of
the same).
Hope this helps,
-Sundar
On Thursday 19 March 2015 04:00 AM, Chris Pettitt wrote:
Hannes,
Thanks for the detailed reply - this is very helpful. Your answers clear up my
questions for #1 and #2.
For #3, I was hoping we could use some key to retrieve the cached classes that
did not involve reloading / re-reading the source scripts on each lookup.
jdk.nashorn.internal.runtime.Source is used as the key in
jdk.nashorn.internal.objects.Global.findCachedClass. Source tries to avoid the
cost of walking the source contents twice, once for hashing (by caching the
hash) and once for the equality check by checking the reference. Unfortunately,
it *appears* that we're always locked out of this optimization because there is
no public way to feed a Source into the NashornScriptEngine.
Thanks,
Chris
________________________________________
From: Hannes Wallnoefer [[email protected]]
Sent: Tuesday, March 17, 2015 10:38
To: Chris Pettitt; [email protected]; Kunal Cholera
Subject: Re: 8u20 with multi-threaded class cache
Hi Chris, Kunal,
Answers are inlined below.
Am 2015-03-16 um 23:03 schrieb Chris Pettitt:
Hi folks,
We're looking into the possibility of using the class cache in multi-threaded
code, as introduced in [1]. We have a few questions related to this feature:
1. The article implies that ScriptEngine can be treated as thread-safe -
provided we're not using the default context - though the code doesn't state
this explicitly. Is this a safe assumption? Are there any other caveats?
This is correct as far as Nashorn is concerned. In the original JDK8
release, using a ScriptEngine with multiple bindings/globals will
compile each script from scratch. We introduced the class caching in
8u20 as our best effort to fit code reuse on the existing ScriptEngine
API. The only caveat I can think of is that code may run slower with
multiple bindings because of callsite polymorphism. Also bear in mind
that this is a relatively new feature, but there seemed to be no problem
using it with your dust scripts (see example below).
2. As we need to set the Context for each eval, does this lock us out of using
Invocable?
You can actually use this with Invocable.invokeMethod(), passing the
binding as first argument ("thiz" parameter). I've rewritten the
threaded class cache example from the blog post to do this with your
dust benchmark and it seems to work fine:
https://gist.github.com/hns/8f52a620ce36daa3d0ca
I just edited the bench.js file to remove the benchmark loop at the
bottom, otherwise this will run with the script files you sent me. Feel
free to use this as a starting point for your own testing.
It could also work with the other Invocable method taking a "thiz"
parameter, getInterface(Object, Class), but I haven't tested this.
3. The code for determining if two Sources are the same ultimately falls back
to a comparison of the url / name / content of the scripts. Is there a way to
eval with a Source to avoid this fallback? It looks like it is not exposed in a
public way.
I'm not sure I understand this question. What I use in my example is
jdk.nashorn.api.scripting.URLReader, which is handy to preserve the
source URL in error messages. I believe Nashorn will still load the
content of the URL to make sure scripts are actually identical.
I hope this helps. Let me know if you have further questions.
Regards,
Hannes
[1]: https://blogs.oracle.com/nashorn/entry/improving_nashorn_startup_time_using
Thanks,
Chris