@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
视为常量而无法分辨。
答案 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
包装在函数中,它将再次发生。
因此,一般而言,结论可能是,在标准情况下,已编译的代码中您会期望不断传播,但是在复杂的情况下,编译器可能不够聪明(当然,将来可以改进)。