Hi Sundar,

Thanks again for all of the help. The code looks good when I pulled from 
jdk8u-dev.

Thanks,
Chris

________________________________________
From: A. Sundararajan [[email protected]]
Sent: Thursday, March 19, 2015 11:10
To: Chris Pettitt; [email protected]
Subject: Re: 8u20 with multi-threaded class cache

Hi,

Perhaps repo not sync'ed?

http://hg.openjdk.java.net/jdk8u/jdk8u/nashorn/file/da9741520576/src/jdk/nashorn/api/scripting/NashornScriptEngine.java

You can see Source.sourceFor static helper is called to create Source
object. And that one has cache logic to choose existing Source - if
available. And Context.findCachedClass(Source) can check Source->Class
cache.

BTW, nashorn development checks into this repo for 8 update fixes:

http://hg.openjdk.java.net/jdk8u/jdk8u-dev/

You can check here too:

http://hg.openjdk.java.net/jdk8u/jdk8u-dev/nashorn/file/e024db176497/src/jdk/nashorn/api/scripting/NashornScriptEngine.java

Hope this helps,
-Sundar


On Thursday 19 March 2015 11:05 PM, Chris Pettitt wrote:
> 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

Reply via email to