2015-02-21 15 views
10

Ho 2 funzioni per determinare il pi numericamente in Julia. La seconda funzione (che penso sia vettorializzata) è più lenta della prima. Perché la vettorizzazione è più lenta? Ci sono regole per il vettorializzare e quando no?quando la vettorizzazione è favorita in Julia?

function determine_pi(n) 
    area = zeros(Float64, n); 
    sum = 0; 
    for i=1:n 
     if ((rand()^2+rand()^2) <=1) 
      sum = sum + 1; 
     end 
      area[i] = sum*1.0/i; 
    end 
    return area 
end 

e un'altra funzione

function determine_pi_vec(n) 
    res = cumsum(map(x -> x<=1?1:0, rand(n).^2+rand(n).^2))./[1:n] 
    return res 
end 

Quando eseguito per n = 10^7, di seguito sono tempi di esecuzione (dopo aver eseguito alcune volte)

n=10^7 
@time returnArray = determine_pi(n) 
#output elapsed time: 0.183211324 seconds (80000128 bytes allocated) 
@time returnArray2 = determine_pi_vec(n); 
#elapsed time: 2.436501454 seconds (880001336 bytes allocated, 30.71% gc time) 

risposta

8

vettorizzazione è buona se

  • Rende il codice più facile da leggere e le prestazioni non sono c ritical
  • Se è un'operazione di algebra lineare, l'uso di uno stile vettoriale può essere utile perché Julia può utilizzare BLAS e LAPACK per eseguire l'operazione con codice altamente specializzato e ad alte prestazioni.

In generale, personalmente trovo il modo migliore di iniziare con il codice vettoriale, cercare eventuali problemi di velocità, quindi devetrizzare eventuali problemi problematici.

Il tuo secondo codice è lento non tanto per il suo vettore, ma per l'uso di una funzione anonima: sfortunatamente in Julia 0.3, questi sono normalmente un po 'più lenti. map in generale non funziona molto bene, credo perché Julia non può inferire il tipo di output della funzione (è ancora "anonima" dal punto di vista della funzione map). Ho scritto un vettorizzati versione diversa che evita funzioni anonime, ed è forse un po 'più facile da leggere:

function determine_pi_vec2(n) 
    return cumsum((rand(n).^2 .+ rand(n).^2) .<= 1) ./ (1:n) 
end 

Benchmarking con

function bench(n, f) 
    f(10) 
    srand(1000) 
    @time f(n) 
    srand(1000) 
    @time f(n) 
    srand(1000) 
    @time f(n) 
end 

bench(10^8, determine_pi) 
gc() 
bench(10^8, determine_pi_vec) 
gc() 
bench(10^8, determine_pi_vec2) 

mi dà i risultati

elapsed time: 5.996090409 seconds (800000064 bytes allocated) 
elapsed time: 6.028323688 seconds (800000064 bytes allocated) 
elapsed time: 6.172004807 seconds (800000064 bytes allocated) 
elapsed time: 14.09414031 seconds (8800005224 bytes allocated, 7.69% gc time) 
elapsed time: 14.323797823 seconds (8800001272 bytes allocated, 8.61% gc time) 
elapsed time: 14.048216404 seconds (8800001272 bytes allocated, 8.46% gc time) 
elapsed time: 8.906563284 seconds (5612510776 bytes allocated, 3.21% gc time) 
elapsed time: 8.939001114 seconds (5612506184 bytes allocated, 4.25% gc time) 
elapsed time: 9.028656043 seconds (5612506184 bytes allocated, 4.23% gc time) 

così vectorized il codice può essere sicuramente buono quanto devectorizzato in alcuni casi, anche quando non siamo in un caso di algebra lineare.

+0

Grazie. Ho modificato il secondo codice per rimuovere la funzione anonima ma il suo rendimento è ancora negativo: la funzione èInside (x) x <= 1? 1: 0 fine; function define_pi_vec (n) res = cumsum (mappa (isInside, rand (n).^2 + rand (n).^2)) ./ [1: n]; restituzioni fine; @time returnArray2 = define_pi_vec (n); # tempo trascorso: 2.303632751 secondi (880001336 byte allocati, 31.88% tempo gc) –

+3

La funzione anonima non è il problema più grande - è più che 'mappa' non esegue argomenti di funzione in linea e genera codice specializzato, mentre evitando la mappa, il controllo isInside può essere sottolineato. – StefanKarpinski

+0

Hah si Ho appena aggiornato la mia risposta per tentare di chiarire quel punto – IainDunning