Как быстро сделать 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