web-dev-qa-db-ja.com

Java 8 Nashornを高速にする方法は?

Java 8Nashornを使用して CommonMark をHTMLサーバー側にレンダリングしています。 CompiledScriptをコンパイルしてキャッシュし、再利用すると、特定のページのレンダリングに5分かかります。ただし、代わりにevalを使用し、スクリプトエンジンをキャッシュして再利用すると、同じページのレンダリングに3秒かかります。

CompiledScriptがとても遅いのはなぜですか? (サンプルコードは次のとおりです)

NashornでJavascriptコードをできるだけ速く何度も何度も実行するための良いアプローチは何ですか?そして、Javascriptコードを複数回コンパイルすることを避けますか?

これは、5分かかる方法でNashornを呼び出すサーバー側のScalaコードスニペットです:( 200回実行すると、CommonMarkからHTMLに多くのコメントをコンパイルします。)(このコードは thisに基づいていますブログ記事 。)

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上記の$scriptは200回再評価されることに注意してください。私はそれを一度だけ評価したバージョンをテストしましたが、それは明らかにそれからいくつかのバグを書きました、なぜならそれは最速のものの1つであるはずでしたが、一度だけのバージョンは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を使用すると、ほぼ2倍の速度で、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**")
18
KajMagnus

CompiledScriptを使用するコードのバリアントは、_remarkable.min.js_を200回再評価しているようですが、evalベースのバージョンはこれを1回実行します。これは、ランタイムの大きな違いを説明しています。

remarkable.render(__source__)だけがプリコンパイルされているため、CompiledScriptベースのバリアントはevalおよびinvokeFunctionベースのバリアントよりもわずかに高速です(私のマシンでは、Oracle Java 8u25)。

8
halfbit

CompiledScriptは8u40で少し改善されました。 jdk8u40 @ https://jdk8.Java.net/download.html の早期アクセスダウンロードをダウンロードできます。

2
A. Sundararajan