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

Julia 并行计算

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

的处理器上运行,应使用 并行 for 循环 ,它在 Julia 中应写为: nheads = @parallel (+) for i=1:200000000 int(randbool()) end这个构造实现了给多处理器分配迭代的模式,并且使用特定约简来综合结果(此例中为 (+) )。注意,尽管并行 for 循环看起来和一组 for 循环差不多,但它们的行为有很大区别。第一,循环不是按顺序进行的。第二,写进变量或数组的值不是全局可见的,因为迭代运行在不同的处理器上。并行循环内使用的所有变量都会被复制、广播到每个处理器。下列代码并不会按照预想运行: a = zeros(100000) @parallel for i=1:100000 a[i] = i end如果不需要,可以省略约简运算符。但此代码不会初始化 a 的所有元素,因为每个处理器上都只有独立的一份儿。应避免类似的并行 for 循环。但是我们可以使用分布式数组来规避这种情形,后面我们会讲。如果“外部”变量是只读的,就可以在并行循环中使用它: a = randn(1000) @parallel (+) for i=1:100000 f(a[randi(end)]) end有时我们不需要约简,仅希望将函数应用到某个范围的整数(或某个集合的元素)上。这时可以使用 并行映射 pmap 函数。下例中并行计算几个大随机矩阵的奇异值: M = {rand(1000,1000) for i=1:10} pmap(svd, M)被调用的函数需处理大量工作时使用 pmap ,反之,则使用 @parallel for 。与远程引用同步调度Julia 的平行编程平台使用任务(也成为协程) ,其可在多个计算中切换。每当代码执行一个通信操作,例如 fetch 或者 wait,当前任务便暂停同时调度器会选择另一个任务运行。在事件等待完成后,任务会重新启动。对于很多问题,没必要直接考虑任务。然而,由于提供了动态调度,可以同时等待多个事件。在动态调度中,一个程序决定计算什么和在哪计算,这是基于其他工作何时完成的。这是被不可预知的或不可平衡的工作荷载所需要的,只有当他们结束当前任务我们才能分配更多的工作进程。作为一个例子,考虑计算不同大小的矩阵的奇异值: M = {rand(800,800), rand(600,600), rand(800,800), rand(600,600)} pmap(svd, M)如果一个进程要处理 800 x 800 矩阵和另一个 600 x 600 矩阵,我们不会得到很多的可伸缩性。解决方案是让本地的任务在他们完成当前的任务时去“喂”每个进程中的工作。pmap 的实现过程中可以看到这个: function pmap(f, lst) np = nprocs() # determine the number of processes available n = length(lst) results = cell(n) i = 1 # function to produce the next work item from the queue. # in this case it's just an index. nextidx() = (idx=i; i+=1; idx) @sync begin for p=1:np if p != myid() || np == 1 @async begin while true idx = nextidx() if idx > n break end results[idx] = remotecall_fetch(p, f, lst[idx]) end end end end end results end只有在本地运行任务的过程中,@async 才与 @spawn 类似。我们使用它来为每个流程创建一个“供给”的任务。每个任务选择下一个需要被计算的指数,然后等待它的进程完成,接着一直重复到用完指数。注意,“供给”任务只有当主要任务到达 @sync 块结束时才开始执行,此时它放弃控制并等待所有的本地任务在从函数返回之前完成。供给任务可以通过 nextidx() 共享状态,因为它们都在相同的进程上运行。这个过程不需要锁定,因为线程是实时进行调度的而不是一成不变。这意味着内容的切换只发生在定义好的时候:在这种情况下,当 remotecall_fetch 会被调用。分布式数组并行计算综合使用多个机器上的内存资源,因而可以使用在一个机器上不能实现的大数组。这时,可使用分布式数组,每个处理器仅对它所拥有的那部分数组进行操作。分布式数组(或 全局对象 )逻辑上是个单数组,但它分为很多块儿,每个处理器上保存一块儿。但对整个数组的运算与在本地数组的运算是一样的,并行计算是隐藏的。分布式数组是用 DArray 类型来实现的。 DArray 的元素类型和维度与 Array 一样。 DArray 的数据的分布,是这样实现的:它把索引空间在每个维度都分成一些小块。一些常用分布式数组可以使用 d 开头的函数来构造: dzeros(100,100,10) dones(100,100,10) drand(100,100,10) drandn(100,100,10) dfill(x, 100,100,10)最后一个例子中,数组的元素由值 x 来初始化。这些函数自动选取某个分布。如果要指明使用哪个进程,如何分布数据,应这样写: dzeros((100,100), [1:4], [1,4])第二个参数指定了数组应该在处理器 1 到 4 中创建。划分含有很多进程的数据时,人们经常看到性能收益递减。把 DArrays 放在一个进程的子集中,该进程允许多个 DArray 同时计算,并且每个进程拥有更高比例的通信工作。第三个参数指定了一个分布;数组第 n 个元素指定了应该分成多少个块。在本例中,第一个维度不会分割,而第二个维度将分为四块。因此每个局部块的大小为 (100,25)。注意,分布式数组必须与进程数量相符。distribute(a::Array) 可用来将本地数组转换为分布式数组。localpart(a::DArray) 可用来获取 DArray 本地存储的部分。localindexes(a::DArray) 返回本地进程所存储的维度索引值范围多元组。convert(Array, a::DArray) 将所有数据综合到本地进程上。使用索引值范围来索引 DArray (方括号)时,会创建 SubArray 对象,但不复制数据。构造分布式数组DArray 的构造函数是 darray ,它的声明如下: DArray(init, dims[, procs, dist])init 函数的参数,是索引值范围多元组。这个函数在本地声名一块分布式数组,并用指定索引值来进行初始化。 dims 是整个分布式数组的维度。 procs 是可选的,指明一个存有要使用的进程 ID 的向量 。 dist 是一个整数向量,指明分布式数组在每个维度应该被分成几块。最

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


Julia 并行计算