如何使 Java 8 Nashorn 快速运行?

2022-09-02 13:59:05

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

为什么这么慢?(示例代码如下)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()

编辑请注意,上述内容被重新评估了200次。我确实测试了一个只评估过一次的版本,但显然我写了一些错误,因为唯一一次的版本不会超过5分钟,尽管它应该是最快的版本之一,请参阅Halfbit的答案。这是快速版本:$script

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

我实际上会猜到版本(最上面的片段)会更快。无论如何,我想我必须缓存呈现的HTML服务器端。CompiledScript

(Linux Mint 17 & Java 8 u20)

更新:

我刚刚注意到,在最后使用而不是几乎是两倍的速度,只需要1.7秒。这与我的Java 7版本一样快,该版本使用由Rhino编译的Javascript代码到Java字节码(作为构建过程中的一个单独且复杂的步骤)。也许这是它所能得到的一样快?invokeFunctioneval

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

答案 1

使用的代码变体似乎重新评估了200次 - 而您的基于版本只这样做一次。这解释了运行时的巨大差异。CompiledScriptremarkable.min.jseval

仅使用预编译,基于的变体比基于的变体(在我的机器上,Oracle Java 8u25)略快。remarkable.render(__source__)CompiledScriptevalinvokeFunction


答案 2

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