Julia @code_warntype 揭示了隐藏的临时变量#temp#
Julia @code_warntype reveals hidden temporary variable #temp#
我 运行 最近对我的代码进行了类型稳定性检查。当我对其调用 @code_warntype
时,我得到以下输出:
Variables:
#unused#::IHT.#kw##L0_reg
#temp#@_2::Array{Any,1}
::IHT.#L0_reg
x::Array{Float64,2}
y::Array{Float64,1}
k::Int64
#temp#@_7::Bool
#temp#@_8::Bool
max_iter::Int64
max_step::Int64
quiet::Bool
v::IHT.IHTVariables{Float64,Array{Float64,1}}
tol::Float64
#temp#@_14::Int64
#temp#@_15::Int64
#temp#@_16::Int64
#temp#@_17::Any
###...###
#temp#@_17::Any = (Core.arrayref(#temp#@_2::Array{Any,1},#temp#@_16::Int64)::Any
###...###
v::IHT.IHTVariables{Float64,Array{Float64,1}} = (Core.typeassert)((Core.arrayref)(#temp#@_2::Array{Any,1},(Base.box)(Int64,(Base.add_int)(#temp#@_16::Int64,1)))::Any,IHT.IHTVariables{Float64,Array{Float64,1}})::IHT.IHTVariables{Float64,Array{Float64,1}}
最小工作示例,使用我的 IHT.jl 包:
Pkg.clone("https://github.com/klkeys/IHT.jl")
n = 100; p = 250; k = 2;
x = randn(n,p)
b = zeros(p); b[1:k] = randn(k); shuffle!(b)
y = x*b + randn(n)
@code_warntype L0_reg(x, y, k, quiet=true)
编译器似乎正在使用 #temp#
读取函数 L0_reg
的参数。函数参数是完全指定的。这个邪恶的小 #temp#@_2
变量从何而来?我能告诉编译器它的类型是什么吗? (希望不是 Array{Any,1}
...)
您可以使用 @code_lowered
查看 #temp#
变量的来源:
julia> @code_lowered L0_reg(x, y, k, quiet=true)
LambdaInfo template for (::IHT.#kw##L0_reg){T<:Union{Float32,Float64}}(::Array{Any,1}, ::IHT.#L0_reg, x::DenseArray{T,2}, y::DenseArray{T,1}, k::Int64)
:(begin
NewvarNode(:(temp))
NewvarNode(:(tol))
#temp#@_7 = true
#temp#@_8 = true
max_iter = 100
max_step = 50
quiet = true
SSAValue(0) = (IHT.colon)(1,(Base.length)(#temp#@_2) >> 1)
#temp#@_14 = (Base.start)(SSAValue(0))
10:
unless !((Base.done)(SSAValue(0),#temp#@_14)) goto 42
SSAValue(1) = (Base.next)(SSAValue(0),#temp#@_14)
#temp#@_15 = (Core.getfield)(SSAValue(1),1)
#temp#@_14 = (Core.getfield)(SSAValue(1),2)
#temp#@_16 = #temp#@_15 * 2 - 1
#temp#@_17 = (Core.arrayref)(#temp#@_2,#temp#@_16)
unless #temp#@_17 === :quiet goto 20
quiet = (Core.typeassert)((Core.arrayref)(#temp#@_2,#temp#@_16 + 1),IHT.Bool)
goto 40
20:
unless #temp#@_17 === :max_step goto 24
max_step = (Core.typeassert)((Core.arrayref)(#temp#@_2,#temp#@_16 + 1),IHT.Int)
goto 40
24:
unless #temp#@_17 === :max_iter goto 28
max_iter = (Core.typeassert)((Core.arrayref)(#temp#@_2,#temp#@_16 + 1),IHT.Int)
goto 40
28:
unless #temp#@_17 === :tol goto 33
tol = (Core.typeassert)((Core.arrayref)(#temp#@_2,#temp#@_16 + 1),IHT.Float)
#temp#@_8 = false
goto 40
33:
unless #temp#@_17 === :temp goto 38
temp = (Core.typeassert)((Core.arrayref)(#temp#@_2,#temp#@_16 + 1),(Core.apply_type)(IHT.IHTVariables,$(Expr(:static_parameter, 1))))
#temp#@_7 = false
goto 40
38:
(Base.kwerr)(#temp#@_2,,x,y,k)
40:
goto 10
42:
unless #temp#@_7 goto 45
temp = (IHT.IHTVariables)(x,y,k)
45:
unless #temp#@_8 goto 48
tol = (IHT.convert)($(Expr(:static_parameter, 1)),0.0001)
48:
return (IHT.#L0_reg#75)(temp,tol,max_iter,max_step,quiet,,x,y,k)
end)
在这种情况下,临时值(特别是 #temp#@_2
)来自关键字参数。这对于关键字参数来说是很正常的。
julia> f(; x=1) = x
f (generic function with 1 method)
julia> @code_warntype f(x=1)
Variables:
#unused#::#kw##f
#temp#@_2::Array{Any,1}
::#f
x::Any
#temp#@_5::Int64
#temp#@_6::Int64
#temp#@_7::Int64
#temp#@_8::Any
Body:
begin
x::Any = 1
SSAValue(2) = (Base.arraylen)(#temp#@_2::Array{Any,1})::Int64
SSAValue(3) = (Base.select_value)((Base.sle_int)(0,1)::Bool,(Base.box)(Int64,(Base.ashr_int)(SSAValue(2),(Base.box)(UInt64,1))),(Base.box)(Int64,(Base.shl_int)(SSAValue(2),(Base.box)(UInt64,(Base.box)(Int64,(Base.neg_int)(1))))))::Int64
SSAValue(4) = (Base.select_value)((Base.sle_int)(1,SSAValue(3))::Bool,SSAValue(3),(Base.box)(Int64,(Base.sub_int)(1,1)))::Int64
#temp#@_5::Int64 = 1
6:
unless (Base.box)(Base.Bool,(Base.not_int)((#temp#@_5::Int64 === (Base.box)(Int64,(Base.add_int)(SSAValue(4),1)))::Bool)) goto 21
SSAValue(5) = #temp#@_5::Int64
SSAValue(6) = (Base.box)(Int64,(Base.add_int)(#temp#@_5::Int64,1))
#temp#@_6::Int64 = SSAValue(5)
#temp#@_5::Int64 = SSAValue(6)
#temp#@_7::Int64 = (Base.box)(Int64,(Base.sub_int)((Base.box)(Int64,(Base.mul_int)(#temp#@_6::Int64,2)),1))
#temp#@_8::Any = (Core.arrayref)(#temp#@_2::Array{Any,1},#temp#@_7::Int64)::Any
unless (#temp#@_8::Any === :x)::Bool goto 17
x::Any = (Core.arrayref)(#temp#@_2::Array{Any,1},(Base.box)(Int64,(Base.add_int)(#temp#@_7::Int64,1)))::Any
goto 19
17:
(Base.throw)($(Expr(:new, :(Base.MethodError), :((Core.getfield)((Core.getfield)((Core.getfield)(#f,:name)::TypeName,:mt),:kwsorter)), :((Core.tuple)(#temp#@_2,)::Tuple{Array{Any,1},#f}))))::Union{}
19:
goto 6
21:
return x::Any
end::Any
已知关键字参数有 callsite overhead that can be somewhat worked around by declaring types。请注意,除非您的函数做的很少,否则关键字参数的排序实际上不太可能是一个巨大的瓶颈(尽管令人讨厌的 @code_warntype
输出)。
当您在关键字参数调用上执行 @code_warntype
时,您实际上是在查看关键字参数排序器的类型不稳定性,这是一个围绕实际函数自动生成的包装器。如您所见,代码最终调用 (IHT.#L0_reg#75)(temp,tol,max_iter,max_step,quiet,,x,y,k)
,这是一个采用位置参数的普通函数。所以@code_warntype
的输出在这种情况下几乎没有用。
我 运行 最近对我的代码进行了类型稳定性检查。当我对其调用 @code_warntype
时,我得到以下输出:
Variables:
#unused#::IHT.#kw##L0_reg
#temp#@_2::Array{Any,1}
::IHT.#L0_reg
x::Array{Float64,2}
y::Array{Float64,1}
k::Int64
#temp#@_7::Bool
#temp#@_8::Bool
max_iter::Int64
max_step::Int64
quiet::Bool
v::IHT.IHTVariables{Float64,Array{Float64,1}}
tol::Float64
#temp#@_14::Int64
#temp#@_15::Int64
#temp#@_16::Int64
#temp#@_17::Any
###...###
#temp#@_17::Any = (Core.arrayref(#temp#@_2::Array{Any,1},#temp#@_16::Int64)::Any
###...###
v::IHT.IHTVariables{Float64,Array{Float64,1}} = (Core.typeassert)((Core.arrayref)(#temp#@_2::Array{Any,1},(Base.box)(Int64,(Base.add_int)(#temp#@_16::Int64,1)))::Any,IHT.IHTVariables{Float64,Array{Float64,1}})::IHT.IHTVariables{Float64,Array{Float64,1}}
最小工作示例,使用我的 IHT.jl 包:
Pkg.clone("https://github.com/klkeys/IHT.jl")
n = 100; p = 250; k = 2;
x = randn(n,p)
b = zeros(p); b[1:k] = randn(k); shuffle!(b)
y = x*b + randn(n)
@code_warntype L0_reg(x, y, k, quiet=true)
编译器似乎正在使用 #temp#
读取函数 L0_reg
的参数。函数参数是完全指定的。这个邪恶的小 #temp#@_2
变量从何而来?我能告诉编译器它的类型是什么吗? (希望不是 Array{Any,1}
...)
您可以使用 @code_lowered
查看 #temp#
变量的来源:
julia> @code_lowered L0_reg(x, y, k, quiet=true)
LambdaInfo template for (::IHT.#kw##L0_reg){T<:Union{Float32,Float64}}(::Array{Any,1}, ::IHT.#L0_reg, x::DenseArray{T,2}, y::DenseArray{T,1}, k::Int64)
:(begin
NewvarNode(:(temp))
NewvarNode(:(tol))
#temp#@_7 = true
#temp#@_8 = true
max_iter = 100
max_step = 50
quiet = true
SSAValue(0) = (IHT.colon)(1,(Base.length)(#temp#@_2) >> 1)
#temp#@_14 = (Base.start)(SSAValue(0))
10:
unless !((Base.done)(SSAValue(0),#temp#@_14)) goto 42
SSAValue(1) = (Base.next)(SSAValue(0),#temp#@_14)
#temp#@_15 = (Core.getfield)(SSAValue(1),1)
#temp#@_14 = (Core.getfield)(SSAValue(1),2)
#temp#@_16 = #temp#@_15 * 2 - 1
#temp#@_17 = (Core.arrayref)(#temp#@_2,#temp#@_16)
unless #temp#@_17 === :quiet goto 20
quiet = (Core.typeassert)((Core.arrayref)(#temp#@_2,#temp#@_16 + 1),IHT.Bool)
goto 40
20:
unless #temp#@_17 === :max_step goto 24
max_step = (Core.typeassert)((Core.arrayref)(#temp#@_2,#temp#@_16 + 1),IHT.Int)
goto 40
24:
unless #temp#@_17 === :max_iter goto 28
max_iter = (Core.typeassert)((Core.arrayref)(#temp#@_2,#temp#@_16 + 1),IHT.Int)
goto 40
28:
unless #temp#@_17 === :tol goto 33
tol = (Core.typeassert)((Core.arrayref)(#temp#@_2,#temp#@_16 + 1),IHT.Float)
#temp#@_8 = false
goto 40
33:
unless #temp#@_17 === :temp goto 38
temp = (Core.typeassert)((Core.arrayref)(#temp#@_2,#temp#@_16 + 1),(Core.apply_type)(IHT.IHTVariables,$(Expr(:static_parameter, 1))))
#temp#@_7 = false
goto 40
38:
(Base.kwerr)(#temp#@_2,,x,y,k)
40:
goto 10
42:
unless #temp#@_7 goto 45
temp = (IHT.IHTVariables)(x,y,k)
45:
unless #temp#@_8 goto 48
tol = (IHT.convert)($(Expr(:static_parameter, 1)),0.0001)
48:
return (IHT.#L0_reg#75)(temp,tol,max_iter,max_step,quiet,,x,y,k)
end)
在这种情况下,临时值(特别是 #temp#@_2
)来自关键字参数。这对于关键字参数来说是很正常的。
julia> f(; x=1) = x
f (generic function with 1 method)
julia> @code_warntype f(x=1)
Variables:
#unused#::#kw##f
#temp#@_2::Array{Any,1}
::#f
x::Any
#temp#@_5::Int64
#temp#@_6::Int64
#temp#@_7::Int64
#temp#@_8::Any
Body:
begin
x::Any = 1
SSAValue(2) = (Base.arraylen)(#temp#@_2::Array{Any,1})::Int64
SSAValue(3) = (Base.select_value)((Base.sle_int)(0,1)::Bool,(Base.box)(Int64,(Base.ashr_int)(SSAValue(2),(Base.box)(UInt64,1))),(Base.box)(Int64,(Base.shl_int)(SSAValue(2),(Base.box)(UInt64,(Base.box)(Int64,(Base.neg_int)(1))))))::Int64
SSAValue(4) = (Base.select_value)((Base.sle_int)(1,SSAValue(3))::Bool,SSAValue(3),(Base.box)(Int64,(Base.sub_int)(1,1)))::Int64
#temp#@_5::Int64 = 1
6:
unless (Base.box)(Base.Bool,(Base.not_int)((#temp#@_5::Int64 === (Base.box)(Int64,(Base.add_int)(SSAValue(4),1)))::Bool)) goto 21
SSAValue(5) = #temp#@_5::Int64
SSAValue(6) = (Base.box)(Int64,(Base.add_int)(#temp#@_5::Int64,1))
#temp#@_6::Int64 = SSAValue(5)
#temp#@_5::Int64 = SSAValue(6)
#temp#@_7::Int64 = (Base.box)(Int64,(Base.sub_int)((Base.box)(Int64,(Base.mul_int)(#temp#@_6::Int64,2)),1))
#temp#@_8::Any = (Core.arrayref)(#temp#@_2::Array{Any,1},#temp#@_7::Int64)::Any
unless (#temp#@_8::Any === :x)::Bool goto 17
x::Any = (Core.arrayref)(#temp#@_2::Array{Any,1},(Base.box)(Int64,(Base.add_int)(#temp#@_7::Int64,1)))::Any
goto 19
17:
(Base.throw)($(Expr(:new, :(Base.MethodError), :((Core.getfield)((Core.getfield)((Core.getfield)(#f,:name)::TypeName,:mt),:kwsorter)), :((Core.tuple)(#temp#@_2,)::Tuple{Array{Any,1},#f}))))::Union{}
19:
goto 6
21:
return x::Any
end::Any
已知关键字参数有 callsite overhead that can be somewhat worked around by declaring types。请注意,除非您的函数做的很少,否则关键字参数的排序实际上不太可能是一个巨大的瓶颈(尽管令人讨厌的 @code_warntype
输出)。
当您在关键字参数调用上执行 @code_warntype
时,您实际上是在查看关键字参数排序器的类型不稳定性,这是一个围绕实际函数自动生成的包装器。如您所见,代码最终调用 (IHT.#L0_reg#75)(temp,tol,max_iter,max_step,quiet,,x,y,k)
,这是一个采用位置参数的普通函数。所以@code_warntype
的输出在这种情况下几乎没有用。