2014-12-28 19 views
7

Ho pensato che in Julia (a differenza di R o Matlab) il codice devectorizzato era spesso più veloce del codice vettoriale. Ma non sto trovando questo il caso. Ecco un esempio:Perché questo codice Julia devectorizzato oltre 20 volte è troppo lento?

julia> x = Float64[1:10000000]; 

julia> y = Array(Float64, length(x)); 

julia> @time for i = 1:length(x) y[i] = exp(x[i]) end; 
elapsed time: 7.014107314 seconds (959983704 bytes allocated, 25.39% gc time) 

julia> @time y = exp(x); 
elapsed time: 0.364695612 seconds (80000128 bytes allocated) 

Perché il codice vettoriale è molto più veloce? Sembra che il codice devectorizzato stia allocando più di 10 volte la memoria. Ma solo pochi byte devono essere allocati per esponenziare qualsiasi numero di float. C'è un modo per scrivere il codice devectorizzato in modo che non assegni così tanta memoria, e quindi funzioni più velocemente del codice vettoriale?

Grazie!

+0

"_Io ho pensato ..._" Dovresti sempre fornire delle prove per supportare affermazioni di questo tipo. – csmckelvey

+2

Certo, ecco un post che dimostra quanto può essere più veloce il codice devectorizzato in Julia: http://www.johnmyleswhite.com/notebook/2013/12/22/the-relationship-between-vectorized-and-devectorized-code/ – Jeff

risposta

10

Si consideri il seguente frammento di codice:

x = Float64[1:10000000]; 
y = Array(Float64, length(x)); 
function nonglobal_devec!(x,y) 
    for i = 1:length(x) y[i] = exp(x[i]) end 
end 
function nonglobal_vec(x) 
    exp(x) 
end 
@time nonglobal_devec!(x,y); 
@time y = nonglobal_vec(x); 
x = Float64[1:10000000]; 
y = Array(Float64, length(x)); 
@time for i = 1:length(x) y[i] = exp(x[i]) end 
@time y = exp(x) 

che dà i tempi

A: elapsed time: 0.072701108 seconds (115508 bytes allocated) 
B: elapsed time: 0.074584697 seconds (80201532 bytes allocated) 
C: elapsed time: 2.029597656 seconds (959990464 bytes allocated, 22.86% gc time) 
D: elapsed time: 0.058509661 seconds (80000128 bytes allocated) 

L'uno strano, C, è due to it operating in the global scope, dove l'inferenza di tipo non funziona, e s codice inferiore è generato.

I tempi relativi tra A e B sono soggetti a qualche variabilità a causa delle funzioni che vengono compilate la prima volta che vengono utilizzate. Se si corre nuovamente otteniamo

A2: elapsed time: 0.038542212 seconds (80 bytes allocated) 
B2: elapsed time: 0.063630172 seconds (80000128 bytes allocated) 

che ha senso come A2 non alloca memoria (che 80 byte è per il valore di ritorno della funzione), e B2 crea un nuovo vettore. Si noti inoltre che B2 assegna la stessa quantità di memoria di D - l'extra la prima volta è stata allocata memoria per la compilazione.

Infine, devectorized vs vectorized è un caso per caso. Ad esempio, se hai implementato la moltiplicazione della matrice in modo ingenuo con i cicli for e senza la consapevolezza della cache, è probabile che tu sia molto più lento rispetto all'utilizzo dello A*b vettorizzato che utilizza BLAS.

+2

Grazie, Iain, questa è una risposta molto migliore della mia. – StefanKarpinski

+0

Ho intenzione di dirottare questo thread un po 'per chiedere se gli 80 byte in A2 sono allocati ogni volta che viene chiamata la funzione? Ad esempio, se quella funzione è in un ciclo for che la chiama 10^5 volte, verranno allocati 80 * 10^5 byte? – Nick

+0

Piuttosto sicuro che non sarebbe, che è facile da controllare - è solo una cosa REPL. Però le cose sono strane al REPL! Se dovessi mettere quel comando in un loop, al REPL, penso che probabilmente avresti 80 byte, il che sarebbe il valore dell'ultima volta del ciclo. – IainDunning