@noinline g(x::Int) = x > 2 ? 1 : nothing
function test(x::Int)
a = g(x)
if a == nothing
f(a)
# Do I need f(a::Void)
else
f(a)
# Do I need f(a::Int)
end
end
@noinline f(x) = 1
@noinline f(x::Int) = 2
@noinline f(x::Void) = 3
我是正确的说Julia在致电x
时并不知道f
的类型。那么它会进行动态调度吗? (当然,两种类型的调度都会给出正确的结果)
(Main.f)(a::Union{Int64, Void})::Int64
出于性能原因,我是否需要在手臂中注释我的代码,以便在调用f
时Julia可以执行静态调度而不是动态调度?
@code_warntype test(3)
Variables:
#self#::#test
x::Int64
a::Union{Int64, Void}
#temp#@_4::Core.MethodInstance
#temp#@_5::Bool
#temp#@_6::Core.MethodInstance
#temp#@_7::Int64
#temp#@_8::Core.MethodInstance
#temp#@_9::Int64
Body:
begin
a::Union{Int64, Void} = $(Expr(:invoke, MethodInstance for g(::Int64), :(Main.g), :(x))) # line 4:
unless (a::Union{Int64, Void} isa Int64)::Bool goto 6
#temp#@_4::Core.MethodInstance = MethodInstance for ==(::Int64, ::Void)
goto 15
6:
unless (a::Union{Int64, Void} isa Void)::Bool goto 10
#temp#@_4::Core.MethodInstance = MethodInstance for ==(::Void, ::Void)
goto 15
10:
goto 12
12:
#temp#@_5::Bool = (a::Union{Int64, Void} == Main.nothing)::Bool
goto 17
15:
#temp#@_5::Bool = $(Expr(:invoke, :(#temp#@_4), :(Main.==), :(a), :(Main.nothing)))
17:
unless #temp#@_5::Bool goto 36 # line 5:
unless (a::Union{Int64, Void} isa Int64)::Bool goto 23
#temp#@_6::Core.MethodInstance = MethodInstance for f(::Int64)
goto 32
23:
unless (a::Union{Int64, Void} isa Void)::Bool goto 27
#temp#@_6::Core.MethodInstance = MethodInstance for f(::Void)
goto 32
27:
goto 29
29:
#temp#@_7::Int64 = (Main.f)(a::Union{Int64, Void})::Int64
goto 34
32:
#temp#@_7::Int64 = $(Expr(:invoke, :(#temp#@_6), :(Main.f), :(a)))
34:
return #temp#@_7::Int64
36: # line 7:
unless (a::Union{Int64, Void} isa Int64)::Bool goto 41
#temp#@_8::Core.MethodInstance = MethodInstance for f(::Int64)
goto 50
41:
unless (a::Union{Int64, Void} isa Void)::Bool goto 45
#temp#@_8::Core.MethodInstance = MethodInstance for f(::Void)
goto 50
45:
goto 47
47:
#temp#@_9::Int64 = (Main.f)(a::Union{Int64, Void})::Int64
goto 52
50:
#temp#@_9::Int64 = $(Expr(:invoke, :(#temp#@_8), :(Main.f), :(a)))
52:
return #temp#@_9::Int64
end::Int64
答案 0 :(得分:4)
值得注意的是Julia 并非在您编写示例时在示例中执行动态调度。 Julia 0.6实际上做了你想要在这里实现的优化。这就是以下几行:
unless (a::Union{Int64, Void} isa Int64)::Bool goto 23
#temp#@_6::Core.MethodInstance = MethodInstance for f(::Int64)
goto 32
23:
unless (a::Union{Int64, Void} isa Void)::Bool goto 27
#temp#@_6::Core.MethodInstance = MethodInstance for f(::Void)
goto 32
它正在“拆分”联合并显式添加分支以获取确切的方法实例。
你编写它的方式,Julia缺少==
的优化。在这种情况下,x == nothing
只是调用x === nothing
,但推理现在没有利用这一事实。所以你最终得到了嵌套分支的 lot 。将==
更改为更严格的===
,即可获得您正在寻找的优化:
julia> function test(x::Int)
a = g(x)
if a === nothing
# … same as above
julia> @code_warntype test(2)
Variables:
#self#::#test
x::Int64
a::Union{Int64, Void}
Body:
begin
a::Union{Int64, Void} = $(Expr(:invoke, MethodInstance for g(::Int64), :(Main.g), :(x))) # line 3:
unless (a::Union{Int64, Void} === Main.nothing)::Bool goto 6 # line 4:
return 3
6: # line 7:
return 2
end::Int64
这实际上比使用类型断言更有效。
有趣的是,f
方法如此简单,尽管有@noinline
注释,Julia仍会将它们内联。使它们比单个文字值更复杂,它不会内联。