вектор Джулии матрицы или 3d массива

#vector #multidimensional-array #julia

Вопрос:

Я хочу умножить несколько матриц (все одинакового размера) на векторную бета. Я пробовал две версии для хранения матриц, либо в виде вектора матриц, либо в виде массива трех измерений.

Нормально ли, что версия с вектором матриц работает быстрее?

 using BenchmarkTools

nid =10000
npar = 5
x1 = reshape(repeat([1], nid * npar), nid,:)
x2 = reshape(repeat([2], nid * npar), nid,:)
x3 = reshape(repeat([3], nid * npar), nid,:)
X = reshape([x1 x2 x3], nid, npar, :);
X1 = [x1, x2, x3]
beta = rand(npar)



  function f(X::Array{Int,3}, beta::Vector{Float64})::Array{Float64}
     hcat([ X[:,:,i] * beta  for i=1:size(X,3)]...)
   end

   function g(X::Array{Array{Int,2},1}, beta::Vector{Float64})::Array{Float64}
  hcat([X[i] * beta for  i=1:size(X)[1]]...)
end

f(X,beta);
g(X1,beta);

@benchmark f(X, beta)
@benchmark g(X1, beta)
 

Результаты показывают, что f занимает почти 2 раза больше времени g.
Это нормальный шаблон, или я неправильно использую 3D-массив?

Ответ №1:

Это связано с тем, что оператор среза заставляет копировать каждую матрицу, что приводит к увеличению выделения памяти.

Обратите внимание на последнюю строку контрольного показателя:

 julia> @benchmark f(X, beta)
BenchmarkTools.Trial: 8011 samples with 1 evaluation.
 Range (min … max):  356.013 μs …   4.076 ms  ┊ GC (min … max): 0.00% … 87.21%
 Time  (median):     457.231 μs               ┊ GC (median):    0.00%
 Time  (mean ± σ):   615.235 μs ± 351.236 μs  ┊ GC (mean ± σ):  6.54% ± 11.69%

  ▃▇██▆▅▄▄▄▃▂        ▂▃▄▄▄▃▁▁▁ ▁   ▁                            ▂
  ████████████▆▇▅▅▄▄▇███████████████▇█▇▇█▇▆▇█▆▆▆▆▅▆▅▅▄▃▃▂▂▄▂▄▄▄ █
  356 μs        Histogram: log(frequency) by time       1.96 ms <

 Memory estimate: 1.60 MiB, allocs estimate: 17.

julia> @benchmark g(X1, beta)
BenchmarkTools.Trial: 10000 samples with 1 evaluation.
 Range (min … max):  174.348 μs …   2.493 ms  ┊ GC (min … max): 0.00% … 83.85%
 Time  (median):     219.383 μs               ┊ GC (median):    0.00%
 Time  (mean ± σ):   245.192 μs ± 119.612 μs  ┊ GC (mean ± σ):  3.54% ±  7.68%

   ▃▇█▂                                                          
  ▅████▆▄▄▃▃▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▁▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂ ▃
  174 μs           Histogram: frequency by time          974 μs <

 Memory estimate: 469.25 KiB, allocs estimate: 11.
 

Чтобы избежать этого, просто используйте макрос @views , который вызывает ссылку, без выделения. Затем время между двумя реализациями становится одинаковым из-за случайных шумов:

 function fbis(X::Array{Int,3}, beta::Vector{Float64})::Array{Float64}
   @views  hcat([ X[:,:,i] * beta  for i=1:size(X,3)]...)
end


julia> @benchmark fbis(X, beta)
BenchmarkTools.Trial: 10000 samples with 1 evaluation.
 Range (min … max):  175.984 μs …   2.710 ms  ┊ GC (min … max): 0.00% … 79.70%
 Time  (median):     225.990 μs               ┊ GC (median):    0.00%
 Time  (mean ± σ):   274.611 μs ± 166.015 μs  ┊ GC (mean ± σ):  4.17% ±  7.78%

   ▅█▃                                                           
  ▆███▆▄▃▄▄▃▂▂▂▂▂▂▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁ ▂
  176 μs           Histogram: frequency by time         1.15 ms <

 Memory estimate: 469.25 KiB, allocs estimate: 11.
 

Хотя в этом случае использование ссылок улучшает контрольные показатели, обратите внимание, чтобы не злоупотреблять ими. Если вы «создаете» некоторую матрицу, которую собираетесь использовать снова и снова, начальное время выделения при копировании матрицы может стать незначительным по сравнению со временем поиска в разных пространствах памяти, если вы используете ссылочный способ.

Комментарии:

1. Спасибо вам за обстоятельный ответ. Спасибо вам и за последнее предупреждение.