If you check the output of @code_warntype
, you’ll find
julia> @code_warntype test2(2)
MethodInstance for test2(::Int64)
from test2(N) in Main at REPL[5]:1
Arguments
#self#::Core.Const(test2)
N::Int64
Locals
@_3::Union{Nothing, Tuple{Any, Union{Bool, Tuple}}}
kiter::Base.Iterators.ProductIterator
k::Any
Body::Nothing
1 ─ %1 = Base.getproperty(Main.Iterators, :product)::Core.Const(Base.Iterators.product)
│ %2 = Main.rfftfreq(N, N)::Frequencies{Float64}
│ %3 = Main.rfftfreq(N, N)::Frequencies{Float64}
│ %4 = Base.vect(%2, %3)::Vector{Frequencies{Float64}}
│ (kiter = Core._apply_iterate(Base.iterate, %1, %4))
│ %6 = kiter::Base.Iterators.ProductIterator
│ (@_3 = Base.iterate(%6))
│ %8 = (@_3 === nothing)::Bool
│ %9 = Base.not_int(%8)::Bool
└── goto #6 if not %9
2 ┄ %11 = @_3::Tuple{Any, Union{Bool, Tuple}}
│ (k = Core.getfield(%11, 1))
│ %13 = Core.getfield(%11, 2)::Union{Bool, Tuple}
│ %14 = Base.getindex(k, 1)::Any
│ %15 = (%14 < 0)::Any
└── goto #4 if not %15
3 ─ Main.print("hello")
4 ┄ (@_3 = Base.iterate(%6, %13))
│ %19 = (@_3::Union{Nothing, Tuple{Any, Tuple{Any, Vararg{Any}}}} === nothing)::Bool
│ %20 = Base.not_int(%19)::Bool
└── goto #6 if not %20
5 ─ goto #2
6 ┄ return nothing
so the types of k
and kiter
are not being inferred, and also there’s the allocation of the array in the first place. You may try this instead:
julia> function test3(N, ndims)
kiter = Iterators.product(ntuple(_ -> rfftfreq(N,N), ndims)...)
for k in kiter
if (k[1] < 0)
print("hello")
end
end
end
test3 (generic function with 2 methods)
This should be almost as fast as test1
if the number of dimensions is known at compile time:
julia> @btime test1(10);
38.884 ns (0 allocations: 0 bytes)
julia> @btime test3(10, Val(2));
49.298 ns (0 allocations: 0 bytes)
Otherwise, if the number of dimensions is only known at runtime, it should be about as fast as test2
:
julia> @btime test2(10);
15.831 μs (149 allocations: 5.92 KiB)
julia> @btime test3(10, 2);
15.756 μs (149 allocations: 5.88 KiB)