2011-08-28 12 views
15

Ho del codice Clojure che simula e quindi elabora dati numerici. I dati sono fondamentalmente vettori di valori doppi; l'elaborazione coinvolge principalmente la somma dei loro valori in vari modi. Includerò del codice qui sotto, ma la mia domanda è (penso) più generale - semplicemente non ho la minima idea di come interpretare i risultati dell'hprof.Per favore aiutami a capire queste tracce di Hclof Clojure

Comunque, il mio codice di prova è:

(defn spin [n] 
    (let [c 6000 
     signals (spin-signals c)] 
     (doseq [_ (range n)] (time (spin-voxels c signals))))) 

(defn -main [] 
    (spin 4)) 

dove spin-voxels dovrebbe essere più costoso di spin-signals (soprattutto se ripetuta più volte). Posso dare le routine di livello inferiore, ma penso che questa domanda riguardi di più il fatto che non capisco le basi delle tracce (sotto).

Quando compilo questo con Lein e poi fare qualche semplice profiling:

> java -cp classes:lib/clojure-1.3.0-beta1.jar -agentlib:hprof=cpu=samples,depth=10,file=hprof.vec com.isti.compset.stack 
"Elapsed time: 14118.772924 msecs" 
"Elapsed time: 10082.015672 msecs" 
"Elapsed time: 9212.522973 msecs" 
"Elapsed time: 12968.23877 msecs" 
Dumping CPU usage by sampling running threads ... done. 

e la traccia del profilo assomiglia:

CPU SAMPLES BEGIN (total = 4300) Sun Aug 28 15:51:40 2011 
rank self accum count trace method 
    1 5.33% 5.33%  229 300791 clojure.core$seq.invoke 
    2 5.21% 10.53%  224 300786 clojure.core$seq.invoke 
    3 5.05% 15.58%  217 300750 clojure.core$seq.invoke 
    4 4.93% 20.51%  212 300787 clojure.lang.Numbers.add 
    5 4.74% 25.26%  204 300799 clojure.core$seq.invoke 
    6 2.60% 27.86%  112 300783 clojure.lang.RT.more 
    7 2.51% 30.37%  108 300803 clojure.lang.Numbers.multiply 
    8 2.42% 32.79%  104 300788 clojure.lang.RT.first 
    9 2.37% 35.16%  102 300831 clojure.lang.RT.more 
    10 2.37% 37.53%  102 300840 clojure.lang.Numbers.add 

che è piuttosto fresco. Fino a qui, sono felice. Vedo che sto perdendo tempo con la gestione generica dei valori numerici.

Così guardo il mio codice e decido che, come primo passo, mi sostituisco vec con d-vec:

(defn d-vec [collection] 
    (apply conj (vector-of :double) collection)) 

non sono sicuro che sarà sufficiente - Ho il sospetto che sarà anche bisogno di aggiungere alcune annotazioni di tipo in vari punti - ma sembra un buon inizio. Quindi compilo e profilo di nuovo:

> java -cp classes:lib/clojure-1.3.0-beta1.jar -agentlib:hprof=cpu=samples,depth=10,file=hprof.d-vec com.isti.compset.stack 
"Elapsed time: 15944.278043 msecs" 
"Elapsed time: 15608.099677 msecs" 
"Elapsed time: 16561.659408 msecs" 
"Elapsed time: 15416.414548 msecs" 
Dumping CPU usage by sampling running threads ... done. 

Ewww. Quindi è molto più lento. E il profilo?

CPU SAMPLES BEGIN (total = 6425) Sun Aug 28 15:55:12 2011 
rank self accum count trace method 
    1 26.16% 26.16% 1681 300615 clojure.core.Vec.count 
    2 23.28% 49.45% 1496 300607 clojure.core.Vec.count 
    3 7.74% 57.18%  497 300608 clojure.lang.RT.seqFrom 
    4 5.59% 62.77%  359 300662 clojure.core.Vec.count 
    5 3.72% 66.49%  239 300604 clojure.lang.RT.first 
    6 3.25% 69.74%  209 300639 clojure.core.Vec.count 
    7 1.91% 71.66%  123 300635 clojure.core.Vec.count 
    8 1.03% 72.68%  66 300663 clojure.core.Vec.count 
    9 1.00% 73.68%  64 300644 clojure.lang.RT.more 
    10 0.79% 74.47%  51 300666 clojure.lang.RT.first 
    11 0.75% 75.22%  48 300352 clojure.lang.Numbers.double_array 
    12 0.75% 75.97%  48 300638 clojure.lang.RT.more 
    13 0.64% 76.61%  41 300621 clojure.core.Vec.count 
    14 0.62% 77.23%  40 300631 clojure.core.Vec.cons 
    15 0.61% 77.84%  39 300025 java.lang.ClassLoader.defineClass1 
    16 0.59% 78.43%  38 300670 clojure.core.Vec.cons 
    17 0.58% 79.00%  37 300681 clojure.core.Vec.cons 
    18 0.54% 79.55%  35 300633 clojure.lang.Numbers.multiply 
    19 0.48% 80.03%  31 300671 clojure.lang.RT.seqFrom 
    20 0.47% 80.50%  30 300609 clojure.lang.Numbers.add 

Ho incluso più righe qui perché questa è la parte che non capisco.

Perché su terra è Vec.count visualizzato così spesso? È un metodo che restituisce la dimensione del vettore. Una singola linea di ricerca di un attributo.

Suppongo di essere più lento perché sto ancora saltando avanti e indietro tra Double e Double, e che le cose potrebbero migliorare ancora quando aggiungo più annotazioni di tipo. Ma non capisco quello che ho adesso, quindi non sono così sicuro che fungere da sbalordimento abbia molto senso.

Per favore, qualcuno può spiegare la discarica sopra in termini generali? Prometto di non chiamare ripetutamente lo count - invece ho un sacco di mappe e riduzioni e alcuni cicli espliciti.

Mi chiedevo se potrei essere confuso dalla JIT? Forse mi manca una pila di informazioni perché le funzioni sono in linea? Oh, e sto usando 1.3.0-beta1 perché sembra avere una gestione dei numeri più ragionevole.

[UPDATE ] ho riassunto le mie esperienze a http://www.acooke.org/cute/Optimising1.html - ha ottenuto un aumento di velocità 5x (in realtà era 10 volte dopo la pulizia un po 'di più e passare alla 1.3), nonostante non capire questo.

+0

'(in (vector-of: double) collection)' può funzionare meglio per d-vec, ed è idiomatico –

+0

ah, grazie. Ho finito per riscriverlo per usare matrici mutevoli come descritto su http://www.acooke.org/cute/Optimising1.html ma potrebbe funzionare al di fuori del ciclo centrale. –

+0

ok, ho provato a sostituire alcuni doppi array che erano immutabili con i doppi vettori usando il tuo suggerimento, ma si scopre che non c'è modo (che io possa trovare) di aggiungere suggerimenti tipo per loro, e senza suggerimenti ricevo avvertimenti dinamici (e codice lento) in cui vengono utilizzati i dati. –

risposta

1

chiamando seq per un oggetto Vec (oggetto creato da vector-of) crea un oggetto VecSeq.

Oggetto VecSeq creato su Veca chiama Vec.count nel suo metodo internal-reduce, che viene utilizzato da clojure.core/reduce.

quindi sembra che un vettore creato dal vettore di chiamate Vec.count mentre si riduce. E come lei ha ricordato che il codice ha fatto un sacco di ridurre questo sembra essere la causa

Ciò che rimane inquietante è che Vec.count è che Vec.count sembra essere molto semplice:

clojure.lang.Counted 
    (count [_] cnt) 

un semplice getter che non fa alcun conteggio.

+0

questo sembra rispondere alla mia domanda; non ho idea del motivo per cui non l'ho segnato correttamente quasi un anno fa. spiace/grazie. –

0

Basta parlare ad alta voce, sembra che il tuo codice stia effettuando molte conversioni avanti/indietro da/verso Seq.

Guardando RT.seqFrom, ciò richiede ArraySeq.createFromObject

if(array == null || Array.getLength(array) == 0) 
    return null; 

Quindi vorrei che essere che l'utilizzo di usi vec accesso vettore veloce, e che l'uso di D-vec è forzare l'uso di array e la chiamata a un Java lento. metodo lang.Array.getLength (che utilizza la riflessione ..)

+0

grazie per aver scavato nel codice - sembra esserne parte. tuttavia, non stavo aggiungendo * la routine 'd-vec' qui sopra, ma usandola per sostituire un semplice' vec'. quindi non capisco perché dovrebbe * cambiare * le cose - il mio obiettivo era semplicemente essere specifico riguardo al tipo. quindi presumo che 'vec' non sia equivalente a quello che ho fatto per' d-vec', ma in tal caso, che cos'è? –

Problemi correlati