这里先看一份代码示例
using Distributed
addprocs()
@everywhere using SharedArrays;
@everywhere using BenchmarkTools;function test(lim)r = zeros(Int64(lim / 16),Threads.nthreads())Threads.@threads for i in eachindex(r)r[Threads.threadid()] = (BigInt(i)^7 +5)%7; endreturn sum(r)
end
@btime test(10^4)
# 1.178 ms (240079 allocations: 3.98 MiB)@everywhere function test2(lim)a = SharedArray{Int64}(lim);@sync @distributed for i=1:lima[i] = (BigInt(i)^7 +5)%7;endreturn sum(a)
end
@btime test2(10^4)
# 3.796 ms (4413 allocations: 189.02 KiB)
这两个循环执行过程差异很大。
1:在第一个循环中每个线程在不断更新数组中的同一个元素。最有可能的是,由于在一个线程中只有一个内存单元被更新,所以处理器缓存机制可以用来加快速度。另一方面,在第二个循环中每个进程都在更新几个不同的内存单元(内部会做异步拆分),这时的缓存可能起不到作用。
2:第一个数组是Float64类型,而第二个数组则是Int64类型
再执行下,虽然结果一样,但是执行时间却是有差异的。
julia> @btime test(10^4)2.781 ms (220037 allocations: 3.59 MiB)
29997julia> @btime test2(10^4)4.867 ms (2145 allocations: 90.14 KiB)
29997
另一个问题是,当使用分布式时会有进程间通信,但是使用线程没有。基本上,使用多进程间处理持续几毫秒的作业没有意义。但是当增加处理量时,差异可能会出现。所以什么时候用多线程还是多进程应该是有一些准则的。
- 进程更健壮(线程仍处于试验阶段)
- 如果不需要使用锁或原子变量,线程更简单些
- 当本地并行度超过16个线程,程序就会变得效率低下,应该使用分布式线程(这个数字可能不是太准,还要看具体的硬件或者软件平台)
- 为其他人编写实用程序包时使用线程-不要在包内分发代码。说明:如果向包中添加多线程,它的行为对用户应该是透明的。另一方面,Julia的多线程(分布式包)抽象没有区分并行和分布式——也就是说,执行任务的worker可以是本地的,也可以是远程的。这使得代码的设计方式有了根本的区别(例如sharedarray vs distributedarray),此外,代码的设计也可能取决于服务器的数量或限制节点间是否可以通信等情况。因此,通常情况下,分布式相关程序包的逻辑应该从标准实用程序包中分离出来,而多线程功能可以对包用户透明。当然,这条规则也有一些例外,比如提供一些分布式数据处理服务器工具等等,但这是一条一般的经验法则。
- 对于大规模的计算,最好是使用进程,因为可以很容易地进入一个计算机集群,并将工作负载分布在数百台机器上。