19

我正在使用 Java 8 Nashorn 将CommonMark呈现到 HTML 服务器端。如果我编译并缓存并重用 a 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 秒。这与我使用 Rhino 编译为 Java 字节码的 Javascript 代码的 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**")
4

2 回答 2

8

您使用的代码变体CompiledScript似乎重新评估了remarkable.min.js200 次 - 而您eval的基础版本只执行了一次。这解释了运行时的巨大差异。

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

于 2014-10-26T20:37:38.393 回答
2

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

于 2014-10-26T15:20:05.350 回答