我已经使用Julia了一段时间,但对它的了解仍然很少,尤其是对于并行计算。我想使用现有数据获取一个新数组,因为该数组非常大,我想以并行方式进行操作,并且代码编写如下:
ψ1 = Array{Complex}(undef, D)
ψ = rand(Complex{Float64},D)
Threads.@threads for k = 1:D
ψ1[k] = @views sum(ψ[GetBasis(k - 1, N)])
end
我使用julia -t 4来运行它,但是与非并行代码相比,它的运行速度非常慢,如下所示:
ψ1 = [@views sum(ψ[GetBasis(k - 1, N)]) for k=1:D]
我不知道为什么会这样,GetBasis()只是一个生成数组的函数,即Array {Int64,1}(N)。
我想问一下如何改善第一个代码,或者是否可以通过某种方式修改第二个代码以使其并行运行?由于数组可能非常大,我想找到一种方法来加快它的速度...
非常感谢,期待你的答复!
完整的代码如下
function GetBasis(n, N)
xxN = collect(0:N-1)
BI = BitFlip.(n,xxN).+1
end
function BitFlip(n, i)
n = n ⊻ (1 << i)
end
N=24
D=2^N
ψ1 = Array{Complex}(undef, D)
ψ = rand(Complex{Float64},D)
Threads.@threads for k = 1:D
ψ1[k] = @views sum(ψ[GetBasis(k - 1, N)])
end
ψ2 = [@views sum(ψ[GetBasis(k - 1, N)]) for k=1:D]
如果所有步骤都正确完成,那么多线程代码确实会更快。
function GetBasis(n, N)
BI = BitFlip.(n,0:N-1).+1
end
function BitFlip(n, i)
n = n ⊻ (1 << i)
end
const N=24
const D=2^N
const ψ = rand(Complex{Float64},D)
const ψ1 = Vector{Complex{Float64}}(undef, D)
using BenchmarkTools
Threads.nthreads() # should return 4 or more
# set JULIA_NUM_THREADS environment variable
现在测试:
julia> GC.gc()
julia> @btime ψ2 = [@views sum(ψ[GetBasis(k - 1, N)]) for k=1:D];
5.591 s (16777218 allocations: 4.50 GiB)
julia> GC.gc()
julia> @btime Threads.@threads for k = 1:D
@inbounds ψ1[k] = @views sum(ψ[GetBasis(k - 1, N)])
end
2.293 s (16777237 allocations: 4.25 GiB)
请注意此代码使用的内存量-你需要在运行Benchamrk之前运行垃圾回收器,并且当计算机中的RAM少于16GB时,该测试的意义将减小。
非常感谢你!现在的性能要好得多!请问为什么要将ψ和ψ1定义为常量?我尝试研究N = 26,在这种情况下,并行代码再次比第一个代码慢,我不确定为什么会这样,这里我的机器中有16GB RAM。
对于N = 26,您正在进入垃圾回收。您需要64GB的RAM才能测试N = 26的性能。
关于
const
-如果您正在运行功能之外的代码基准,则需要使其类型稳定。当您将该代码放入函数中时,可以删除const
。有关详细信息,请参阅docs.julialang.org/en/v1/manual/performance-tips。那么,这是否表示如果我的机器的RAM不足,并行代码可能会比非并行代码慢一些?如果是这样,有什么办法可以改善它?而且我也想通过GPU运行它,但是在Julia的CUDA中找不到类似于@threads的函数...
对于GPU,请查看juliagpu.github.io/CUDA.jl/stable/usage/array再次出现内存问题。“并行代码可能比非并行代码慢”-它取决于垃圾收集器的行为方式。您将需要标记,但是在那种情况下,由于垃圾收集器引起的高差异,将需要运行数百次。