Jestem zainteresowany wykorzystaniem Julia SharedArray
s dla projektu naukowego informatyki. Moja obecna implementacja odwołuje się do BLAS dla wszystkich operacji macierzy-wektorowych, ale pomyślałem, że być może SharedArray
zaoferuje pewne przyspieszenie na maszynach wielordzeniowych. Moim pomysłem jest po prostu zaktualizować indeks wyjściowy po indeksie, aktualizując indeksy do procesów roboczych.BLAS v. Równoległe aktualizacje dla obiektów Julia SharedArray
Poprzednie dyskusje here o SharedArray
s oraz here o obiektach pamięci współdzielonej nie zawierały jasnych wskazówek dotyczących tego problemu. Wydaje się to intuicyjnie proste, ale po testach jestem nieco zdezorientowany, dlaczego to podejście działa tak źle (zobacz poniższy kod). Na początek wygląda na to, że @parallel for
przydziela dużo pamięci. I jeśli przedwzmacniam pętlę z @sync
, co wydaje się mądrą rzeczą, jeśli cały wektor wyjściowy jest wymagany później, to pętla równoległa jest znacznie wolniejsza (ale bez @sync
, pętla jest potężna szybko).
Czy źle zinterpretowałem właściwe użycie obiektu SharedArray
? A może nieefektywnie przydzieliłem obliczenia?
### test for speed gain w/ SharedArray vs. Array ###
# problem dimensions
n = 10000; p = 25000
# set BLAS threads; 64 seems reasonable in testing
blas_set_num_threads(64)
# make normal Arrays
x = randn(n,p)
y = ones(p)
z = zeros(n)
# make SharedArrays
X = convert(SharedArray{Float64,2}, x)
Y = convert(SharedArray{Float64,1}, y)
Z = convert(SharedArray{Float64,1}, z)
# run BLAS.gemv! on Arrays twice, time second case
BLAS.gemv!('N', 1.0, x, y, 0.0, z)
@time BLAS.gemv!('N', 1.0, x, y, 0.0, z)
# does BLAS work equally well for SharedArrays?
# check timing result and ensure same answer
BLAS.gemv!('N', 1.0, X, Y, 0.0, Z)
@time BLAS.gemv!('N', 1.0, X, Y, 0.0, Z)
println("$(isequal(z,Z))") # should be true
# SharedArrays can be updated in parallel
# code a loop to farm updates to worker nodes
# use transposed X to place rows of X in columnar format
# should (hopefully) help with performance issues from stride
Xt = X'
@parallel for i = 1:n
Z[i] = dot(Y, Xt[:,i])
end
# now time the synchronized copy of this
@time @sync @parallel for i = 1:n
Z[i] = dot(Y, Xt[:,i])
end
# still get same result?
println("$(isequal(z,Z))") # should be true
Wyjście z testu z 4 pracowników + węzłów 1 master:
elapsed time: 0.109010169 seconds (80 bytes allocated)
elapsed time: 0.110858551 seconds (80 bytes allocated)
true
elapsed time: 1.726231048 seconds (119936 bytes allocated)
true
ooh, nie myślałem o alokacjach pamięci dla 'Xt [:, i]'. Twój przykładowy kod jest łatwy do naśladowania. Dziękuję Ci! –
Dlaczego "dotcol" jest szybszy, gdy użyto 'B [i, j]' (jak w kodzie), a nie 'B [j, i]' (bez wymogu transpozycji macierzy "X")? – Taiki
@Taiki jest to kwestia dostępu z ograniczonym dostępem do pamięci? Julia przechowuje tablice w formacie kolumna-major. Więcej informacji można znaleźć w [documentation] (http://docs.julialang.org/en/release-0.4/manual/performance-tips/#access-arrays-in-memory-order- long-columns). –