Consider this
julia> struct data{N,G} end
julia> const binomsum_cache = [[1]];
julia> function impure_binomsum(n::Int, i::Int)
for k=length(binomsum_cache)+1:n
push!(binomsum_cache, cumsum([binomial(k,q) for q=0:k]))
end
i!=0 ? binomsum_cache[n][i] : 0
end;
julia> Base.@pure function pure_binomsum(n::Int, i::Int)
for k=length(binomsum_cache)+1:n
push!(binomsum_cache, cumsum([binomial(k,q) for q=0:k]))
end
i!=0 ? binomsum_cache[n][i] : 0
end;
Then compare
julia> pure(::data{N,G}) where {N,G} = float(pure_binomsum(N,G));
julia> impure(::data{N,G}) where {N,G} = float(impure_binomsum(N,G));
julia> @code_warntype impure(data{5,4}())
Body::Float64
1 1 ─ %1 = invoke Main.impure_binomsum($(Expr(:static_parameter, 1))::Int64, $(Expr(:static_parameter, 2))::Int64)::Int64
│ %2 = (Base.sitofp)(Float64, %1)::Float64 │╻╷ float
└── return %2 │
julia> @code_warntype pure(data{5,4}())
Body::Float64
1 1 ─ return 26.0 │
with @inline
it is much much worse
julia> @inline function inline_binomsum(n::Int, i::Int)
for k=length(binomsum_cache)+1:n
push!(binomsum_cache, cumsum([binomial(k,q) for q=0:k]))
end
i!=0 ? binomsum_cache[n][i] : 0
end;
julia> inline(::data{N,G}) where {N,G} = float(inline_binomsum(N,G));
julia> @code_warntype inline(data{5,4}())
Body::Float64
1 1 ── %1 = $(Expr(:static_parameter, 1))::Core.Compiler.Const(5, false)
│ %2 = $(Expr(:static_parameter, 2))::Core.Compiler.Const(4, false)
│ %3 = Main.binomsum_cache::Array{Array{Int64,1},1} │╻ inline_binomsum
│ %4 = (Base.arraylen)(%3)::Int64 ││╻ length
│ %5 = (Base.add_int)(%4, 1)::Int64 ││╻ +
│ %6 = (Base.sle_int)(%5, %1)::Bool │││╻╷╷╷ Type
│ (Base.sub_int)(%1, %5) ││││╻ unitrange_last
│ %8 = (Base.sub_int)(%5, 1)::Int64 │││││┃ -
│ %9 = (Base.ifelse)(%6, %1, %8)::Int64 │││││
│ %10 = (Base.slt_int)(%9, %5)::Bool │││╻╷╷ isempty
└─── goto #3 if not %10 │││
2 ── goto #4 │││
3 ── goto #4 │││
4 ┄─ %14 = φ (#2 => true, #3 => false)::Bool ││
│ %15 = φ (#3 => %5)::Int64 ││
│ %16 = φ (#3 => %5)::Int64 ││
│ %17 = (Base.not_int)(%14)::Bool ││
└─── goto #31 if not %17 ││
5 ┄─ %19 = φ (#4 => %15, #30 => %71)::Int64 ││
│ %20 = φ (#4 => %16, #30 => %72)::Int64 ││
│ %21 = %new(getfield(Main, Symbol("##15#16")){Int64}, %19)::getfield(Main, Symbol("##15#16")){Int64}
│ %22 = (Base.sle_int)(0, %19)::Bool │││╻╷╷╷ Type
│ (Base.sub_int)(%19, 0) ││││╻ unitrange_last
│ %24 = (Base.ifelse)(%22, %19, -1)::Int64 │││││
│ %25 = %new(UnitRange{Int64}, 0, %24)::UnitRange{Int64}│││
│ %26 = %new(Base.Generator{UnitRange{Int64},getfield(Main, Symbol("##15#16")){Int64}}, %21, %25)::Base.Generator{UnitRange{Int64},getfield(Main, Symbol("##15#16")){Int64}}
│ %27 = invoke Base.collect(%26::Base.Generator{UnitRange{Int64},getfield(Main, Symbol("##15#16")){Int64}})::Array{Int64,1}
│ %28 = Base.cumsum::typeof(cumsum) ││╻ cumsum
│ %29 = (Base.sle_int)(1, 1)::Bool │││╻╷╷╷╷ #cumsum
└─── goto #7 if not %29 ││││┃│││ isempty
6 ── %31 = (Base.sle_int)(1, 0)::Bool │││││┃│││ iterate
└─── goto #8 ││││││┃│ iterate
7 ── nothing │
8 ┄─ %34 = φ (#6 => %31, #7 => false)::Bool │││││││┃ iterate
└─── goto #10 if not %34 ││││││││
9 ── invoke Base.getindex(()::Tuple{}, 1::Int64) ││││││││
└─── $(Expr(:unreachable)) ││││││││
10 ─ goto #12 ││││││││
11 ─ $(Expr(:unreachable)) ││││││││
12 ┄ goto #13 │││││││
13 ─ goto #14 │││││╻ iterate
14 ─ goto #15 │││││
15 ─ %43 = invoke Base.:(#cumsum#570)(1::Int64, %28::Function, %27::Array{Int64,1})::Array{Int64,1}
└─── goto #16 ││││
16 ─ goto #17 │││
17 ─ %46 = Main.binomsum_cache::Array{Array{Int64,1},1} ││
│ %47 = (Core.lshr_int)(1, 63)::Int64 │││╻╷╷╷╷╷╷ _growend!
│ %48 = (Core.trunc_int)(Core.UInt8, %47)::UInt8 ││││┃│││││ cconvert
│ %49 = (Core.eq_int)(%48, 0x01)::Bool │││││┃││││ convert
└─── goto #19 if not %49 ││││││┃││ Type
18 ─ invoke Core.throw_inexacterror(:check_top_bit::Symbol, Int64::Any, 1::Int64)
└─── $(Expr(:unreachable)) ││││││││┃ check_top_bit
19 ─ goto #20 │││││││││
20 ─ %54 = (Core.bitcast)(Core.UInt64, 1)::UInt64 ││││││││
└─── goto #21 ││││││││
21 ─ goto #22 │││││││
22 ─ goto #23 ││││││
23 ─ goto #24 │││││
24 ─ $(Expr(:foreigncall, :(:jl_array_grow_end), Nothing, svec(Any, UInt64), :(:ccall), 2, :(%46), :(%54), :(%54)))
└─── goto #25 ││││
25 ─ %61 = (Base.arraysize)(%46, 1)::Int64 │││╻╷╷╷╷ lastindex
│ %62 = (Base.slt_int)(%61, 0)::Bool ││││╻╷╷╷ eachindex
│ %63 = (Base.ifelse)(%62, 0, %61)::Int64 │││││┃│││││ axes1
│ (Base.arrayset)(true, %46, %43, %63) │││╻ setindex!
└─── goto #26 ││╻ push!
26 ─ %66 = (%20 === %9)::Bool │││╻ ==
└─── goto #28 if not %66 │││
27 ─ goto #29 │││
28 ─ %69 = (Base.add_int)(%20, 1)::Int64 │││╻ +
└─── goto #29 ││╻ iterate
29 ┄ %71 = φ (#28 => %69)::Int64 ││
│ %72 = φ (#28 => %69)::Int64 ││
│ %73 = φ (#27 => true, #28 => false)::Bool ││
│ %74 = (Base.not_int)(%73)::Bool ││
└─── goto #31 if not %74 ││
30 ─ goto #5 ││
31 ─ %77 = invoke Base.getindex(Main.binomsum_cache::Array{Array{Int64,1},1}, %1::Int64)::Array{Int64,1}
│ %78 = (Base.arrayref)(true, %77, %2)::Int64 ││╻ getindex
└─── goto #32 ││
32 ─ %80 = (Base.sitofp)(Float64, %78)::Float64 ││╻╷ Type
└─── return %80 │
this is why I wanted to use @pure
in my situation, it provides performance boost
how can I achieve similar optimizations without @pure
in this situation?