简体   繁体   English

如何使Java 8 Nashorn快速?

[英]How make Java 8 Nashorn fast?

I'm using Java 8 Nashorn to render CommonMark to HTML server side. 我正在使用Java 8 Nashorn将CommonMark呈现给HTML服务器端。 If I compile and cache and reuse a CompiledScript , a certain page takes 5 minutes to render. 如果我编译并缓存并重用CompiledScript ,则某个页面需要5分钟才能呈现。 However, if I instead use eval , and cache and reuse the script engine, rendering the same page takes 3 seconds. 但是,如果我改为使用eval ,并缓存eval用脚本引擎,则渲染同一页面需要3秒钟。

Why is CompiledScript so slow? 为什么CompiledScript这么慢? (sample code follows) (示例代码如下)

What's a good approach for running Javascript code in Nashorn, over and over again as quickly as possible? 什么是在Nashorn中运行Javascript代码的好方法,一次又一次地反复运行? And avoiding compiling the Javascript code more than once? 并避免不止一次编译Javascript代码?

This is the server side Scala code snippet that calls Nashorn in a way that takes 5 minutes: (when run 200 times; I'm compiling many comments from CommonMark to HTML.) (This code is based on this blog article .) 这是服务器端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()

Edit Note that the $script above is reevaluated 200 times. 编辑请注意,上面的$script会重新评估200次。 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 . 我测试了一个只评估过一次的版本,但显然我写了一些bug,因为只有一次版本并不快于5分钟,虽然它应该是最快的版本之一, 请参阅Halfbit的答案 Here's the fast version: 这是快速版本:

...
val newCompiledScript = newEngine.asInstanceOf[js.Compilable].compile(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) 这需要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)")

I would actually have guessed that the CompiledScript version (the topmost snippet) would have been faster. 我实际上已经猜到CompiledScript版本(最顶层的代码片段)会更快。 Anyway, I suppose I'll have to cache the rendered HTML server side. 无论如何,我想我必须缓存渲染的HTML服务器端。

(Linux Mint 17 & Java 8 u20) (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. 我只是注意到在末尾使用invokeFunction而不是eval几乎快两倍,只需1.7秒。 This is roughly as fast as my Java 7 version that used Javascript code compiled by Rhino to Java bytecode (as a separate and complicated step in the build process). 这与使用Rhino编译的Javascript代码到Java字节码的Java 7版本大致一样快(作为构建过程中的一个单独且复杂的步骤)。 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**")

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. 使用CompiledScript的代码变体似乎重新评估了remarkable.min.js 200次 - 而基于eval的版本只执行了一次。 This explains the huge difference in runtimes. 这解释了运行时的巨大差异。

With just the remarkable.render(__source__) precompiled, the CompiledScript based variant is slightly faster than the eval and invokeFunction based ones (on my machine, Oracle Java 8u25). 只使用预编译的remarkable.render(__source__) ,基于CompiledScript的变体比基于evalinvokeFunction的变体略快(在我的机器上,Oracle Java 8u25)。

CompiledScript has been improved a bit in 8u40. CompiledScript在8u40中有所改进。 You can download early access download of jdk8u40 @ https://jdk8.java.net/download.html 您可以下载jdk8u40 @ https://jdk8.java.net/download.html的早期访问下载

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM