当前位置:K88软件开发文章中心编程语言非主流编程语言Julia → 文章内容

Julia 并行计算

减小字体 增大字体 作者:佚名  来源:网上搜集  发布时间:2019-1-15 16:28:21

后俩参数是可选的,忽略的时候使用默认值。下例演示如果将本地数组 fill 的构造函数更改为分布式数组的构造函数: dfill(v, args...) = DArray(I->fill(v, map(length,I)), args...)此例中 init 函数仅对它构造的本地块的维度调用 fill 。分布式数组运算在这个时候,分布式数组没有太多的功能。主要功能是通过数组索引来允许进行通信,这对许多问题来说都很方便。作为一个例子,考虑实现“生活”细胞自动机,每个单元网格中的细胞根据其邻近的细胞进行更新。每个进程需要其本地块中直接相邻的细胞才能计算一个迭代的结果。下面的代码可以实现这个功能: function life_step(d::DArray) DArray(size(d),procs(d)) do I top = mod(first(I[1])-2,size(d,1))+1 bot = mod( last(I[1]) ,size(d,1))+1 left = mod(first(I[2])-2,size(d,2))+1 right = mod( last(I[2]) ,size(d,2))+1 old = Array(Bool, length(I[1])+2, length(I[2])+2) old[1 , 1 ] = d[top , left] # left side old[2:end-1, 1 ] = d[I[1], left] old[end , 1 ] = d[bot , left] old[1 , 2:end-1] = d[top , I[2]] old[2:end-1, 2:end-1] = d[I[1], I[2]] # middle old[end , 2:end-1] = d[bot , I[2]] old[1 , end ] = d[top , right] # right side old[2:end-1, end ] = d[I[1], right] old[end , end ] = d[bot , right] life_rule(old) end end可以看到,我们使用一系列的索引表达式来获取一个本地数组中的数组 old。注意,do 块语法方便 init 函数传递给 DArray 构造函数。接下来,连续函数 life_rule 被调用以提供数据的更新规则,产生所需的 DArray 块。 life_rule 与 DArray-specific 没有关系,但为了完整性,我们在此仍将它列出: function life_rule(old) m, n = size(old) new = similar(old, m-2, n-2) for j = 2:n-1 for i = 2:m-1 nc = +(old[i-1,j-1], old[i-1,j], old[i-1,j+1], old[i ,j-1], old[i ,j+1], old[i+1,j-1], old[i+1,j], old[i+1,j+1]) new[i-1,j-1] = (nc == 3 || nc == 2 && old[i,j]) end end new end共享数组 (用于试验, 仅在 unix 上)共享阵列使用在许多进程中共享内存来映射相同数组的系统。虽然与 DArray 有一些相似之处,但是 SharedArray 的行为是完全不同的。在一个 DArray 中,每个进程只能本地访问一块数据,并且两个进程共享同一块;相比之下,在 SharedArray 中,每个“参与”的进程能够访问整个数组。当你想要在同一台机器上大量数据共同访问两个或两个以上的进程时, SharedArray 是一个不错的选择。SharedArray 索引(分配和访问值)与常规数组一样工作,并且是非常高效的,因为其底层内存可用于本地进程。因此,大多数算法自然地在 SharedArrays 上运行,即使在单进程模式中。当某个算法必须在一个 Array 输入的情况下,可以从 SharedArray 检索底层数组通过调用 sdata(S) 取回。对于其他 AbstractArray 类型, sdata 返回对象本身,所以在任何数组类型下使用 sdata 都是很安全的。共享数字构造函数数的形式: SharedArray(T::Type, dims::NTuple; init=false, pids=Int[])创建一个被 pids 进程指定的,bitstype 为 T 并且大小为 dims 的共享数组。与分布式阵列不同,共享数组只能用于这些参与人员指定的以 pid 命名的参数(如果在同一个主机上,创建过程也同样如此)。如果一个签名为 initfn(S::SharedArray) 的 init 函数被指定,它会被所有参与人员调用。你可以控制它,每个工人可以在数组的不同部分运行 init 函数,因此进行并行的初始化。这里有一个简单的例子: julia> addprocs(3) 3-element Array{Any,1}: 2 3 4 julia> S = SharedArray(Int, (3,4), init = S -> S[localindexes(S)] = myid()) 3x4 SharedArray{Int64,2}: 2 2 3 4 2 3 3 4 2 3 4 4 julia> S[3,2] = 7 7 julia> S 3x4 SharedArray{Int64,2}: 2 2 3 4 2 3 3 4 2 7 4 4localindexes 提供不相交的一维索引的范围,它有时方便进程之间的任务交流。当然,你可以按你希望的方式来划分工作: julia> S = SharedArray(Int, (3,4), init = S -> S[myid()-1:nworkers():length(S)] = myid()) 3x4 SharedArray{Int64,2}: 2 2 2 2 3 3 3 3 4 4 4 4因为所有进程都可以访问底层数据,你必须小心不要设置冲突。例如: @sync begin for p in workers() @async begin remotecall_wait(p, fill!, S, p) end end end这有可能导致未定义的行为:因为每个进程有他自己的 pid 来充满整个数组,无论最后执行的是哪一个进程(任何特定元素 S)都将保留他的 pid。ClusterManagersJulia 工作进程也可以在任意机器中产生,让 Julia 的自然并行功能非常透明地在集群环境中运行。ClusterManager 接口提供了一种方法来指定启动和管理工作进程的手段。例如, ssh 集群也使用 ClusterManager 来实现: immutable SSHManager <: ClusterManager launch::Function manage::Function machines::AbstractVector SSHManager(; machines=[]) = new(launch_ssh_workers, manage_ssh_workers, machines) end function launch_ssh_workers(cman::SSHManager, np::Integer, config::Dict) ... end function manage_ssh_workers(id::Integer, config::Dict, op::Symbol) ... endlaunch_ssh_workers 负责实例化新的 Julia 进程并且 manage_ssh_workers 提供了一种方法来管理这些进程,例如发送中断信号。在运行时可以使用 addprocs 添加新进程: addprocs(5, cman=LocalManager())来指定添加一批进程并且 ClusterManager 用于启动这些进程。脚注[1]:在这边文中, MPI 是指 MPI-1 标准。从 MPI-2 开始,MPI 标准委员会引入了一系列新的通信机制,统称为远程内存访问 (RMA) 。添加 RMA MPI 标准的动机是改善单方面的沟通模式。最新的 MPI 标准的更多信息,参见 http://www.mpi-forum.org/docs。

上一页  [1] [2] [3] 


Julia 并行计算