我在Julia中遇到了并行计算的一些性能问题。我是朱莉娅和平行计算的新手。
为了学习,我并行化了一个应该从并行化中获益的代码,但事实并非如此。
该程序估计阵列组件的平均值的平均值,其元素是随机选择的,具有均匀分布。
串行版
tic()
function mean_estimate(N::Int)
iter = 100000*2
p = 5000
vec_mean = zeros(iter)
for i = 1:iter
vec_mean[i] = mean( rand(p) )
end
return mean(vec_mean)
end
a = mean_estimate(0)
toc()
println("The mean is: ", a)
并行版本
addprocs(CPU_CORES - 1)
println("CPU cores ", CPU_CORES)
tic()
@everywhere function mean_estimate(N::Int)
iter = 100000
p = 5000
vec_mean = zeros(iter)
for i = 1:iter
vec_mean[i] = mean( rand(p) )
end
return mean(vec_mean)
end
the_mean = mean(vcat(pmap(mean_estimate,[1,2])...))
toc()
println("The mean is: ", the_mean)
注意:
我得到的输出是:
me@pentium-ws:~/average$ time julia serial.jl
elapsed time: 2.68671022 seconds
The mean is: 0.49999736055814215
real 0m2.961s
user 0m2.928s
sys 0m0.116s
和
me@pentium-ws:~/average$ time julia -p 2 parallel.jl
CPU cores 2
elapsed time: 2.890163089 seconds
The mean is: 0.5000104221069994
real 0m7.576s
user 0m11.744s
sys 0m0.308s
我注意到,对于代码的定时部分,串行版本比并行版本略快。此外,总执行时间存在很大差异。
问题
注意:我将pmap与vcat一起使用,因为我也希望尝试使用中位数。
感谢您的帮助
修改
我测量了@HighPerformanceMark建议的次数。 tic()/ toc()次数如下。每种情况的迭代次数都是2E6。
Array Size Single thread Parallel Ratio
5000 2.69 2.89 1.07
100 000 488.77 346.00 0.71
1000 000 4776.58 4438.09 0.93
我很困惑为什么阵列大小没有明显的趋势。
答案 0 :(得分:2)
您应该在评论中支持 prime 注意建议。
正如@ChrisRackauckas指出的那样,类型不稳定是高性能Julia代码的常见障碍。如果您需要高性能代码,请确保您的函数是type-stable。考虑注释函数pmap
和/或vcat
的返回类型,例如f(pids::Vector{Int}) = mean(vcat(pmap(mean_estimate, pids))) :: Float64
或类似内容,因为pmap
没有强烈输入其输出。另一种策略是推出自己的并行调度程序。您可以使用pmap
源代码作为跳板(请参阅代码here)。
此外,正如@AlexMorley评论的那样,通过包括编译时间,您会混淆性能测量。通常,在Julia中通过运行两次并仅测量第二次运行来测量函数f()
的性能。在第一次运行中,JIT编译器在运行之前编译f()
,而第二次运行则使用编译的函数。编译会产生(不需要的)性能成本,因此第二次运行的计时会避免测量编译。
如果可能,preallocate all outputs。在您的代码中,您已将每个工作程序设置为分配自己的zeros(iter)
及其自己的rand(p)
。这可能会产生严重的性能影响。代码草图:
# code mean_estimate as two functions
f(p::Int) = mean(rand(p))
function g(iter::Int, p::Int)
vec_mean = zeros(iter)
for i in eachindex(vec_mean)
vec_mean[i] = f(p)
end
return mean(vec_mean)
end
# run twice, time on second run to get compute time
g(200000, 5000)
@time g(200000, 5000)
### output on my machine
# 2.792953 seconds (600.01 k allocations: 7.470 GB, 24.65% gc time)
# 0.4999951853035917
@time
宏警告您垃圾收集器在执行期间清理了大量已分配的内存,实际上是几千兆字节。这会导致性能下降。内存分配可能会掩盖串行和并行计算时间之间的任何区别。
最后,请记住,并行计算会导致计划和管理个别工作人员的开销。你的工人正在计算长度为5000的许多随机向量均值的平均值。但是你可以简洁地计算5M条目的平均值(或中位数)
x = rand(5_000_000)
mean(x)
@time mean(x) # 0.002854 seconds (5 allocations: 176 bytes)
所以目前还不清楚你的并行计算方案如何改善串行性能。并行计算通常在阵列真正强大或计算算术强度时提供最佳帮助,而向量意味着可能不属于该域。
最后一点:您可能想要查看SharedArrays
,它将数组分布在具有公共内存池的多个工作者或Julia中的实验性multithreading工具中。您可能会发现这些并行框架比pmap
更直观。