如何使Java 8 Nashorn快速?

时间:2014-10-25 10:13:31

标签: java javascript performance nashorn

我正在使用Java 8 Nashorn将CommonMark呈现给HTML服务器端。如果我编译并缓存并重用CompiledScript,则某个页面需要5分钟才能呈现。但是,如果我改为使用eval,并缓存并重用脚本引擎,则渲染同一页面需要3秒钟。

为什么CompiledScript这么慢? (示例代码如下)

在Nashorn中运行Javascript代码的好方法是什么,一次又一次地反复运行?并且避免多次编译Javascript代码?

这是服务器端Scala代码片段,它以一种需要5分钟的方式调用Nashorn :(运行200次;我正在编译从CommonMark到HTML的许多注释。)(此代码基于{{3} }。)

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

修改请注意,上面的$script会重新评估200次。我测试了一个只评估过一次的版本,但显然我写了一些bug,因为只有一次版本不会超过5分钟,尽管它应该是最快的版本之一,this blog article。这是快速版本:

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

/修改

这需要2.7秒:(当运行200次时)

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)")

我实际上已经猜到CompiledScript版本(最顶端的代码段)会更快。无论如何,我想我必须缓存渲染的HTML服务器端。

(Linux Mint 17& Java 8 u20)

更新

我刚才注意到在末尾使用invokeFunction代替eval几乎快了两倍,只需1.7秒。这与使用Rhino编译的Javascript代码到Java字节码的Java 7版本大致一样快(作为构建过程中的一个单独且复杂的步骤)。也许这是最快的结果?

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**")

2 个答案:

答案 0 :(得分:8)

使用CompiledScript的代码变体似乎重新评估remarkable.min.js 200次 - 而基于eval的版本执行此操作一次。这解释了运行时的巨大差异。

只有remarkable.render(__source__)预编译,基于CompiledScript的变体比基于evalinvokeFunction的变体略快(在我的机器上,Oracle Java 8u25)。< / p>

答案 1 :(得分:1)

CompiledScript在8u40中有所改进。您可以下载jdk8u40 @ https://jdk8.java.net/download.html

的早期访问下载