2012-05-30 26 views
6

Come esercizio, ho riscritto il programma di esempio nel post del blog Gibbs sampler in various languages (revisited) di Darren Wilkinson.ottimizzazione del semplice programma di campionamento di Gibits Lisp comune

Il codice viene visualizzato di seguito. Questo codice viene eseguito sul mio (5 anni) la macchina in circa 53 secondi, usando SBCL 1.0.56, la creazione di un'immagine di base utilizzando BUILDAPP, e poi eseguirlo con

time ./gibbs > gibbs.dat 

Dato che questo era il modo i tempi sono stati calcolati per le altre lingue nel post, ho pensato di fare qualcosa di simile Il codice C nel post viene eseguito in circa 25 secondi. Mi piacerebbe provare e accelerare il codice Lisp se possibile.

############################## 
gibbs.lisp 
############################## 
(eval-when (:compile-toplevel :load-toplevel :execute) 
    (require :cl-rmath) (setf *read-default-float-format* 'double-float)) 

(defun gibbs (N thin) 
    (declare (fixnum N thin)) 
    (declare (optimize (speed 3) (safety 1))) 
    (let ((x 0.0) (y 0.0)) 
    (declare (double-float x y)) 
    (print "Iter x y") 
    (dotimes (i N) 
     (dotimes (j thin) 
    (declare (fixnum i j)) 
    (setf x (cl-rmath::rgamma 3.0 (/ 1.0 (+ (* y y) 4)))) 
    (setf y (cl-rmath::rnorm (/ 1.0 (+ x 1.0)) (/ 1.0 (sqrt (+ (* 2 x) 2)))))) 
     (format t "~a ~a ~a~%" i x y)))) 

(defun main (argv) 
    (declare (ignore argv)) 
    (gibbs 50000 1000)) 

Poi ho costruito l'eseguibile gibbs con chiamando sh gibbs.sh con gibbs.sh come

################## 
gibbs.sh 
################## 
buildapp --output gibbs --asdf-tree /usr/share/common-lisp/source/ --asdf-tree /usr/local/share/common-lisp/source/ --load-system cl-rmath --load gibbs.lisp --entry main 

ottengo 6 note compilatore quando si compila con SBCL 1.0.56, che riporto qui di seguito. Non sono sicuro di cosa fare con loro, ma sarei grato per qualsiasi suggerimento.

; compiling file "/home/faheem/lisp/gibbs.lisp" (written 30 MAY 2012 02:00:55 PM): 

; file: /home/faheem/lisp/gibbs.lisp 
; in: DEFUN GIBBS 
;  (SQRT (+ (* 2 X) 2)) 
; 
; note: unable to 
; optimize 
; due to type uncertainty: 
; The result is a (VALUES (OR (DOUBLE-FLOAT 0.0) (COMPLEX DOUBLE-FLOAT)) 
;       &OPTIONAL), not a (VALUES FLOAT &REST T). 

;  (/ 1.0d0 (SQRT (+ (* 2 X) 2))) 
; 
; note: unable to 
; optimize 
; due to type uncertainty: 
; The second argument is a (OR (DOUBLE-FLOAT 0.0) 
;        (COMPLEX DOUBLE-FLOAT)), not a (COMPLEX 
;                DOUBLE-FLOAT). 
; 
; note: forced to do static-fun Two-arg-/ (cost 53) 
;  unable to do inline float arithmetic (cost 12) because: 
;  The second argument is a (OR (DOUBLE-FLOAT 0.0) (COMPLEX DOUBLE-FLOAT)), not a DOUBLE-FLOAT. 
;  The result is a (VALUES (OR (COMPLEX DOUBLE-FLOAT) (DOUBLE-FLOAT 0.0)) 
;        &OPTIONAL), not a (VALUES DOUBLE-FLOAT &REST T). 

;  (CL-RMATH:RGAMMA 3.0d0 (/ 1.0d0 (+ (* Y Y) 4))) 
; 
; note: doing float to pointer coercion (cost 13) 

;  (SQRT (+ (* 2 X) 2)) 
; 
; note: doing float to pointer coercion (cost 13) 

;  (CL-RMATH:RNORM (/ 1.0d0 (+ X 1.0d0)) (/ 1.0d0 (SQRT (+ (* 2 X) 2)))) 
; 
; note: doing float to pointer coercion (cost 13) 
; 
; compilation unit finished 
; printed 6 notes 

; /home/faheem/lisp/gibbs.fasl written 
; compilation finished in 0:00:00.073 

UPDATE 1: Rainer Joswig's answer ha sottolineato che l'argomento del SQRT potrebbe essere negativa, che era la fonte delle note compilatore oscuri che stavo vedendo, e cioè

The result is a (VALUES (OR (DOUBLE-FLOAT 0.0) (COMPLEX DOUBLE-FLOAT)) 
    ;       &OPTIONAL), not a (VALUES FLOAT &REST T). 

Il compilatore si lamentava che dal momento che non sapeva se il valore dell'argomento era positivo, il risultato poteva essere un numero complesso. Poiché nell'esempio, il valore x è una variabile di esempio dalla distribuzione gamma, è sempre maggiore di 0. È stato utile indicato da Stephan nella mailing list dell'utente SBCL, (vedere il secondo messaggio nella thread "Optimizing a simple Common Lisp Gibbs sampler program", che questo potrebbe essere risolto dichiarando x per essere maggiore o pari a zero come segue,

(declare (type (double-float 0.0 *) x)) 

Vedere le Lisp Hyperspec comune per la relativa documentazione su FLOAT types e Interval Designators.

Thi s sembra accelerare un po 'il codice. Ora è affidabile al di sotto di 52 secondi, ma ancora, non molto di un guadagno. Questo lascia anche le note su

note: doing float to pointer coercion (cost 13) 

Se questo non è risolvibile per qualche motivo, mi piacerebbe sapere perché. Inoltre, una spiegazione di ciò che significa la nota sarebbe interessante, a prescindere. In particolare, che significato ha la parola pointer qui? Questo è legato al fatto che vengono chiamate le funzioni C? Inoltre, il costo 13 non sembra molto utile. Cosa viene misurato?

Inoltre, Rainer ha suggerito che potrebbe essere possibile ridurre l'inclinazione, che potrebbe ridurre il runtime. Non so se sia possibile una riduzione del consumo, o se ridurrebbe il tempo di esecuzione, ma sarei interessato a opinioni e approcci. Nel complesso, sembra che non si possa fare molto per migliorare le prestazioni di questa funzione. Forse è troppo piccolo e semplice.

risposta

4

Nota che Common Lisp ha un operatore speciale THE. Permette di dichiarare i tipi per i risultati di espressioni. Questo ad esempio consente di restringere i tipi, se possibile.

Ad esempio, qual è il risultato di (SQRT somefloat)? Può essere un float, ma potrebbe essere un numero complesso se somefloat è negativo. Se sai che somefloat è sempre positivo (e solo allora), allora potresti scrivere (the double-float (sqrt somefloat)). Il compilatore potrebbe quindi essere in grado di generare un codice più efficiente.

Si noti inoltre che Common Lisp contiene dichiarazioni OPTIMIZE. Se vuoi il codice più veloce devi assicurarti di averlo impostato di conseguenza. Forse solo per le singole funzioni. Di solito è meglio che cambiare l'ottimizzazione a livello globale per essere molto aggressivi.

Common Lisp ha una funzione DISASSEMBLE che consente di visualizzare il codice compilato.

Quindi c'è la macro TIME. Le informazioni interessanti che ottieni includono quanto costa. Con l'aritmetica a doppio galleggiamento c'è probabilmente una grande quantità di guadagni. Sarebbe utile chiedere la mailing list SBCL per aiuto. Forse qualcuno può dirti come evitarlo.

+0

Ciao, Rainer. Grazie per il commento, ma speravo in qualcosa di più specifico. Sto già usando 'OPTIMIZE'. Ho provato a usare' THE', ma non vedo dove sarebbe utile se non sugli RHS dei compiti 'x' e' y', e in quei casi, mi aspetterei che il compilatore potrebbe dedurre che tutte quelle espressioni, che derivano dal doppio, sono esse stesse doppie. L'ho provato, ma non fa alcuna differenza evidente. Per mancanza di idee migliori, mi piacerebbe sbarazzarmi delle note del compilatore, ma non capisco cosa mi stanno dicendo. –

+0

Potrei anche provare il profiling, ma non sono sicuro di quanto sarebbe utile per un pezzo così piccolo di codice. Senza dichiarazioni speciali, ho ottenuto un po 'più di 1 minuto, e ora ho circa 52 secondi, quindi le dichiarazioni non hanno fatto molta differenza. –

+0

Ah, buon punto sulla radice quadrata. Ho perso questo. Grazie. –

2

questo funziona per me:

(sqrt (the (double-float 0d0) (+ (* 2d0 x) 2d0))) 
+1

Questo fa sparire gli avvisi sqrt, ma potresti aggiungere una spiegazione, per favore? Grazie. –