2009-05-28 16 views
12

O Groovy Guru,Groovy per il tempo di esecuzione del ciclo

Questo frammento di codice viene eseguito in circa 1 secondo

for (int i in (1..10000000)) { 
     j = i; 
    } 

mentre questo prende quasi 9 secondi

for (int i = 1; i < 10000000; i++) { 
     j = i; 
    } 

Perché è così?

risposta

9

Ok. Ecco la mia opinione sul perché?

Se si converte entrambi gli script in bytecode, si noterà che

  1. ForInLoop utilizza Range. Iterator viene utilizzato per avanzare durante ogni ciclo. Il confronto (<) viene effettuato direttamente su int (o Integer) per determinare se la condizione di uscita è stata soddisfatta o meno
  2. ForLoop utilizza l'incremento tradizionale, verifica la condizione ed esegue l'azione. Per verificare la condizione i < 10000000 utilizza Groovy's ScriptBytecodeAdapter .compareLessThan. Se si scava in profondità nel codice che di metodo, si trovano entrambi i lati del confronto è preso come oggetto e ci sono tante cose in corso, casting, confrontandoli come oggetto, ecc

ScriptBytecodeAdapter .compareLessThan - >ScriptBytecodeAdapter .compareTo ->DefaultTypeTransformation .compareTo

non ci sono altre classi in package che implementa typehandling metodo compareTo appositamente per i tipi di dati per la matematica, non so perché non vengono utilizzati, (se non sono utilizzati)

Sono sospettoso che questo sia il motivo per cui il secondo ciclo richiede più tempo. Ancora, per favore correggimi se ho torto o mi manca qualcosa ...

+0

Questo quasi certamente lo spiega. So che lo stile Java per ciclo offre maggiore flessibilità in ciò che si può fare, ma sicuramente avrebbero potuto applicare qualche ottimizzazione alla sua forma più basilare (e più comunemente usata) in modo che eseguisse sia il ciclo for..in? Questa è una piccola trappola per le prestazioni per le persone che arrivano da Java o C# ... – Xiaofu

+0

Nessuno si aspetta mai questa disparità in queste due operazioni. Aree in cui Groovy potrebbe migliorare, soprattutto perché un codice simile in Java viene eseguito in 300 mS. –

+1

+1 per osservare il bytecode. – Leonel

2

Durante i test, assicurarsi di "riscaldare" la JVM prima di prendere la misura, altrimenti è possibile che vengano attivate varie azioni di avvio nella piattaforma (caricamento della classe, compilazione JIT). Esegui i tuoi test più volte di seguito. Inoltre, se hai effettuato il secondo test mentre stava svolgendo un processo di raccolta dei dati, ciò potrebbe avere un impatto. Prova a eseguire ciascuno dei tuoi test 100 volte e stampare i tempi dopo ogni test, e guarda cosa ti dice.

1

Se è possibile eliminare i potenziali artefatti dal tempo di avvio come suggerisce Jim, azzarderei quindi un'ipotesi che lo stile Java per il ciclo in Groovy non sia così ben implementato come lo stile originale per ciclo di Groovy. È stato aggiunto solo a partire dalla v1.5 dopo le richieste degli utenti, quindi forse la sua implementazione è stata un po 'ripensata.

Hai dato un'occhiata al bytecode generato per i tuoi due esempi per vedere se ci sono delle differenze? C'è stata una discussione su prestazioni Groovy here in cui uno dei commenti (da un 'johnchase') dice:

Mi chiedo se la differenza avete visto legato al modo in Groovy utilizza i numeri (primitive) - dal momento che avvolge tutti i primitivi nelle loro equivalenti classi di wrapper Java (int -> Integer), immagino che rallenterebbero un po 'le cose. Sarei interessato a vedere le prestazioni del codice Java che esegue il ciclo di 10.000.000 utilizzando le classi wrapper invece di ints.

Quindi forse il loop originale di Groovy non ne risente? Solo speculazioni da parte mia, però.

+0

Xiaofu, questi sono buoni punti. La cosa che trovo confusa è che il ciclo in basso dovrebbe essere il più veloce, dal momento che si occupa solo di Ints, non di Integer (sebbene il tipo di j non sia specificato). Il ciclo superiore dovrebbe essere più lento, data la sequenza. –

Problemi correlati