2014-10-25 16 views
16

Sto usando Java 8 Nashorn per il rendering di CommonMark lato server HTML. Se compilo e cache e riusci uno CompiledScript, una determinata pagina impiega 5 minuti per il rendering. Tuttavia, se invece utilizzo eval e cache e riutilizza il motore di script, il rendering della stessa pagina richiede 3 secondi.Come rendere veloce Java 8 Nashorn?

Perché lo è CompiledScript così lento? (segue il codice di esempio)

Qual è un buon approccio per l'esecuzione del codice Javascript in Nashorn, più e più volte il più rapidamente possibile? E evitando di compilare il codice Javascript più di una volta?

Questo è lo snippet di codice Scala lato server che chiama Nashorn in un modo che richiede 5 minuti: (quando viene eseguito 200 volte, sto compilando molti commenti da CommonMark a HTML.) (Questo codice è basato su 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() 

Modifica si noti che il $script sopra si rivaluta 200 volte. Ho provato una versione che l'ha valutata solo una volta, ma a quanto pare ho scritto qualche bug, perché la versione solo una volta non era più veloce di 5 minuti, anche se avrebbe dovuto essere uno dei più veloci, see Halfbit's answer. Ecco la versione veloce:

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

/Modifica

considerando che la presente richiede 2,7 secondi: (quando viene eseguito 200 volte)

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

vorrei davvero hanno intuito che la versione CompiledScript (la il frammento più in alto) sarebbe stato più veloce. Ad ogni modo, suppongo che dovrò mettere in cache il lato del server HTML reso.

(Linux Mint 17 & Java 8 U20)

Aggiornamento:

Ho appena notato che usando invokeFunction alla fine, invece di eval è quasi due volte più veloce, richiede solo 1,7 secondi. Questo è più o meno veloce della mia versione di Java 7 che utilizzava il codice Javascript compilato da Rhino in bytecode Java (come un passaggio separato e complicato nel processo di compilazione). Forse questo è il più veloce che può arrivare?

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

Ho anche trovato che Nashorn è più lento del rhino http://softwarecenturion.me/posts/2014-04-07-jdk8-nashorn-performance/. Diventa molto più veloce con JIT caldo, ma è freddo insolitamente lento. Anche curioso se c'è qualcosa che puoi fare al riguardo. – coudy

risposta

6

La variante del codice che utilizza CompiledScript sembra rivalutare remarkable.min.js 200 volte - mentre la versione base eval fa questo una volta. Questo spiega l'enorme differenza di autonomia.

Con solo il remarkable.render(__source__) precompilata, la variante basata CompiledScript è leggermente più veloce rispetto a quelli a base di eval e invokeFunction (sulla mia macchina, Oracle Java 8u25).

+0

Questa sembra essere la spiegazione. Ho provato un'alternativa che non ha rivalutato 'remarkable.min.js 'più di una volta, ma ho trovato anche 5 minuti, quindi non l'ho incluso nella domanda. Tuttavia, ho dovuto fare un errore (vale a dire azzerando accidentalmente una variabile su null, piuttosto che semplicemente dichiarandola), perché ora quando ho ancora testato un 'CompiledScript' che non ha rivalutato' remarkable.min.js', è grosso modo come veloce come la veloce versione 'eval' (non so quale sia la più veloce sul mio computer). – KajMagnus

+0

Ho aggiornato la domanda con la veloce versione 'CompiledScript' - sembra approssimativamente come la versione che hai usato spero? Mi sento un po 'incerto su quali variabili Nashorn ricordi dopo le successive invocazioni di "eval", ma a quanto pare ricorda variabili di primo livello come "globale" e "notevole". – KajMagnus

+0

In realtà ho appena precompilato il 'remarkable.render (__ source __)'. Tutte le variabili di script di primo livello finiscono nello scope del motore e vengono riutilizzate (e eventualmente modificate) da ogni 'eval', precompilato o meno. (Ho effettivamente verificato che i collegamenti sono uguali dopo l'esecuzione di script con la variante eval e precompilata.) – halfbit

2

CompiledScript è stato migliorato un po 'in 8u40. È possibile scaricare scaricare accesso anticipato di jdk8u40 @https://jdk8.java.net/download.html

+1

questo in realtà non risponde alla domanda però. – eis

Problemi correlati