Julia多线程并行加速
简单测试
1 | using ProgressMeter |
运行程序的时候需要手动开启多个进程,否则会报错1
julia -p 10 filename.jl
如果使用1
julia filename.jl
程序则会报错,始终显示函数fun1
未定义.
代码分析
首先将问题描述一下,假设这里要计算一个量,需要进行一些嵌套循环,而这个量在计算的时候又需要其它的参数依赖,这也就是上面的函数fun1
所描述的问题,他的计算跟变量qx,qy
是有关系的,而接下来要计算的另外一个量,就是需要对这个qx,qy
进行撒点,然后再所有的点上对fun1
进行计算,这也就是下面的三个函数fun2,fun3,fun4
所描述的计算.
首先来看函数fun2
1
2
3
4
5
6
7
8
9
10
11function fun2(kn::Int64)::Float64
re::Float64 = 0.0
for i1 in 1:kn
for i2 in 1:kn
qx = i1*pi/kn
qy = i2*pi/kn
re = re + fun1(qx,qy)
end
end
return re
end
这就是最简单的写法,是个串行执行的程序,只能简单进行循环加和,虽然Julia在这种嵌套循环上已经进行过优化,速度可很客观,但是在现在这个计算情况下他的速度还是很慢的,哪有那么多时间等待程序的执行,可以发现随着kn
变大,执行时间差不多就是指数上升,所以在这里就需要对这个程序进行优化.
接下来看经过改动的多线程版本fun3
1
2
3
4
5
6
7
8
9
10
11
12
13
14function fun3(kn)::Float64
# 增加8线程,注意别开太多
addprocs(8 - nprocs());
println("Running ",nprocs()," processes");
re = SharedArray(zeros(Float64,1))
for i1 in 1:kn
for i2 in 1:kn
qx = i1*pi/kn
qy = i2*pi/kn
re[1] = re[1] + fun1(qx,qy)
end
end
return re[1]
end
在这个升级的函数中,最主要的开了多线程来将循环分发到不同的线程上来加速计算.nprocs()
用来获取当前开启的线程数目,addprocs()
用来开启一定数目的线程.这里要说明一下,想要成功的实现多线程并行,一定要安装好程序最开始using的那些库.这里因为开了多线程的关系,数据的收集方式就要改变一下,所以就有了re = SharedArray(zeros(Float64,1))
这个语句,因为函数fun1
的返回值只是一个单纯的数,所以就用这个函数实现一个多线程共享的一维数组,这样可以用这个数组来存储函数fun1
的结果(我再这里本想找一个共享的变量而不是数组来存储结果,不过我没有找到实现方法,就先用共享数组的方式了).最后利用两个宏@sync @distributed
来将循环过程分发到不同的线程上,这个操作还是很友好的,不需要再去自己手动设置什么内容.
接下来将fun3
改动为fun4
,直接用一个数组来存储所有的结果,因为这个计算结果在平时会用到,其实也就是把fun3
的共享数组从1维变成不同的大小而已,并没有什么实质性的改动1
2
3
4
5
6
7
8
9
10
11
12
13
14function fun4(kn)::SharedArray{Float64,2}
# 增加8线程,注意别开太多
addprocs(8 - nprocs());
println("Running ",nprocs()," processes");
re = SharedArray(zeros(Float64,kn,kn))
for i1 in 1:kn
for i2 in 1:kn
qx = i1*pi/kn
qy = i2*pi/kn
re[i1,i2] = fun1(qx,qy)
end
end
return re
end
并行结果分析
上图是执行的结果,从结果中可以看到,对于串行执行的程序(我这里选的参数在我的笔记本上函数fun1
执行的时间差不多是1s,所以差不多是执行多少次fun1
那么需要的时间就是多少s)可以发现串行执行的时间确实是最多的,开启了多线程之后,虽然并不是开启几个线程时间就减少几倍,但是时间上差不多是串行的1/6,所以速度上的提升还是很明显的.
鉴于该网站分享的大都是学习笔记,作者水平有限,若发现有问题可以发邮件给我
- yxliphy@gmail.com
也非常欢迎喜欢分享的小伙伴投稿