Как быстро сделать Java 8 Nashorn?
Я использую Java 8 Nashorn для рендеринга CommonMark на стороне HTML-сервера. Если я компилирую и кеширую и повторно использую CompiledScript
, для определенной страницы требуется 5 минут. Однако, если я вместо этого использую eval
, а кеш и повторно использую механизм script, рендеринг одной и той же страницы занимает 3 секунды.
Почему CompiledScript
так медленно? (пример кода следует)
Какой хороший подход для запуска Javascript-кода в Нашорне, снова и снова, как можно быстрее? И избегать компиляции кода Javascript более одного раза?
Это фрагмент кода на стороне сервера Scala, который вызывает Nashorn таким образом, который занимает 5 минут: (при запуске 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__);""")
...
/Edit
В то время как это занимает 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)
Update:
Я только заметил, что использование invokeFunction
в конце вместо eval
почти в два раза быстрее, занимает всего 1,7 секунды. Это примерно так же быстро, как и моя версия Java 7, в которой используется код Javascript, скомпилированный байт-кодом Rhino для Java (как отдельный и сложный шаг в процессе сборки). Возможно, это так же быстро, как может?
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
Вариант вашего кода, который использует CompiledScript
, кажется, переоценивает remarkable.min.js
200 раз - в то время как ваша версия на основе eval
делает это один раз. Это объясняет огромную разницу в времени выполнения.
Только с прекомпилированным remarkable.render(__source__)
, вариант CompiledScript
немного быстрее, чем те, на основе eval
и invokeFunction
(на моей машине, Oracle Java 8u25).
Ответ 2
Скомпилированный интерфейс был немного улучшен в 8u40. Вы можете скачать ранний доступ для загрузки jdk8u40 @https://jdk8.java.net/download.html