2014-10-25 13 views
16

Używam Java 8 Nashorn do renderowania CommonMark po stronie serwera HTML. Jeśli kompiluję i buforuję i ponownie używam CompiledScript, pewna strona zajmuje 5 minut. Jednakże, jeśli zamiast tego użyję eval, i buforowania i ponownego użycia silnika skryptu, renderowanie tej samej strony trwa 3 sekundy.Jak szybko zrobić Java 8 Nashorn?

Dlaczego tak wolny jest CompiledScript? (przykładowy kod jest podany)

Jakie jest dobre podejście do uruchamiania kodu JavaScript w Nashorn tak szybko, jak to możliwe? I unikanie kompilowania kodu Javascript więcej niż raz?

To jest fragment kodu Scala po stronie serwera, który wywołuje Nashorn w sposób trwający 5 minut: (gdy uruchamiam 200 razy, kompiluję wiele komentarzy od CommonMark do HTML.) (Ten kod jest oparty na this blog article.)

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 Należy zauważyć, że powyższe $script jest reevaluated 200 razy. Testowałem wersję, która oceniła ją tylko raz, ale najwyraźniej napisałem jakiś błąd, ponieważ jedyna wersja nie była szybsza niż 5 minut, chociaż powinna być jedną z najszybszych, see Halfbit's answer. Oto szybka wersja:

... 
val newCompiledScript = newEngine.asInstanceOf[js.Compilable].compile(s""" 
    var global; 
    var remarkable; 
    if (!remarkable) { 
    global = this; 
    $script; 
    remarkable = new Remarkable({}); 
    } 
    remarkable.render(__source__);""") 
... 

/Edycja

niniejsza trwa 2,7 sekundy (w przypadku uruchomienia 200 razy)

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 rzeczywiście się domyślić, że wersja CompiledScript (the najwyższy fragment) byłaby szybsza. W każdym razie, przypuszczam, że będę musiał buforować stronę renderowanego serwera HTML.

(Linux Mint 17 & Java 8 u20)

Aktualizacja:

Właśnie zauważyłem, że przy użyciu invokeFunction na końcu zamiast eval jest prawie dwa razy szybciej, zajmuje tylko 1,7 sekundy. Jest to mniej więcej tak szybkie, jak moja wersja Java 7, która wykorzystywała kod JavaScript skompilowany przez Rhino do kodu bajtowego Java (jako osobny i skomplikowany krok w procesie kompilacji). Być może jest to tak szybko, jak to możliwe?

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**") 
+0

Znalazłem również nashorn wolniej niż rhino http://softwarecenturion.me/posts/2014-04-07-jdk8-nashorn-performance/. Staje się znacznie szybszy przy ciepłym JIT, ale jest wyjątkowo powolny na zimno. Ciekawe, czy jest coś, co możesz z tym zrobić. – coudy

Odpowiedz

6

Wariant z kodem, który wykorzystuje CompiledScript wydaje się przewartościować remarkable.min.js 200 razy - w czasie, gdy eval wersja oparta robi to raz. Wyjaśnia to ogromną różnicę w środowiskach wykonawczych.

Wystarczy tylko remarkable.render(__source__) prekompilowanego wariant bazie CompiledScript jest nieco szybciej niż te, opartych eval i invokeFunction (na moim komputerze, Oracle Java 8u25).

+0

To wydaje się być wyjaśnieniem. Zrobiłem test alternatywny, który nie przewartościował 'remarkable.min.js' więcej niż raz, ale okazało się, że zajmuje to również 5 minut, więc nie uwzględniłem go w pytaniu. Jednak musiałem popełnić jakiś błąd (mianowicie przypadkowo zresetować zmienną do wartości null, a nie tylko ją ogłosić), ponieważ teraz, gdy ponownie przetestowałem "CompiledScript", który nie przeszedł reewaluacji 'remarkable.min.js', jest to mniej więcej tak samo jak szybka jak szybka wersja 'eval' (nie wiem, która z nich jest najszybsza na moim komputerze). – KajMagnus

+0

Zaktualizowałem pytanie szybką wersją "CompiledScript" - wygląda mniej więcej na wersję, której użyłeś Mam nadzieję? Nie mam pewności, jakie zmienne Nashorn zapamiętuje po kolejnych wywołaniach 'eval', ale najwyraźniej pamięta on zmienne najwyższego poziomu, takie jak' global' i 'godne uwagi'. – KajMagnus

+0

Właściwie to właśnie skompilowałem 'remarkable.render (__ source __)'. Wszystkie zmienne skryptu najwyższego poziomu kończą się zasięgiem silnika i są ponownie używane (i ewentualnie modyfikowane) przez każdy "eval", prekompilowany lub nie. (Faktycznie zweryfikowałem, że wiązania są równe po wykonaniu skryptu z eval i prekompilowanym wariantem). – halfbit