我们可以使用
c = @parallel (vcat) for i=1:10
(i,i+1)
end
但是当我尝试使用push!()而不是vcat()
时,我遇到了一些错误。如何在此并行循环中使用push!()
?
c = @parallel (push!) for i=1:10
(c, (i,i+1))
end
答案 0 :(得分:3)
@parallel
有点类似于foldl(op, itr)
,因为它使用itr
的第一个值作为op
的初始第一个参数。 push!
缺少操作数之间所需的对称性。也许您正在寻找的是:
julia> c = @parallel (append!) for i=1:10
[(i,i+1)]
end
答案 1 :(得分:2)
详细阐述丹的观点;要查看parallel
宏的工作原理,请查看以下两个调用之间的区别:
julia> @parallel print for i in 1:10
(i,i+1)
end
(1, 2)(2, 3)nothing(3, 4)nothing(4, 5)nothing(5, 6)nothing(6, 7)nothing(7, 8)nothing(8, 9)nothing(9, 10)nothing(10, 11)
julia> @parallel string for i in 1:10
(i,i+1)
end
"(1, 2)(2, 3)(3, 4)(4, 5)(5, 6)(6, 7)(7, 8)(8, 9)(9, 10)(10, 11)"
从最顶层开始,应该清楚发生了什么。每次迭代都会产生一个输出。在这些输出上使用指定的功能时,这是在输出对中完成的。输入两个第一对输出进行打印,然后打印操作的结果成为下一对要处理的第一项。由于输出为nothing
,print
只打印(3,4)。此打印语句的结果为nothing
,因此要打印的下一对是nothing
和(4,5)
,依此类推,直到消耗掉所有元素。即就伪代码而言,这就是发生的事情:
步骤1:state = print((1,2),(2,3)); #state变为nothing
第2步:state = print(state,(3,4)); #start再次成为nothing
第3步:state = print(state,(4,5)); #等等
字符串按预期工作的原因是因为发生了以下步骤:
步骤1:state = string((1,2),(2,3));
第2步:state = string(state,(3,4));
第3步:state = string(state,(4,5);
等
通常,传递给并行宏的函数应该是两个相同类型的输入,并输出相同类型的对象。
因此您不能使用push!
,因为它总是使用两个不同类型的输入(一个数组和一个普通元素),并输出一个数组。因此,您需要使用符合规范的append!
。
另请注意,不保证输出顺序。 (这里恰好是因为我只使用了1名工人)。如果你想要一些操作顺序很重要的东西,那么你就不应该使用这个结构。例如,显然在添加之类的东西中它并不重要,因为加法是完全相关的操作;但是如果我使用string
,如果输出按不同的顺序处理,那么显然你最终会得到一个不同于你所期望的字符串。
编辑 - 解决vcat / append之间的基准! /索引作业
我认为最有效的方法是通过正常索引到预分配的数组。但是在append!
和vcat
之间,追加将肯定会更快,因为vcat总是复制(据我理解)。
基准:
function parallelWithVcat!( A::Array{Tuple{Int64, Int64}, 1} )
A = @parallel vcat for i = 1:10000
(i, i+1)
end
end;
function parallelWithFunction!( A::Array{Tuple{Int64, Int64}, 1} )
A = @parallel append! for i in 1:10000
[(i, i+1)];
end
end;
function parallelWithPreallocation!( A::Array{Tuple{Int64, Int64}, 1} )
@parallel for i in 1:10000
A[i] = (i, i+1);
end
end;
A = Array{Tuple{Int64, Int64}, 1}(10000);
### first runs omitted, all benchmarks here are from 2nd runs ###
# first on a single worker:
@time for n in 1:100; parallelWithVcat!(A); end
#> 8.050429 seconds (24.65 M allocations: 75.341 GiB, 15.42% gc time)
@time for n in 1:100; parallelWithFunction!(A); end
#> 0.072325 seconds (1.01 M allocations: 141.846 MiB, 52.69% gc time)
@time for n in 1:100; parallelWithPreallocation!(A); end
#> 0.000387 seconds (4.21 k allocations: 234.750 KiB)
# now with true parallelism:
addprocs(10);
@time for n in 1:100; parallelWithVcat!(A); end
#> 1.177645 seconds (160.02 k allocations: 109.618 MiB, 0.75% gc time)
@time for n in 1:100; parallelWithFunction!(A); end
#> 0.060813 seconds (111.87 k allocations: 70.585 MiB, 3.91% gc time)
@time for n in 1:100; parallelWithPreallocation!(A); end
#> 0.058134 seconds (116.16 k allocations: 4.174 MiB)
如果有人能提出更有效的方法,请这样做!
特别注意索引的赋值比其余的快得多,因此它出现(至少在这个例子中)并行情况下的大多数计算似乎都在并行化本身丢失了。
免责声明:我没有声称以上是@parallel法术的正确召唤。我没有详细研究宏的内部工作,以便能够另外声明。特别是,我不知道宏导致哪些部分远程处理与本地处理(例如分配部分)。建议谨慎,ymmv等