朱PMAP速度 - 并行处理 - 动态规划(Julia pmap speed - parallel

2019-11-04 22:21发布

我试图加快矩阵填充为茱莉亚(v0.6.0)动态规划的问题,我似乎无法使用获得多少额外的速度pmap 。 这涉及到这个问题,我发布将近一年前: 在朱莉娅采用并行处理填充一个矩阵 。 我能够加快与一些很大的帮助串行处理的话,现在我想从朱莉娅并行处理工具获得额外的速度。

对于串行处理的情况下,我使用的是3维矩阵(实际上是一组大小相等的矩阵,由第一维的索引),并遍历第一维。 我想给pmap一试,不过,更有效地遍历矩阵集。

下面是代码的设置。 使用pmapv_iter以下功能,我转换的三维基质成字典对象,具有等于在第一维的指标值的字典键( v_dict在下面的代码,与gcc等于第一维尺寸) 。 所述v_iter函数采用其他字典对象( E_opt_dictgridpoint_m_dict下文)作为附加输入:

function v_iter(a,b,c)
   diff_v = 1
   while diff_v>convcrit
     diff_v = -Inf

     #These lines efficiently multiply the value function by the Markov transition matrix, using the A_mul_B function
     exp_v       = zeros(Float64,gkpc,1)
     A_mul_B!(exp_v,a[1:gkpc,:],Zprob[1,:])
     for j=2:gz
       temp=Array{Float64}(gkpc,1)
       A_mul_B!(temp,a[(j-1)*gkpc+1:(j-1)*gkpc+gkpc,:],Zprob[j,:])
       exp_v=hcat(exp_v,temp)
     end    

     #This tries to find the optimal value of v
     for h=1:gm
       for j=1:gz
         oldv = a[h,j]
         newv = (1-tau)*b[h,j]+beta*exp_v[c[h,j],j]
         a[h,j] = newv
         diff_v = max(diff_v, oldv-newv, newv-oldv)
       end
     end
   end
end

gz =  9  
gp =  13  
gk =  17  
gcc =  5  
gm    = gk * gp * gcc * gz
gkpc  = gk * gp * gcc
gkp = gk*gp
beta  = ((1+0.015)^(-1))
tau        = 0.35
Zprob = [0.43 0.38 0.15 0.03 0.00 0.00 0.00 0.00 0.00; 0.05 0.47 0.35 0.11 0.02 0.00 0.00 0.00 0.00; 0.01 0.10 0.50 0.30 0.08 0.01 0.00 0.00 0.00; 0.00 0.02 0.15 0.51 0.26 0.06 0.01  0.00 0.00; 0.00 0.00 0.03 0.21 0.52 0.21 0.03 0.00 0.00 ; 0.00 0.00  0.01  0.06 0.26 0.51 0.15 0.02 0.00 ; 0.00 0.00 0.00 0.01 0.08 0.30 0.50 0.10 0.01 ; 0.00 0.00 0.00 0.00 0.02 0.11 0.35 0.47 0.05; 0.00 0.00 0.00 0.00 0.00 0.03 0.15 0.38 0.43]
convcrit = 0.001   # chosen convergence criterion

E_opt                  = Array{Float64}(gcc,gm,gz)    
fill!(E_opt,10.0)

gridpoint_m   = Array{Int64}(gcc,gm,gz)
fill!(gridpoint_m,fld(gkp,2)) 

v_dict=Dict(i => zeros(Float64,gm,gz) for i=1:gcc)
E_opt_dict=Dict(i => E_opt[i,:,:] for i=1:gcc)
gridpoint_m_dict=Dict(i => gridpoint_m[i,:,:] for i=1:gcc) 

用于并行处理的,我所执行的以下两个命令:

wp = CachingPool(workers())
addprocs(3)
pmap(wp,v_iter,values(v_dict),values(E_opt_dict),values(gridpoint_m_dict))

......它生产这样的表现:

135.626417 seconds (3.29 G allocations: 57.152 GiB, 3.74% gc time)

然后我试着连续的过程,而不是:

for i=1:gcc
    v_iter(v_dict[i],E_opt_dict[i],gridpoint_m_dict[i])
end

...并获得更好的性能。

128.263852 seconds (3.29 G allocations: 57.101 GiB, 4.53% gc time)

这也给了我相同的性能与运行v_iter原始三维对象:

v=zeros(Float64,gcc,gm,gz)
for i=1:gcc
    v_iter(v[i,:,:],E_opt[i,:,:],gridpoint_m[i,:,:])
end

我知道,并行处理涉及建立时间,但是当我增加值gcc ,我仍然得到约等于处理时间串行和并行。 这似乎是一个很好的候选人进行并行处理,因为没有必要为工人之间的消息! 但我似乎无法使其有效地工作。

Answer 1:

您创建CachingPool加入工作进程之前。 因此,传递给你的缓存池pmap告诉它仅使用一个工人。 你可以简单地通过运行检查wp.workers你会看到类似这样的Set([1]) 因此,它应该是: addprocs(3) wp = CachingPool(workers())你也可以考虑运行朱莉娅-p命令行参数,如julia -p 3 ,然后你可以跳过addprocs(3)命令。

最重要的是你forpmap循环是不等价的。 茱莉亚Dict对象是一个HashMap和类似的其他语言不提供类似元素为了什么。 因此,在你的for循环可以保证得到同样匹配i个元素,同时与values值的排序并不需要原来的顺序相匹配(你可以在具有不同的顺序为每个三个变量的pmap环)。 由于您的钥匙Dicts是从只是数字1高达gcc ,你只需要使用数组来代替。 您可以使用发电机非常类似的Python。 举一个例子,而不是v_dict=Dict(i => zeros(Float64,gm,gz) for i=1:gcc)使用v_dict_a = [zeros(Float64,gm,gz) for i=1:gcc]

希望帮助。



Answer 2:

基于@Przemyslaw Szufeul的有用的建议,我已经放置,妥善执行并行处理下面的代码。 一旦它运行后,我运行时间实现大幅提升: 77.728264 seconds (181.20 k allocations: 12.548 MiB)

除了重新排序wp命令和使用普热建议的发电机,我也重铸v_iter作为匿名功能,以避免具有洒@everywhere围绕代码喂功能和数据给工人。

我还添加return av_iter功能,并设置v_a下面等于输出pmap ,因为你不能通过引用传递到远程对象。

addprocs(3)
v_iter = function(a,b,c)
   diff_v = 1
   while diff_v>convcrit
     diff_v = -Inf

     #These lines efficiently multiply the value function by the Markov transition matrix, using the A_mul_B function
     exp_v       = zeros(Float64,gkpc,1)
     A_mul_B!(exp_v,a[1:gkpc,:],Zprob[1,:])
     for j=2:gz
       temp=Array{Float64}(gkpc,1)
       A_mul_B!(temp,a[(j-1)*gkpc+1:(j-1)*gkpc+gkpc,:],Zprob[j,:])
       exp_v=hcat(exp_v,temp)
     end    

     #This tries to find the optimal value of v
     for h=1:gm
       for j=1:gz
         oldv = a[h,j]
         newv = (1-tau)*b[h,j]+beta*exp_v[c[h,j],j]
         a[h,j] = newv
         diff_v = max(diff_v, oldv-newv, newv-oldv)
       end
     end
   end
  return a
end

gz =  9  
gp =  13  
gk =  17  
gcc =  5  
gm    = gk * gp * gcc * gz
gkpc  = gk * gp * gcc
gkp   =gk*gp
beta  = ((1+0.015)^(-1))
tau        = 0.35
Zprob = [0.43 0.38 0.15 0.03 0.00 0.00 0.00 0.00 0.00; 0.05 0.47 0.35 0.11 0.02 0.00 0.00 0.00 0.00; 0.01 0.10 0.50 0.30 0.08 0.01 0.00 0.00 0.00; 0.00 0.02 0.15 0.51 0.26 0.06 0.01  0.00 0.00; 0.00 0.00 0.03 0.21 0.52 0.21 0.03 0.00 0.00 ; 0.00 0.00  0.01  0.06 0.26 0.51 0.15 0.02 0.00 ; 0.00 0.00 0.00 0.01 0.08 0.30 0.50 0.10 0.01 ; 0.00 0.00 0.00 0.00 0.02 0.11 0.35 0.47 0.05; 0.00 0.00 0.00 0.00 0.00 0.03 0.15 0.38 0.43]
convcrit = 0.001   # chosen convergence criterion

E_opt                  = Array{Float64}(gcc,gm,gz)    
fill!(E_opt,10.0)

gridpoint_m   = Array{Int64}(gcc,gm,gz)
fill!(gridpoint_m,fld(gkp,2)) 

v_a=[zeros(Float64,gm,gz) for i=1:gcc]
E_opt_a=[E_opt[i,:,:] for i=1:gcc]
gridpoint_m_a=[gridpoint_m[i,:,:] for i=1:gcc]

wp = CachingPool(workers())
v_a = pmap(wp,v_iter,v_a,E_opt_a,gridpoint_m_a)


文章来源: Julia pmap speed - parallel processing - dynamic programming