2017-05-05 14 views

Odpowiedz

6

Jeśli pracujesz z matrycami, mogą być właściwe rozważyć x jako wektor macierzy zamiast tablicy 3D. Następnie można zrobić

x = [rand(6,6) for _ in 1:2^10] 
y = [rand(6)] 
z = x .* y 

z jest teraz wektorem wektorów.

A jeśli z jest zdefiniowanej przez, które byłyby

z .= x .* y 

A jeśli chcesz je bardzo szybko, używać wektorów StaticArrays

using StaticArrays 

x = [@SMatrix rand(6, 6) for _ in 1:2^10] 
y = [@SVector rand(6)] 
z = x .* y 

To jest pokazujące 10x przyspieszenie na moim komputerze z systemem w 12us.

+0

Jak działa "_" w zrozumieniu? –

+1

To po prostu sztuczna zmienna. Mógłbym użyć na przykład 'i', ale często można powiedzieć, że' _' oznacza zmienną jednorazową, która nie jest używana dalej i która nazwa jest nieważna. – DNF

7

mapslices(i->i*y, x, (1,2)) jest może „czystsze”, ale to będzie wolniejszy.

Przeczytaj jako: zastosuj funkcję "razy po Y" do każdego plastra z pierwszych dwóch wymiarów.

function tst(x,y) 
    z = zeros(6,1,2^10) 
    for i in 1:2^10 
     z[:,:,i] = x[:,:,i] * y 
    end 
    return z 
end 

tst2(x,y) = mapslices(i->i*y, x, (1,2)) 

time tst(x,y); 0.002152 sekund (4,10 k allocations: 624,266 KB)

@time tst2(x,y); 0.005720 sekund (13,36 k allocations: 466,969 KB)

+0

Zgadzam się, że rozwiązanie jest bardziej "czyste", ale z drugiej strony uważam też za mniej oczywiste, co się dzieje. –

+0

Może ... chociaż zgodziłbym się z @DNF, że jeśli chcesz po prostu powtórzyć ten wymiar, to wektor macierzy będzie bardziej czytelny. –

3

sum(x.*y',2) to czyste, krótkie rozwiązanie.

Ma również dobre właściwości prędkości i pamięci. Sztuką jest widok mnożenia macierzy-wektora jako liniowej kombinacji kolumn macierzy skalowanych przez elementy wektorowe. Zamiast wykonywać każdą liniową kombinację dla macierzy x [:,:, i], używamy tej samej skali y [i] dla x [:, i ,:]. W kodzie:

const x = rand(6,6,2^10); 
const y = rand(6,1); 
function tst(x,y) 
    z = zeros(6,1,2^10) 
    for i in 1:2^10 
     z[:,:,i] = x[:,:,i]*y 
    end 
    return z 
end 
tst2(x,y) = mapslices(i->i*y,x,(1,2)) 
tst3(x,y) = sum(x.*y',2) 

Benchmarking daje:

julia> using BenchmarkTools 
julia> z = tst(x,y); z2 = tst2(x,y); z3 = tst3(x,y); 
julia> @benchmark tst(x,y) 
    BenchmarkTools.Trial: 
    memory estimate: 688.11 KiB 
    allocs estimate: 8196 
    -------------- 
    median time:  759.545 μs (0.00% GC) 
    samples:   6068 
julia> @benchmark tst2(x,y) 
    BenchmarkTools.Trial: 
    memory estimate: 426.81 KiB 
    allocs estimate: 10798 
    -------------- 
    median time:  1.634 ms (0.00% GC) 
    samples:   2869 
julia> @benchmark tst3(x,y) 
    BenchmarkTools.Trial: 
    memory estimate: 336.41 KiB 
    allocs estimate: 12 
    -------------- 
    median time:  114.060 μs (0.00% GC) 
    samples:   10000 

Więc tst3 użyciu sum ma lepszą wydajność (~ 7x nad tst i ~ 15x ponad tst2).

Używanie StaticArrays zgodnie z sugestią @DNF jest również opcją i dobrze byłoby porównać je z rozwiązaniami tutaj.

+1

Czy jest jakaś różnica między "zredukowanymi (+, x. * Y", 2) 'i' sumami (x. * Y ', 2) '? – DNF

+0

Niezupełnie. Cóż, tak, "suma" jest lepsza. Zmienię to, aby to odzwierciedlić. Dzięki. –

+0

BTW, aby naprawdę zoptymalizować, zmieniając kolejność indeksów na (6,2^10,6) i ręcznie kodując operację za pomocą pętli, a '@ inbounds' może uzyskać kolejne ** 10x **. Ma to na celu ułatwienie obliczania układu pamięci i indeksowania. –