Julia并行计算
1. 多协程
协程是一种非抢占式多任务处理的方式,相当于轻量级的线程。简单来看,就相当于函数可以在某个点暂停执行并可以f返回结果,之后可以从该点恢复执行。
Julia中的协程通过Channel数据结构实现多任务间的通信。Channel的构造方法为Channel{T=Any}(size::Int=0)
,T代表Channel中存储数据的类型,size代表缓冲区大小。
Channel中两个重要的操作:
put!
:向Channel中加入数据,如果Channel已满,这一操作就会阻塞;take!
:从Channel中取出数据。
不同的task可以并发地对同一个Channel进行put!
或take!
操作。
可以通过isready
来判断Channel中是否有准备好的数据,通过close
来关闭Channel,关闭后就不能再写入数据,但还可以读出数据。
mission:创建100个 20 × 20 20 \times 20 20×20的随机矩阵,与固定的 20 × 1 20 \times 1 20×1矩阵相乘,将所有的结果相加。
n = 100
matrixs = Channel{Array{Float64,2}}(n)
result = Channel{Array{Float64,2}}(n)
y = rand(20, 1)
function create_matrix(n)
for i in 1:n
put!(matrixs, rand(20, 20))
end
end
function do_works(n)
for task_id in 1:n
x = take!(matrixs)
put!(result, x * y)
end
end
@async create_matrix(n)
for i in 1:10
@async do_works(n)
end
val = 0
for i in 1:n
global val += sum(take!(result))
end
println("Final result is $val")
2. 多线程
多线程相关的操作在Threads模块中,而Threads隶属于Base模块,因此可以直接使用Threads.
Julia程序线程数的查看与设置:
Threads.nthreads()
查看Julia的线程数。export JULIA_NUM_THREADS=?
(Linux OSX)或Set JULIA_NUM_THREADS=?
(Windows)来设置线程数(在terminal中进行设置),JULIA_NUM_THREADS
环境变量用来设置 Julia 可用线程的最大数。如果$JULIA_NUM_THREADS
超过可用的物理 CPU 核心数,那么线程数设置为核心数。要在进入julia前的ternimal中进行设置,只在当前ternimal生效,如果想进行持久化配置可以新建环境变量。
可以通过Threads.threadid()
查看线程的id.
**通过Threads.@threads
**可以使得一个for循环并行化,这个宏可以将一个迭代空间分割并且并行执行,如:
Threads.@threads for i in 1:10
println(Threads.threadid())
end
原子操作
Threads.Atomic{T}
可以保证所创建的对象是原子化的,即线程安全的。注意只有简单类型可以原子化,包括Bool
、Int
、Float
等。原子化的元素获取要通过[]
符号,例:
x = Threads.Atomic{Int}(3)
x # Base.Threads.Atomic{Int64}(3)
x[] # 3
原子操作都包含atomic_
前缀,常用的原子操作有:
atomic_cas!(x::Atomic{T}, cmp::T, newval::T) **where** T
,将x与cmp进行比较,如果相等就将newval赋值给x,否则就保持不变;atomic_xchg!(x::Atomic{T}, newval::T) **where** T
:将x改变为newval;atomic_add!(x::Atomic{T}, val::T) **where** T <: ArithmeticTypes
:原子化地将val与x相加;atomic_sub!(x::Atomic{T}, val::T) **where** T <: ArithmeticTypes
:原子化地将val与x相减。
例:
count = Ref(0)
@Threads.threads for i in 1:1000
count[] += 1;
end
println(count[]) # 值不为1000
count = Threads.Atomic{Int64}(0)
@Threads.threads for i in 1:1000
Threads.atomic_add!(count, 1);
end
println(count[]) # 值为1000
mission:创建100个 20 × 20 20 \times 20 20×20的随机矩阵,与固定的 20 × 1 20 \times 1 20×1矩阵相乘,将所有的结果相加。
n = 100
res = Threads.Atomic{Float64}(0.0)
y = rand(20, 1)
@Threads.threads for i in 1:n
x = rand(20, 20)
Threads.atomic_add!(res, sum(x * y))
end
println("Final result is $res[]")