2017-07-19 5 views
2
@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 

は、私は右のfを呼び出すときにジュリアはxの種類を知らないと言うことになっている場合の腕の中でタイプのアサートが必要です。それは動的なディスパッチを行うでしょうか? (もちろん、派遣の両方のタイプが正しい結果を与える)、パフォーマンス上の理由のためには、私はX ==何

(Main.f)(a::Union{Int64, Void})::Int64

、私はfを呼び出すときにジュリアが静的ディスパッチの代わりに、動的ディスパッチを行うことができるように腕の中で私のコードに注釈を付ける必要がありますか?

@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 

答えて

4

それはあなたがそれを書いたとして、ジュリアを例に動的ディスパッチをやってではないことは注目に値します。 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を呼び出しますが、推論はその時点でそのことを利用していません。だから、ロットのネストされた枝で終わる。 ===厳しいへ==を変更し、あなたが探しているの最適化を得る:

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注釈があるにも関わらず、ジュリアが依然としてインラインになっていることは簡単なので、だからです。それらを1つのリテラル値よりも複雑にして、インライン化しません。

関連する問題