如何在julia中进行常量传播?
我希望像如何在julia中进行常量传播?,julia,Julia,我希望像f(1,2)这样的调用可以直接编译为f2(1),而不需要分支,因为2是一个常量 @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
是一个常量
@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
然而,从它生成的代码的外观来看,恒定传播并没有发生。有没有可能在现实生活中会发生常量传播,但诸如
@code\u native
或@code\u warntype
之类的监控工具无法判断,因为它们不将2
视为常量。如果在带有常量参数的代码编译部分调用f
,则会发生常量传播(例如,从函数调用)
因此,在您的情况下,您有:
@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)
;}
作为旁注,AFAIK@pure
宏不应与调用泛型函数的函数一起使用,如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
有趣的是,对于g
而言,常量传播如上所述,但对于h
而言,不是这样,但是如果将h
包装到函数中,它会再次发生
因此,一般来说,结论可能是,在编译代码的标准情况下,可以预期会发生不断的传播,但在复杂情况下,编译器可能不够聪明(当然,这在将来可能会有所改进)
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