如何使Java 8 Nashorn快速

How make Java 8 Nashorn fast?

本文关键字:Nashorn 快速 Java 何使      更新时间:2023-09-26

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

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

在Nashorn中一遍又一遍地快速运行Javascript代码的好方法是什么?避免多次编译Javascript代码?

这是服务器端Scala代码片段,它以一种需要5分钟的方式调用Nashorn:(当运行200次时;我正在将许多注释从CommonMark编译为HTML。)(此代码基于这篇博客文章。)

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次。我确实测试了一个只评估过一次的版本,但很明显,后来我写了一些错误,因为只测试过一次版本的速度不超过5分钟,尽管它应该是最快的版本之一,请参阅Halfbit的答案。这是快速版本:

...
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秒。这大约和我的Java7版本一样快,该版本使用Rhino编译的Javascript代码到Java字节码(作为构建过程中的一个单独而复杂的步骤)。也许这是最快的速度?

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

使用CompiledScript的代码变体似乎对remarkable.min.js进行了200次重新评估,而基于eval的版本只进行了一次评估。这就解释了运行时的巨大差异。

只预编译了remarkable.render(__source__),基于CompiledScript的变体比基于evalinvokeFunction的变体(在我的机器Oracle Java 8u25上)稍微快一些。

CompiledScript在8u40中得到了一些改进。您可以下载jdk8u40@的早期访问下载https://jdk8.java.net/download.html