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**")
CompiledScript
を使用するコードのバリアントは、_remarkable.min.js
_を200回再評価しているようですが、eval
ベースのバージョンはこれを1回実行します。これは、ランタイムの大きな違いを説明しています。
remarkable.render(__source__)
だけがプリコンパイルされているため、CompiledScript
ベースのバリアントはeval
およびinvokeFunction
ベースのバリアントよりもわずかに高速です(私のマシンでは、Oracle Java 8u25)。
CompiledScriptは8u40で少し改善されました。 jdk8u40 @ https://jdk8.Java.net/download.html の早期アクセスダウンロードをダウンロードできます。