2015-02-21 8 views
10

Mam 2 funkcje do określania liczby numerycznej w Julia. Druga funkcja (która według mnie jest wektoryzowana) jest wolniejsza od pierwszej. Dlaczego wektoryzacja jest wolniejsza? Czy istnieją zasady dotyczące wektoryzacji, a kiedy nie?kiedy wektoryzacja jest preferowana w Julii?

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 

a druga funkcja

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

Po uruchomieniu dla n = 10^7, poniżej przedstawiono czasy wykonawcze (po działa kilka razy)

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) 

Odpowiedz

8

wektoryzacji jest, jeśli

  • Ułatwia odczyt kodu, a wydajność nie jest równa ritical
  • Jeśli jest to operacja algebry liniowej, użycie stylu wektorowego może być dobre, ponieważ Julia może używać BLAS i LAPACK do wykonywania operacji za pomocą bardzo wyspecjalizowanego, wysokowydajnego kodu.

Ogólnie uważam, że najlepiej zacząć od wektoryzacji kodu, szukać problemów z szybkością, a następnie opracować wszelkie kłopotliwe problemy.

Twój drugi kod jest powolny nie tyle ze względu na jego wektoryzację, ale ze względu na użycie anonimowej funkcji: niestety w Julii 0.3 są one zwykle nieco wolniejsze. map generalnie nie działa zbyt dobrze, wierzę, ponieważ Julia nie może wywnioskować typu wyjściowego funkcji (jej wciąż "anonimowy" z perspektywy funkcji map). Napisałem inną vectorized wersję, która unika funkcje anonimowe, i to prawdopodobnie nieco łatwiejsze do odczytania:

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

Benchmarking z

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) 

daje mi wyniki

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) 

tak vectorized W niektórych przypadkach kod może być równie dobry, jak devectorized, nawet jeśli nie jesteśmy w przypadku linearno-algebry.

+0

Dzięki. Zmieniłem drugi kod, aby usunąć funkcję anonimową, ale jej nadal działa źle: funkcja isInside (x) x <= 1? 1: 0 koniec; funkcja determine_pi_vec (n) res = cumsum (mapa (isInside, rand (n).^2 + rand (n).^2)) ./ [1: n]; return res koniec; @time returnArray2 = determine_pi_vec (n); #czasowy czas: 2.303632751 sekund (880001336 przydzielonych bajtów, 31.88% czasu gc) –

+3

Anonimowa funkcja nie jest największym problemem - bardziej jest, że 'map' nie wstawia argumentów funkcji i generuje wyspecjalizowany kod, podczas gdy unikając mapy, Kontrola isInside może być zaznaczona. – StefanKarpinski

+0

Hah Tak, właśnie zaktualizowałem swoją odpowiedź, próbując wyjaśnić ten punkt: – IainDunning