Julia 多次从分布中提取的最快方法

Julia 多次从分布中提取的最快方法,julia,Julia,这: 比这要快一些: function draw1(n) return rand(Normal(0,1), Int(n)) end 只是好奇为什么会这样,以及显式循环方式是否可以加速(我尝试了@inbounds和@simd,但没有得到加速)。这是不是初始分配的zero()?我分别在0.25秒左右计时,这并不能完全解释差异(另外,第一种方法不是在引擎盖下预先分配阵列吗?) 例如: function draw2(n) result = zeros(Float64, Int(n))

这:

比这要快一些:

function draw1(n)
    return rand(Normal(0,1), Int(n))
end
只是好奇为什么会这样,以及显式循环方式是否可以加速(我尝试了
@inbounds
@simd
,但没有得到加速)。这是不是初始分配的
zero()
?我分别在0.25秒左右计时,这并不能完全解释差异(另外,第一种方法不是在引擎盖下预先分配阵列吗?)

例如:

function draw2(n)
    result = zeros(Float64, Int(n))
    for i=1:Int(n)
        result[i] =  rand(Normal(0,1))
    end
    return result 
end

尝试以下实现:

@time x = draw1(1e08)
  1.169986 seconds (6 allocations: 762.940 MiB, 4.53% gc time)
@time y = draw2(1e08)
  1.824750 seconds (6 allocations: 762.940 MiB, 3.05% gc time)
区别是什么:

  • 使用
    @inbounds
  • 仅创建一次
    Normal(0,1)
  • 更快地初始化
    结果
当我测试它时,它的性能基本上与
draw1
相同(虽然我没有在10e8向量大小上测试它(内存不足)-如果您可以运行这样的
@benchmark
,那就太好了):

编辑:实际上在一个单独的函数中定义一个循环(与
rand
的做法完全相同)使
draw4
的性能比
draw3
要好一点:

julia> using BenchmarkTools                        

julia> @benchmark draw1(10e5)                      
BenchmarkTools.Trial:                              
  memory estimate:  7.63 MiB                       
  allocs estimate:  2                              
  --------------                                   
  minimum time:     12.296 ms (0.00% GC)           
  median time:      13.012 ms (0.00% GC)           
  mean time:        14.510 ms (8.49% GC)           
  maximum time:     84.253 ms (81.30% GC)          
  --------------                                   
  samples:          345                            
  evals/sample:     1                              

julia> @benchmark draw2(10e5)                      
BenchmarkTools.Trial:                              
  memory estimate:  7.63 MiB                       
  allocs estimate:  2                              
  --------------                                   
  minimum time:     20.374 ms (0.00% GC)           
  median time:      21.622 ms (0.00% GC)           
  mean time:        22.787 ms (5.95% GC)           
  maximum time:     92.265 ms (77.18% GC)          
  --------------                                   
  samples:          220                            
  evals/sample:     1                              

julia> @benchmark draw3(10e5)                      
BenchmarkTools.Trial:                              
  memory estimate:  7.63 MiB                       
  allocs estimate:  2                              
  --------------                                   
  minimum time:     12.415 ms (0.00% GC)           
  median time:      12.956 ms (0.00% GC)           
  mean time:        14.456 ms (8.67% GC)           
  maximum time:     84.342 ms (83.74% GC)          
  --------------                                   
  samples:          346                            
  evals/sample:     1                              

尝试以下实现:

@time x = draw1(1e08)
  1.169986 seconds (6 allocations: 762.940 MiB, 4.53% gc time)
@time y = draw2(1e08)
  1.824750 seconds (6 allocations: 762.940 MiB, 3.05% gc time)
区别是什么:

  • 使用
    @inbounds
  • 仅创建一次
    Normal(0,1)
  • 更快地初始化
    结果
当我测试它时,它的性能基本上与
draw1
相同(虽然我没有在10e8向量大小上测试它(内存不足)-如果您可以运行这样的
@benchmark
,那就太好了):

编辑:实际上在一个单独的函数中定义一个循环(与
rand
的做法完全相同)使
draw4
的性能比
draw3
要好一点:

julia> using BenchmarkTools                        

julia> @benchmark draw1(10e5)                      
BenchmarkTools.Trial:                              
  memory estimate:  7.63 MiB                       
  allocs estimate:  2                              
  --------------                                   
  minimum time:     12.296 ms (0.00% GC)           
  median time:      13.012 ms (0.00% GC)           
  mean time:        14.510 ms (8.49% GC)           
  maximum time:     84.253 ms (81.30% GC)          
  --------------                                   
  samples:          345                            
  evals/sample:     1                              

julia> @benchmark draw2(10e5)                      
BenchmarkTools.Trial:                              
  memory estimate:  7.63 MiB                       
  allocs estimate:  2                              
  --------------                                   
  minimum time:     20.374 ms (0.00% GC)           
  median time:      21.622 ms (0.00% GC)           
  mean time:        22.787 ms (5.95% GC)           
  maximum time:     92.265 ms (77.18% GC)          
  --------------                                   
  samples:          220                            
  evals/sample:     1                              

julia> @benchmark draw3(10e5)                      
BenchmarkTools.Trial:                              
  memory estimate:  7.63 MiB                       
  allocs estimate:  2                              
  --------------                                   
  minimum time:     12.415 ms (0.00% GC)           
  median time:      12.956 ms (0.00% GC)           
  mean time:        14.456 ms (8.67% GC)           
  maximum time:     84.342 ms (83.74% GC)          
  --------------                                   
  samples:          346                            
  evals/sample:     1                              

一个简短的答案是,内置实现是最快的,幸运的是,这种情况经常发生

您可以使用内置的

function g!(d, v)
    @inbounds for i=1:length(v)
        v[i] = rand(d)
    end
end

function draw4(n)
    result = Vector{Float64}(Int(n))
    g!(Normal(0,1), result)
    return result 
end

rand总是在边界内。

一个简短的答案是,内置实现速度最快,幸运的是,这种情况经常发生

您可以使用内置的

function g!(d, v)
    @inbounds for i=1:length(v)
        v[i] = rand(d)
    end
end

function draw4(n)
    result = Vector{Float64}(Int(n))
    g!(Normal(0,1), result)
    return result 
end

rand总是在边界内。

看看
的定义吧!(s::Sampleable{Univariate},A::AbstractArray)
在Distributions包中,您将看到它实际上执行了一个循环。每个人似乎都忽略了
randn
,它基本上对这个特定的发行版执行相同的操作
draw6(n)=randn(n)
在我的机器上是最快的(但并不显著)看看
的定义吧!(s::Sampleable{Univariate},A::AbstractArray)
在Distributions包中,您将看到它实际上执行了一个循环。每个人似乎都忽略了
randn
,它基本上对这个特定的发行版执行相同的操作
draw6(n)=randn(n)
在我的机器上是最快的(但不显著)