黒木玄
2018-01-04
結論: 定数 (const) と函数的オブジェクト (funcot-like object) を使うのが良さそう.
https://docs.julialang.org/en/stable/manual/methods/#Function-like-objects-1
a = 2*Float64(π)
# function f(N) contains the global variable a.
#
function f(N)
s = 0.0
for i in 1:N
s += a*rand()
end
return s/N
end
@time f(10^8)
4.531265 seconds (300.00 M allocations: 4.471 GiB, 3.85% gc time)
3.1415380488121687
@code_warntype f(10^8)
Variables: #self# <optimized out> N::Int64 i <optimized out> #temp#@_4::Int64 s::Any #temp#@_6 <optimized out> #temp#@_7::Float64 Body: begin s::Any = 0.0 # line 7: SSAValue(6) = (Base.select_value)((Base.sle_int)(1, N::Int64)::Bool, N::Int64, (Base.sub_int)(1, 1)::Int64)::Int64 #temp#@_4::Int64 = 1 5: unless (Base.not_int)((#temp#@_4::Int64 === (Base.add_int)(SSAValue(6), 1)::Int64)::Bool)::Bool goto 49 SSAValue(7) = #temp#@_4::Int64 SSAValue(8) = (Base.add_int)(#temp#@_4::Int64, 1)::Int64 #temp#@_4::Int64 = SSAValue(8) # line 8: $(Expr(:inbounds, false)) # meta: location random.jl rand 282 # meta: location random.jl rand 143 # meta: location random.jl reserve_1 132 unless ((Core.getfield)(Base.Random.GLOBAL_RNG, :idx)::Int64 === (Base.sext_int)(Int64, Base.Random.MTCacheLength)::Int64)::Bool goto 26 # meta: location random.jl gen_rand 128 SSAValue(3) = (Core.getfield)(Base.Random.GLOBAL_RNG, :state)::Base.dSFMT.DSFMT_state SSAValue(2) = (Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1} $(Expr(:invoke, MethodInstance for dsfmt_fill_array_close1_open2!(::Base.dSFMT.DSFMT_state, ::Ptr{Float64}, ::Int64), :(Base.Random.dsfmt_fill_array_close1_open2!), SSAValue(3), :($(Expr(:foreigncall, :(:jl_array_ptr), Ptr{Float64}, svec(Any), SSAValue(2), 0))), :((Base.arraylen)((Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1})::Int64))) # line 129: # meta: location random.jl mt_setfull! 123 (Core.setfield!)(Base.Random.GLOBAL_RNG, :idx, 0)::Int64 # meta: pop location # meta: pop location goto 27 26: 27: # meta: pop location # meta: location random.jl rand_inbounds 139 # meta: location random.jl rand_inbounds 138 # meta: location random.jl mt_pop! 125 $(Expr(:inbounds, true)) SSAValue(4) = (Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1} SSAValue(5) = (Base.add_int)((Core.getfield)(Base.Random.GLOBAL_RNG, :idx)::Int64, 1)::Int64 (Core.setfield!)(Base.Random.GLOBAL_RNG, :idx, SSAValue(5))::Int64 #temp#@_7::Float64 = (Base.arrayref)(SSAValue(4), SSAValue(5))::Float64 goto 39 $(Expr(:inbounds, :pop)) 39: # meta: pop location # meta: pop location # meta: pop location # meta: pop location # meta: pop location $(Expr(:inbounds, :pop)) s::Any = (s::Any + (Main.a * (Base.sub_float)(#temp#@_7::Float64, 1.0)::Float64)::Any)::Any 47: goto 5 49: # line 10: return (s::Any / N::Int64)::Any end::Any
f(10^8)
の実行には4~5秒程度かかっている. 遅い. @code_warntype f(10^8)
の結果を見てみると, 局所変数 s
の型が Any
と判定されてしまっている. そこで次のセルでは s::FLoat64
と宣言してみよう.
a = 2*Float64(π)
# function f(N) contains the global variable a.
#
function f₁(N)
s::Float64 = 0.0
for i in 1:N
s += a*rand()
end
return s/N
end
@time f₁(10^8)
27.918037 seconds (500.00 M allocations: 7.451 GiB, 0.84% gc time)
3.1416623923895957
さらに遅くなった!(笑)
@code_warntype f₁(10^8)
Variables: #self# <optimized out> N::Int64 i <optimized out> #temp#@_4::Int64 s::Float64 #temp#@_6 <optimized out> #temp#@_7::Float64 Body: begin s::Float64 = 0.0 # line 7: SSAValue(7) = (Base.select_value)((Base.sle_int)(1, N::Int64)::Bool, N::Int64, (Base.sub_int)(1, 1)::Int64)::Int64 #temp#@_4::Int64 = 1 5: unless (Base.not_int)((#temp#@_4::Int64 === (Base.add_int)(SSAValue(7), 1)::Int64)::Bool)::Bool goto 50 SSAValue(8) = #temp#@_4::Int64 SSAValue(9) = (Base.add_int)(#temp#@_4::Int64, 1)::Int64 #temp#@_4::Int64 = SSAValue(9) # line 8: $(Expr(:inbounds, false)) # meta: location random.jl rand 282 # meta: location random.jl rand 143 # meta: location random.jl reserve_1 132 unless ((Core.getfield)(Base.Random.GLOBAL_RNG, :idx)::Int64 === (Base.sext_int)(Int64, Base.Random.MTCacheLength)::Int64)::Bool goto 26 # meta: location random.jl gen_rand 128 SSAValue(4) = (Core.getfield)(Base.Random.GLOBAL_RNG, :state)::Base.dSFMT.DSFMT_state SSAValue(3) = (Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1} $(Expr(:invoke, MethodInstance for dsfmt_fill_array_close1_open2!(::Base.dSFMT.DSFMT_state, ::Ptr{Float64}, ::Int64), :(Base.Random.dsfmt_fill_array_close1_open2!), SSAValue(4), :($(Expr(:foreigncall, :(:jl_array_ptr), Ptr{Float64}, svec(Any), SSAValue(3), 0))), :((Base.arraylen)((Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1})::Int64))) # line 129: # meta: location random.jl mt_setfull! 123 (Core.setfield!)(Base.Random.GLOBAL_RNG, :idx, 0)::Int64 # meta: pop location # meta: pop location goto 27 26: 27: # meta: pop location # meta: location random.jl rand_inbounds 139 # meta: location random.jl rand_inbounds 138 # meta: location random.jl mt_pop! 125 $(Expr(:inbounds, true)) SSAValue(5) = (Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1} SSAValue(6) = (Base.add_int)((Core.getfield)(Base.Random.GLOBAL_RNG, :idx)::Int64, 1)::Int64 (Core.setfield!)(Base.Random.GLOBAL_RNG, :idx, SSAValue(6))::Int64 #temp#@_7::Float64 = (Base.arrayref)(SSAValue(5), SSAValue(6))::Float64 goto 39 $(Expr(:inbounds, :pop)) 39: # meta: pop location # meta: pop location # meta: pop location # meta: pop location # meta: pop location $(Expr(:inbounds, :pop)) SSAValue(2) = (s::Float64 + (Main.a * (Base.sub_float)(#temp#@_7::Float64, 1.0)::Float64)::Any)::Any s::Float64 = (Core.typeassert)((Base.convert)(Main.Float64, SSAValue(2))::Any, Main.Float64)::Float64 48: goto 5 50: # line 10: return (Base.div_float)(s::Float64, (Base.sitofp)(Float64, N::Int64)::Float64)::Float64 end::Float64
a::Float64
と教えてあげると以下のように速くなる.
a = 2*Float64(π)
# function f(N) contains the global variable a.
#
function f₂(N)
s = 0.0
for i in 1:N
s += (a::Float64)*rand()
end
return s/N
end
@time f₂(10^8)
0.342474 seconds (1.71 k allocations: 88.733 KiB)
3.1415899757536776
@code_warntype f₂(10^8)
Variables: #self# <optimized out> N::Int64 i <optimized out> #temp#@_4::Int64 s::Float64 #temp#@_6 <optimized out> #temp#@_7::Float64 Body: begin s::Float64 = 0.0 # line 7: SSAValue(7) = (Base.select_value)((Base.sle_int)(1, N::Int64)::Bool, N::Int64, (Base.sub_int)(1, 1)::Int64)::Int64 #temp#@_4::Int64 = 1 5: unless (Base.not_int)((#temp#@_4::Int64 === (Base.add_int)(SSAValue(7), 1)::Int64)::Bool)::Bool goto 50 SSAValue(8) = #temp#@_4::Int64 SSAValue(9) = (Base.add_int)(#temp#@_4::Int64, 1)::Int64 #temp#@_4::Int64 = SSAValue(9) # line 8: SSAValue(6) = (Core.typeassert)(Main.a, Main.Float64)::Float64 $(Expr(:inbounds, false)) # meta: location random.jl rand 282 # meta: location random.jl rand 143 # meta: location random.jl reserve_1 132 unless ((Core.getfield)(Base.Random.GLOBAL_RNG, :idx)::Int64 === (Base.sext_int)(Int64, Base.Random.MTCacheLength)::Int64)::Bool goto 27 # meta: location random.jl gen_rand 128 SSAValue(3) = (Core.getfield)(Base.Random.GLOBAL_RNG, :state)::Base.dSFMT.DSFMT_state SSAValue(2) = (Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1} $(Expr(:invoke, MethodInstance for dsfmt_fill_array_close1_open2!(::Base.dSFMT.DSFMT_state, ::Ptr{Float64}, ::Int64), :(Base.Random.dsfmt_fill_array_close1_open2!), SSAValue(3), :($(Expr(:foreigncall, :(:jl_array_ptr), Ptr{Float64}, svec(Any), SSAValue(2), 0))), :((Base.arraylen)((Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1})::Int64))) # line 129: # meta: location random.jl mt_setfull! 123 (Core.setfield!)(Base.Random.GLOBAL_RNG, :idx, 0)::Int64 # meta: pop location # meta: pop location goto 28 27: 28: # meta: pop location # meta: location random.jl rand_inbounds 139 # meta: location random.jl rand_inbounds 138 # meta: location random.jl mt_pop! 125 $(Expr(:inbounds, true)) SSAValue(4) = (Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1} SSAValue(5) = (Base.add_int)((Core.getfield)(Base.Random.GLOBAL_RNG, :idx)::Int64, 1)::Int64 (Core.setfield!)(Base.Random.GLOBAL_RNG, :idx, SSAValue(5))::Int64 #temp#@_7::Float64 = (Base.arrayref)(SSAValue(4), SSAValue(5))::Float64 goto 40 $(Expr(:inbounds, :pop)) 40: # meta: pop location # meta: pop location # meta: pop location # meta: pop location # meta: pop location $(Expr(:inbounds, :pop)) s::Float64 = (Base.add_float)(s::Float64, (Base.mul_float)(SSAValue(6), (Base.sub_float)(#temp#@_7::Float64, 1.0)::Float64)::Float64)::Float64 48: goto 5 50: # line 10: return (Base.div_float)(s::Float64, (Base.sitofp)(Float64, N::Int64)::Float64)::Float64 end::Float64
const b = 2*Float64(π)
# function g(N) contains the constant b.
#
function g(N)
s = 0.0
for i in 1:N
s += b*rand()
end
return s/N
end
@time g(10^8)
0.238895 seconds (1.68 k allocations: 86.458 KiB)
3.141795310513143
@code_warntype g(10^8)
Variables: #self# <optimized out> N::Int64 i <optimized out> #temp#@_4::Int64 s::Float64 #temp#@_6 <optimized out> #temp#@_7::Float64 Body: begin s::Float64 = 0.0 # line 7: SSAValue(6) = (Base.select_value)((Base.sle_int)(1, N::Int64)::Bool, N::Int64, (Base.sub_int)(1, 1)::Int64)::Int64 #temp#@_4::Int64 = 1 5: unless (Base.not_int)((#temp#@_4::Int64 === (Base.add_int)(SSAValue(6), 1)::Int64)::Bool)::Bool goto 49 SSAValue(7) = #temp#@_4::Int64 SSAValue(8) = (Base.add_int)(#temp#@_4::Int64, 1)::Int64 #temp#@_4::Int64 = SSAValue(8) # line 8: $(Expr(:inbounds, false)) # meta: location random.jl rand 282 # meta: location random.jl rand 143 # meta: location random.jl reserve_1 132 unless ((Core.getfield)(Base.Random.GLOBAL_RNG, :idx)::Int64 === (Base.sext_int)(Int64, Base.Random.MTCacheLength)::Int64)::Bool goto 26 # meta: location random.jl gen_rand 128 SSAValue(3) = (Core.getfield)(Base.Random.GLOBAL_RNG, :state)::Base.dSFMT.DSFMT_state SSAValue(2) = (Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1} $(Expr(:invoke, MethodInstance for dsfmt_fill_array_close1_open2!(::Base.dSFMT.DSFMT_state, ::Ptr{Float64}, ::Int64), :(Base.Random.dsfmt_fill_array_close1_open2!), SSAValue(3), :($(Expr(:foreigncall, :(:jl_array_ptr), Ptr{Float64}, svec(Any), SSAValue(2), 0))), :((Base.arraylen)((Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1})::Int64))) # line 129: # meta: location random.jl mt_setfull! 123 (Core.setfield!)(Base.Random.GLOBAL_RNG, :idx, 0)::Int64 # meta: pop location # meta: pop location goto 27 26: 27: # meta: pop location # meta: location random.jl rand_inbounds 139 # meta: location random.jl rand_inbounds 138 # meta: location random.jl mt_pop! 125 $(Expr(:inbounds, true)) SSAValue(4) = (Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1} SSAValue(5) = (Base.add_int)((Core.getfield)(Base.Random.GLOBAL_RNG, :idx)::Int64, 1)::Int64 (Core.setfield!)(Base.Random.GLOBAL_RNG, :idx, SSAValue(5))::Int64 #temp#@_7::Float64 = (Base.arrayref)(SSAValue(4), SSAValue(5))::Float64 goto 39 $(Expr(:inbounds, :pop)) 39: # meta: pop location # meta: pop location # meta: pop location # meta: pop location # meta: pop location $(Expr(:inbounds, :pop)) s::Float64 = (Base.add_float)(s::Float64, (Base.mul_float)(Main.b, (Base.sub_float)(#temp#@_7::Float64, 1.0)::Float64)::Float64)::Float64 47: goto 5 49: # line 10: return (Base.div_float)(s::Float64, (Base.sitofp)(Float64, N::Int64)::Float64)::Float64 end::Float64
function make_h()
c = 2*Float64(π)
# function h(N) contains the local variable c.
#
function h(N)
s = 0.0
for i in 1:N
s += c*rand()
end
return s/N
end
return h
end
h = make_h()
@time h(10^8)
0.223614 seconds (1.72 k allocations: 88.333 KiB)
3.1417151499863762
@code_warntype h(10^8)
Variables: #self#::#h#1{Float64} N::Int64 i <optimized out> #temp#@_4::Int64 s::Float64 #temp#@_6 <optimized out> #temp#@_7::Float64 Body: begin s::Float64 = 0.0 # line 8: SSAValue(6) = (Base.select_value)((Base.sle_int)(1, N::Int64)::Bool, N::Int64, (Base.sub_int)(1, 1)::Int64)::Int64 #temp#@_4::Int64 = 1 5: unless (Base.not_int)((#temp#@_4::Int64 === (Base.add_int)(SSAValue(6), 1)::Int64)::Bool)::Bool goto 49 SSAValue(7) = #temp#@_4::Int64 SSAValue(8) = (Base.add_int)(#temp#@_4::Int64, 1)::Int64 #temp#@_4::Int64 = SSAValue(8) # line 9: $(Expr(:inbounds, false)) # meta: location random.jl rand 282 # meta: location random.jl rand 143 # meta: location random.jl reserve_1 132 unless ((Core.getfield)(Base.Random.GLOBAL_RNG, :idx)::Int64 === (Base.sext_int)(Int64, Base.Random.MTCacheLength)::Int64)::Bool goto 26 # meta: location random.jl gen_rand 128 SSAValue(3) = (Core.getfield)(Base.Random.GLOBAL_RNG, :state)::Base.dSFMT.DSFMT_state SSAValue(2) = (Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1} $(Expr(:invoke, MethodInstance for dsfmt_fill_array_close1_open2!(::Base.dSFMT.DSFMT_state, ::Ptr{Float64}, ::Int64), :(Base.Random.dsfmt_fill_array_close1_open2!), SSAValue(3), :($(Expr(:foreigncall, :(:jl_array_ptr), Ptr{Float64}, svec(Any), SSAValue(2), 0))), :((Base.arraylen)((Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1})::Int64))) # line 129: # meta: location random.jl mt_setfull! 123 (Core.setfield!)(Base.Random.GLOBAL_RNG, :idx, 0)::Int64 # meta: pop location # meta: pop location goto 27 26: 27: # meta: pop location # meta: location random.jl rand_inbounds 139 # meta: location random.jl rand_inbounds 138 # meta: location random.jl mt_pop! 125 $(Expr(:inbounds, true)) SSAValue(4) = (Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1} SSAValue(5) = (Base.add_int)((Core.getfield)(Base.Random.GLOBAL_RNG, :idx)::Int64, 1)::Int64 (Core.setfield!)(Base.Random.GLOBAL_RNG, :idx, SSAValue(5))::Int64 #temp#@_7::Float64 = (Base.arrayref)(SSAValue(4), SSAValue(5))::Float64 goto 39 $(Expr(:inbounds, :pop)) 39: # meta: pop location # meta: pop location # meta: pop location # meta: pop location # meta: pop location $(Expr(:inbounds, :pop)) s::Float64 = (Base.add_float)(s::Float64, (Base.mul_float)((Core.getfield)(#self#::#h#1{Float64}, :c)::Float64, (Base.sub_float)(#temp#@_7::Float64, 1.0)::Float64)::Float64)::Float64 47: goto 5 49: # line 11: return (Base.div_float)(s::Float64, (Base.sitofp)(Float64, N::Int64)::Float64)::Float64 end::Float64
次のセルで使用されている function-like object に関しては次のリンク先を参照.
https://docs.julialang.org/en/stable/manual/methods/#Function-like-objects-1
struct Hoge
d::Float64
end
function (hoge::Hoge)(N)
s = 0.0
for i in 1:N
s += hoge.d*rand()
end
return s/N
end
k = Hoge(2*Float64(π))
@time k(10^8)
0.229560 seconds (1.71 k allocations: 87.893 KiB)
3.1416948055954266
@code_warntype k(10^8)
Variables: hoge::Hoge N::Int64 i <optimized out> #temp#@_4::Int64 s::Float64 #temp#@_6 <optimized out> #temp#@_7::Float64 Body: begin s::Float64 = 0.0 # line 7: SSAValue(6) = (Base.select_value)((Base.sle_int)(1, N::Int64)::Bool, N::Int64, (Base.sub_int)(1, 1)::Int64)::Int64 #temp#@_4::Int64 = 1 5: unless (Base.not_int)((#temp#@_4::Int64 === (Base.add_int)(SSAValue(6), 1)::Int64)::Bool)::Bool goto 49 SSAValue(7) = #temp#@_4::Int64 SSAValue(8) = (Base.add_int)(#temp#@_4::Int64, 1)::Int64 #temp#@_4::Int64 = SSAValue(8) # line 8: $(Expr(:inbounds, false)) # meta: location random.jl rand 282 # meta: location random.jl rand 143 # meta: location random.jl reserve_1 132 unless ((Core.getfield)(Base.Random.GLOBAL_RNG, :idx)::Int64 === (Base.sext_int)(Int64, Base.Random.MTCacheLength)::Int64)::Bool goto 26 # meta: location random.jl gen_rand 128 SSAValue(3) = (Core.getfield)(Base.Random.GLOBAL_RNG, :state)::Base.dSFMT.DSFMT_state SSAValue(2) = (Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1} $(Expr(:invoke, MethodInstance for dsfmt_fill_array_close1_open2!(::Base.dSFMT.DSFMT_state, ::Ptr{Float64}, ::Int64), :(Base.Random.dsfmt_fill_array_close1_open2!), SSAValue(3), :($(Expr(:foreigncall, :(:jl_array_ptr), Ptr{Float64}, svec(Any), SSAValue(2), 0))), :((Base.arraylen)((Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1})::Int64))) # line 129: # meta: location random.jl mt_setfull! 123 (Core.setfield!)(Base.Random.GLOBAL_RNG, :idx, 0)::Int64 # meta: pop location # meta: pop location goto 27 26: 27: # meta: pop location # meta: location random.jl rand_inbounds 139 # meta: location random.jl rand_inbounds 138 # meta: location random.jl mt_pop! 125 $(Expr(:inbounds, true)) SSAValue(4) = (Core.getfield)(Base.Random.GLOBAL_RNG, :vals)::Array{Float64,1} SSAValue(5) = (Base.add_int)((Core.getfield)(Base.Random.GLOBAL_RNG, :idx)::Int64, 1)::Int64 (Core.setfield!)(Base.Random.GLOBAL_RNG, :idx, SSAValue(5))::Int64 #temp#@_7::Float64 = (Base.arrayref)(SSAValue(4), SSAValue(5))::Float64 goto 39 $(Expr(:inbounds, :pop)) 39: # meta: pop location # meta: pop location # meta: pop location # meta: pop location # meta: pop location $(Expr(:inbounds, :pop)) s::Float64 = (Base.add_float)(s::Float64, (Base.mul_float)((Core.getfield)(hoge::Hoge, :d)::Float64, (Base.sub_float)(#temp#@_7::Float64, 1.0)::Float64)::Float64)::Float64 47: goto 5 49: # line 10: return (Base.div_float)(s::Float64, (Base.sitofp)(Float64, N::Int64)::Float64)::Float64 end::Float64