七、共享数组
本章的倒数第二节,介绍了Julia的特色之一:共享数组(SharedArray)。
Julia的共享数组是一个共享的多维数组,由SharedArrays包提供。它可以是跨进程的,此时必须显式地加载SharedArrays包到每个需要用它的进程上,也可以是跨协程的,此时只要在主进程上加载SharedArrays包。可以用@everywhere
来方便地加载SharedArrays包到所有进程上:
julia> @everywhere using SharedArrays
但似乎没办法用@spawnat
之类的命令加载到指定的进程上,所以老老实实用@everywhere
吧。
回顾之前的@distributed
,它也可以在主进程上做协程级并行,于是我们可以在协程级别上用共享数组来实现@distributed
。做个演示:
julia> using SharedArrays
julia> a = SharedArray{Float64}(1,10)
1×10 SharedArray{Float64,2}:
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
julia> @distributed for i = 1:10
a[i]=i
end
Task (done) @0x0000000006671710
julia> a
1×10 SharedArray{Float64,2}:
1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0
可见多个协程并行地操作了同一数组。再看一个跨进程操作的例子:
julia> @everywhere using SharedArrays
julia> a = SharedArray{Float64}(1,10)
1×10 SharedArray{Float64,2}:
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
julia> @fetchfrom 3 (a[6]=2;a[1]=a[6]+1)
3.0
证明在每个进程上都可以把共享数组当作普通数组一样操作,没有区别。
在声明共享数组时,可以指定两个键值参数init
和pids
,做初始化并映射到指定进程上。
小贴士:键值参数又叫关键字参数,有别于一般的参数,必须显式写明关键字和值。例如:Matlab 绘图参数’
'FontSize','值'
,不能简写为'值'
。Julia共享数组的键值参数init
,必须写成init=函数
,而pids
同理。键值参数的优点是可以无序排列,但一般要求排在普通参数的后面。
举个例子:
julia> S = SharedArray{Int,2}((3,4),init = A->(for i=1:3;A[i,1]=100;end),pids=[2,4])
3×4 SharedArray{Int64,2}:
100 0 0 0
100 0 0 0
100 0 0 0
传递参数的时候要注意向量的写法。比如
pids=[2,4]
若是写成pids=[2 4]
就会报错,因为该关键字不接受多维数组。在Julia里`[2 4]会被认为是多行的写法,详情参阅多维数组的章节。
共享数组在任何一个进程上效率是相同的,所以并行操作大规模数据具有极高的效率。
最后我特别指出两个重要内容:
- 原著中演示的辅助初始化的函数
SharedArrays.localindices()
在Julia 1.1.0版本上报错,原因不明。 - 上述的所有方法都是在单台主机上测试的,即所谓单机分布式平台或者虚拟集群,尚未在多台主机组成的分布式平台(此处用“集群”特指多主机的分布式)上测试。Julia为集群提供了
--machinefile
命令,并为虚拟集群和集群提供了统一风格的管理器ClusterManager
,包括Distributed.LocalManager
和Distributed.SSHManager
。然而原著中一笔带过,颇为遗憾。下一节会整理官方文档中的相关内容,可能会补充自己的测试结果。