如果x ==没有,我是否需要类型断言?

时间:2017-07-19 15:14:21

标签: julia

@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

1 个答案:

答案 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仍会将它们内联。使它们比单个文字值更复杂,它不会内联。