最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How make Java 8 Nashorn fast? - Stack Overflow

programmeradmin3浏览0评论

I'm using Java 8 Nashorn to render CommonMark to HTML server side. If I pile and cache and reuse a CompiledScript, a certain page takes 5 minutes to render. However, if I instead use eval, and cache and reuse the script engine, rendering the same page takes 3 seconds.

Why is CompiledScript so slow? (sample code follows)

What's a good approach for running Javascript code in Nashorn, over and over again as quickly as possible? And avoiding piling the Javascript code more than once?

This is the server side Scala code snippet that calls Nashorn in a way that takes 5 minutes: (when run 200 times; I'm piling many ments from CommonMark to HTML.) (This code is based on this blog article.)

if (engine == null) {
  val script = scala.io.Source.fromFile("public/res/remarkable.min.js").mkString
  engine = new js.ScriptEngineManager(null).getEngineByName("nashorn")
  piledScript = engine.asInstanceOf[js.Compilable]pile(s"""
    var global = this;
    $script;
    remarkable = new Remarkable({});
    remarkable.render(__source__);""");
}
engine.put("__source__", "**bold**")
val htmlText = piledScript.eval()

Edit Note that the $script above is reevaluated 200 times. I did test a version that evaluated it only once, but apparently then I wrote some bug, because the only-once version wasn't faster than 5 minutes, although it should have been one of the fastest ones, see Halfbit's answer. Here's the fast version:

...
val newCompiledScript = newEngine.asInstanceOf[js.Compilable]pile(s"""
  var global;
  var remarkable;
  if (!remarkable) {
    global = this;
    $script;
    remarkable = new Remarkable({});
  }
  remarkable.render(__source__);""")
...

/Edit

Whereas this takes 2.7 seconds: (when run 200 times)

if (engine == null) {
  engine = new js.ScriptEngineManager(null).getEngineByName("nashorn")
  engine.eval("var global = this;")
  engine.eval(new jio.FileReader("public/res/remarkable.min.js"))
  engine.eval("remarkable = new Remarkable({});")
}
engine.put("source", "**bold**")
val htmlText = engine.eval("remarkable.render(source)")

I would actually have guessed that the CompiledScript version (the topmost snippet) would have been faster. Anyway, I suppose I'll have to cache the rendered HTML server side.

(Linux Mint 17 & Java 8 u20)

Update:

I just noticed that using invokeFunction at the end instead of eval is almost twice as fast, takes only 1.7 seconds. This is roughly as fast as my Java 7 version that used Javascript code piled by Rhino to Java bytecode (as a separate and plicated step in the build process). Perhaps this is as fast as it can get?

if (engine == null) {
  engine = new js.ScriptEngineManager(null).getEngineByName("nashorn")
  engine.eval("var global = this;")
  engine.eval(new jio.FileReader("public/res/remarkable.min.js"))
  engine.eval("remarkable = new Remarkable({});")
  engine.eval(
    "function renderCommonMark(source) { return remarkable.render(source); }")
}
val htmlText = engine.asInstanceOf[js.Invocable].invokeFunction(
                                       "renderCommonMark", "**bold1**")

I'm using Java 8 Nashorn to render CommonMark to HTML server side. If I pile and cache and reuse a CompiledScript, a certain page takes 5 minutes to render. However, if I instead use eval, and cache and reuse the script engine, rendering the same page takes 3 seconds.

Why is CompiledScript so slow? (sample code follows)

What's a good approach for running Javascript code in Nashorn, over and over again as quickly as possible? And avoiding piling the Javascript code more than once?

This is the server side Scala code snippet that calls Nashorn in a way that takes 5 minutes: (when run 200 times; I'm piling many ments from CommonMark to HTML.) (This code is based on this blog article.)

if (engine == null) {
  val script = scala.io.Source.fromFile("public/res/remarkable.min.js").mkString
  engine = new js.ScriptEngineManager(null).getEngineByName("nashorn")
  piledScript = engine.asInstanceOf[js.Compilable].pile(s"""
    var global = this;
    $script;
    remarkable = new Remarkable({});
    remarkable.render(__source__);""");
}
engine.put("__source__", "**bold**")
val htmlText = piledScript.eval()

Edit Note that the $script above is reevaluated 200 times. I did test a version that evaluated it only once, but apparently then I wrote some bug, because the only-once version wasn't faster than 5 minutes, although it should have been one of the fastest ones, see Halfbit's answer. Here's the fast version:

...
val newCompiledScript = newEngine.asInstanceOf[js.Compilable].pile(s"""
  var global;
  var remarkable;
  if (!remarkable) {
    global = this;
    $script;
    remarkable = new Remarkable({});
  }
  remarkable.render(__source__);""")
...

/Edit

Whereas this takes 2.7 seconds: (when run 200 times)

if (engine == null) {
  engine = new js.ScriptEngineManager(null).getEngineByName("nashorn")
  engine.eval("var global = this;")
  engine.eval(new jio.FileReader("public/res/remarkable.min.js"))
  engine.eval("remarkable = new Remarkable({});")
}
engine.put("source", "**bold**")
val htmlText = engine.eval("remarkable.render(source)")

I would actually have guessed that the CompiledScript version (the topmost snippet) would have been faster. Anyway, I suppose I'll have to cache the rendered HTML server side.

(Linux Mint 17 & Java 8 u20)

Update:

I just noticed that using invokeFunction at the end instead of eval is almost twice as fast, takes only 1.7 seconds. This is roughly as fast as my Java 7 version that used Javascript code piled by Rhino to Java bytecode (as a separate and plicated step in the build process). Perhaps this is as fast as it can get?

if (engine == null) {
  engine = new js.ScriptEngineManager(null).getEngineByName("nashorn")
  engine.eval("var global = this;")
  engine.eval(new jio.FileReader("public/res/remarkable.min.js"))
  engine.eval("remarkable = new Remarkable({});")
  engine.eval(
    "function renderCommonMark(source) { return remarkable.render(source); }")
}
val htmlText = engine.asInstanceOf[js.Invocable].invokeFunction(
                                       "renderCommonMark", "**bold1**")
Share Improve this question edited May 23, 2017 at 12:25 CommunityBot 11 silver badge asked Oct 25, 2014 at 10:13 KajMagnusKajMagnus 11.7k17 gold badges85 silver badges132 bronze badges 1
  • I have also found nashorn to be slower than rhino softwarecenturion.me/posts/2014-04-07-jdk8-nashorn-performance. It bees much faster with warm JIT, but it's unusably slow cold. Also curious if there's anything you can do about it. – coudy Commented Oct 25, 2014 at 11:22
Add a ment  | 

2 Answers 2

Reset to default 8

The variant of your code which uses CompiledScript seems to re-evaluate remarkable.min.js 200 times - while your eval based version does this once. This explains the huge difference in runtimes.

With just the remarkable.render(__source__) prepiled, the CompiledScript based variant is slightly faster than the eval and invokeFunction based ones (on my machine, Oracle Java 8u25).

CompiledScript has been improved a bit in 8u40. You can download early access download of jdk8u40 @ https://jdk8.java/download.html

发布评论

评论列表(0)

  1. 暂无评论