I want to evaluate x^y
at compile time.
The reason is to facilitate efficient construction of NTuple
using ntuple(f, Val(N)) method
.
Compare bar1
and bar2
functions below:
struct Foo end
getX(::Foo) = 5
function bar1(foo::Foo)
X = getX(foo)
Y = 2^X
return (ntuple(i->1, Val(X)), ntuple(i->2, Val(Y)))
end
function bar2(foo::Foo)
X = getX(foo)
Y = prod(ntuple(i->2, Val(X)))
return (ntuple(i->1, Val(X)), ntuple(i->2, Val(Y)))
end
Benchmarking and @code_warntype
suggest that exponentiation is evaluated at run time for bar1
and compile time for bar2
.
- Is this the correct interpretation?
- If so why isn’t
x^y
evaulated at compile time? (I believe +, -, +, / operators are?) - On a practical note, is there any reason not to use
bar2
?
Julia-1.1.0> using BenchmarkTools
Julia-1.1.0> @btime bar1(foo)
2.965 μs (1 allocation: 304 bytes)
((1, 1, 1, 1, 1), (2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2))
Julia-1.1.0> @btime bar2(foo)
7.184 ns (0 allocations: 0 bytes)
((1, 1, 1, 1, 1), (2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2))
Julia-1.1.0> @code_warntype bar1(foo)
Body::Tuple{NTuple{5,Int64},Any}
1 ─ nothing
2 ┄ %2 = φ (#1 => 2, #6 => %16)::Int64
│ %3 = φ (#1 => 2, #6 => %23)::Int64
│ %4 = φ (#1 => 2, #6 => %15)::Int64
│ %5 = (Base.slt_int)(0, %4)::Bool
└── goto #7 if not %5
3 ─ %7 = (Base.cttz_int)(%4)::Int64
│ %8 = (Base.add_int)(%7, 1)::Int64
│ %9 = (Base.sle_int)(0, %8)::Bool
│ %10 = (Base.bitcast)(UInt64, %8)::UInt64
│ %11 = (Base.ashr_int)(%4, %10)::Int64
│ %12 = (Base.neg_int)(%8)::Int64
│ %13 = (Base.bitcast)(UInt64, %12)::UInt64
│ %14 = (Base.shl_int)(%4, %13)::Int64
└── %15 = (Base.ifelse)(%9, %11, %14)::Int64
4 ┄ %16 = φ (#3 => %2, #5 => %21)::Int64
│ %17 = φ (#3 => %8, #5 => %18)::Int64
│ %18 = (Base.sub_int)(%17, 1)::Int64
│ %19 = (Base.sle_int)(0, %18)::Bool
└── goto #6 if not %19
5 ─ %21 = (Base.mul_int)(%16, %16)::Int64
└── goto #4
6 ─ %23 = (Base.mul_int)(%3, %16)::Int64
└── goto #2
7 ─ goto #8
8 ─ goto #9
9 ─ %27 = %new(Main.:(##6#8))::Core.Compiler.Const(getfield(Main, Symbol("##6#8"))(), false)
│ %28 = invoke Main.Val(%3::Int64)::Val{_1} where _1
│ %29 = (Main.ntuple)(%27, %28)::Any
│ %30 = (Core.tuple)((1, 1, 1, 1, 1), %29)::Core.Compiler.PartialTuple(Tuple{NTuple{5,Int64},Any}, Any[Const((1, 1, 1, 1, 1), false), Any])
└── return %30
Julia-1.1.0> @code_warntype bar2(foo)
Body::Tuple{NTuple{5,Int64},NTuple{32,Int64}}
1 ─ %1 = (Core.tuple)((1, 1, 1, 1, 1), (2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2))::Core.Compiler.Const(((1, 1, 1, 1, 1), (2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2)), false)
└── return %1