如何在茱莉亚不断繁殖?

时间:2019-01-04 12:46:51

标签: julia

@noinline f1(x::Int) = x + 1
@noinline f2(x::Int) = x + 2

@Base.pure function f(x::Int, p::Int)
    if p == 1
        return f1(x)
    else
        return f2(x)
    end
end

由于f(1, 2)是常量,我希望将诸如f2(1)之类的调用直接编译为2而不会分支。

@code_warntype f(1, 2)
Body::Int64
│╻ ==5 1 ─ %1 = (p === 1)::Bool
│   └──      goto #3 if not %1
│ 6 2 ─ %3 = invoke Main.f1(_2::Int64)::Int64
│   └──      return %3
│ 8 3 ─ %5 = invoke Main.f2(_2::Int64)::Int64
│   └──      return %5

@code_native f(1, 2)
    .text
; Function f {
; Location: In[1]:5
; Function ==; {
; Location: In[1]:5
    pushq   %rax
    cmpq    $1, %rsi
;}
    jne L21
; Location: In[1]:6
    movabsq $julia_f1_35810, %rax
    callq   *%rax
    popq    %rcx
    retq
; Location: In[1]:8
L21:
    movabsq $julia_f2_35811, %rax
    callq   *%rax
    popq    %rcx
    retq
    nopw    %cs:(%rax,%rax)
;}

但是,从它生成的代码的外观来看,不会发生持续传播。常量传播是否可能在现实生活中发生,但是诸如@code_native@code_warntype之类的监控工具由于无法将2视为常量而无法分辨。

1 个答案:

答案 0 :(得分:1)

如果您在带有常量参数(例如,从函数调用)的代码的已编译部分中调用f,则会发生恒定传播。

因此,您有:

julia> @noinline f1(x::Int) = x + 1
f1 (generic function with 1 method)

julia> @noinline f2(x::Int) = x + 2
f2 (generic function with 1 method)

julia> function f(x::Int, p::Int)
           if p == 1
               return f1(x)
           else
               return f2(x)
           end
       end
f (generic function with 1 method)

julia> @code_warntype f(1,2)
Body::Int64
2 1 ─ %1 = (p === 1)::Bool                                                                                                                                                                     │╻ ==
  └──      goto #3 if not %1                                                                                                                                                                   │
3 2 ─ %3 = invoke Main.f1(_2::Int64)::Int64                                                                                                                                                    │
  └──      return %3                                                                                                                                                                           │
5 3 ─ %5 = invoke Main.f2(_2::Int64)::Int64                                                                                                                                                    │
  └──      return %5                                                                                                                                                                           │

julia> g() = f(1,2)
g (generic function with 1 method)

julia> @code_warntype g()
Body::Int64
1 1 ─     return 3

julia> h(x) = f(x,2)
h (generic function with 1 method)

julia> @code_warntype h(10)
Body::Int64
1 1 ─ %1 = invoke Main.f2(_2::Int64)::Int64                                                                                                                                                     │╻ f
  └──      return %1

请注意,AFAIK @pure宏不应与调用通用函数的函数一起使用,就像f一样。

编辑:我在这里找到了一个有趣的特殊情况:

julia> f(x,p) = (p==1 ? sin : cos)(x)
f (generic function with 1 method)

julia> @code_warntype f(10, 2)
Body::Any
1 1 ─ %1 = (p === 1)::Bool                                                                                                                                                                     │╻ ==
  └──      goto #3 if not %1                                                                                                                                                                   │
  2 ─ %3 = Main.sin::Core.Compiler.Const(sin, false)                                                                                                                                           │
  └──      goto #4                                                                                                                                                                             │
  3 ─ %5 = Main.cos::Core.Compiler.Const(cos, false)                                                                                                                                           │
  4 ┄ %6 = φ (#2 => %3, #3 => %5)::Union{typeof(cos), typeof(sin)}                                                                                                                             │
  │   %7 = (%6)(x)::Any                                                                                                                                                                        │
  └──      return %7                                                                                                                                                                           │

julia> g() = f(10, 2)
g (generic function with 1 method)

julia> @code_warntype g()
Body::Float64
1 1 ─ %1 = invoke Base.Math.cos(10.0::Float64)::Float64                                                                                                                                        │╻╷ f
  └──      return %1                                                                                                                                                                           │

julia> h(x) = f(x, 2)
h (generic function with 1 method)

julia> @code_warntype h(10)
Body::Any
1 1 ─ %1 = invoke Main.f(_2::Int64, 2::Int64)::Any                                                                                                                                                 │
  └──      return %1

julia> z() = h(10)
z (generic function with 1 method)

julia> @code_warntype z()
Body::Float64
1 1 ─ %1 = invoke Base.Math.cos(10.0::Float64)::Float64                                                                                                                                       │╻╷╷ h
  └──      return %1

有趣的是,对于g,常量传播如上所述发生,但是对于h却没有发生,但是如果将h包装在函数中,它将再次发生。

>

因此,一般而言,结论可能是,在标准情况下,已编译的代码中您会期望不断传播,但是在复杂的情况下,编译器可能不够聪明(当然,将来可以改进)。