2014-10-25 3 views
16

Ich benutze Java 8 Nashorn, um CommonMark HTML-Server-Seite zu rendern. Wenn ich eine CompiledScript kompiliere und zwischenspeichern und wiederverwende, dauert die Wiedergabe einer bestimmten Seite 5 Minuten. Wenn ich stattdessen eval verwende und die Skript-Engine zwischenspeichern und wiederverwende, dauert das Rendern derselben Seite 3 Sekunden.Wie macht Java 8 Nashorn schnell?

Warum ist CompiledScript so langsam? (Beispielcode folgt)

Was ist ein guter Ansatz für die Ausführung von Javascript-Code in Nashorn, immer wieder so schnell wie möglich? Und vermeiden, den Javascript-Code mehr als einmal zu kompilieren?

Dies ist das serverseitige Scala-Code-Snippet, das Nashorn in einer Weise aufruft, die 5 Minuten dauert: (wenn 200 Mal ausgeführt; Ich erstelle viele Kommentare von CommonMark zu HTML.) (Dieser Code basiert auf 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() 

bearbeiten Beachten Sie, dass die $script über 200 mal neu bewertet wird. Ich habe eine Version getestet, die es nur einmal ausgewertet hat, aber anscheinend habe ich dann einen Bug geschrieben, weil die einzige Version nicht schneller als 5 Minuten war, obwohl sie eine der schnellsten sein sollte, see Halfbit's answer. Hier ist die schnelle Version:

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

/bearbeiten

Während dieser 2,7 Sekunden dauert: (wenn 200-mal laufen)

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)") 

Ich hätte eigentlich gedacht, dass die CompiledScript Version (die oberste Schnipsel) wäre schneller gewesen. Wie auch immer, ich werde wohl die Seite des gerenderten HTML-Servers zwischenspeichern müssen.

(Linux Mint 17 & Java 8 u20)

Update:

Ich habe gerade bemerkt, dass invokeFunction am Ende statt eval mit fast doppelt so schnell ist, dauert nur 1,7 Sekunden. Das ist ungefähr so ​​schnell wie meine Java 7-Version, die Javascript-Code verwendet, der von Rhino zu Java-Bytecode kompiliert wurde (als separater und komplizierter Schritt im Build-Prozess). Vielleicht ist das so schnell wie es geht?

+0

Ich habe auch Nashorn gefunden, um langsamer als Nashorn zu sein http://softwarecenturion.me/posts/2014-04-07-jdk8-nashorn-performance/. Es wird viel schneller mit warmem JIT, aber es ist unbrauchbar langsam kalt. Ich bin auch neugierig, ob du irgendetwas dagegen tun kannst. – coudy

Antwort

6

Die Variante des Codes, die CompiledScript verwendet scheint remarkable.min.js 200 Mal neu zu bewerten - während eval basierte Version dieses eine Mal der Fall ist. Dies erklärt den enormen Unterschied in den Laufzeiten.

Mit nur remarkable.render(__source__) vorkompilierte, die CompiledScript basierte Variante ist etwas schneller als die eval und invokeFunction basiert sind (auf meinem Rechner, Oracle Java 8u25).

+0

Dies scheint die Erklärung zu sein. Ich habe eine Alternative getestet, die "answer.min.js" nicht mehr als einmal neu bewertet hat, aber ich fand, dass es auch 5 Minuten dauert, also habe ich es nicht in die Frage aufgenommen. Allerdings musste ich einen Fehler gemacht haben (nämlich versehentlich eine Variable auf null zurückzusetzen, anstatt sie einfach zu deklarieren), denn wenn ich nun ein 'CompiledScript' testete, das' bemerkenswert.min.js' nicht neu bewertet hat, ist es ungefähr so schnell wie die schnelle 'eval' Version (weiß nicht welche auf meinem Computer am schnellsten ist). – KajMagnus

+0

Ich habe die Frage mit der schnellen 'CompiledScript'-Version aktualisiert - es sieht ungefähr so ​​aus wie die Version, die du benutzt hast, hoffe ich? Ich bin ein wenig unsicher darüber, welche Variablen sich Nashorn nach den folgenden 'eval'-Aufrufen erinnert, aber anscheinend erinnert es sich an Variablen auf der obersten Ebene wie' global' und 'bemerkenswert'. – KajMagnus

+0

Eigentlich habe ich gerade den 'bemerkenswerten.render (__ source __)' vorkompiliert. Alle Scriptvariablen der obersten Ebene landen im Engine-Bereich und werden von jedem 'eval', vorkompiliert oder nicht, erneut verwendet (und eventuell modifiziert). (Ich habe tatsächlich überprüft, dass die Bindungen nach der Ausführung des Skripts mit eval und vorkompilierter Variante gleich sind.) – halfbit

2

CompiledScript wurde in 8u40 etwas verbessert. Sie können @ Early-Access-Download von jdk8u40 herunterladen https://jdk8.java.net/download.html

+1

das beantwortet die Frage allerdings nicht wirklich. – eis