Performant way to create ntuple?

Consider the following simplified code for Julia 0.6:

using BenchmarkTools
@noinline g(x) = x[1]

function f1(M, ::Type{Val{N}}) where N
    s = 0
    for j = 1:M
        x = ntuple(i -> i + j, Val{N})
        s += g(x)
    end
    return s
end

@generated function f2(M, ::Type{Val{N}}) where N
    t = Expr(:tuple, [:($k+j) for k=1:N]...)
    return quote
        s = 0
        for j = 1:M
            x = $t
            s += g(x)
        end
        return s
    end
end

@btime f1(10000, Val{3})
@btime f2(10000, Val{3})

Timing results are

julia> @btime f1(10000, Val{3})
  2.647 ms (77422 allocations: 1.33 MiB)
50015000

julia> @btime f2(10000, Val{3})
  22.971 μs (0 allocations: 0 bytes)
50015000

Is there any way to create an NTuple performantly without resorting to generated functions?

2 Likes

This is performance of captured variables in closures · Issue #15276 · JuliaLang/julia · GitHub. You can work around it using a let block

using BenchmarkTools
@noinline g(x) = x[1]

function f1(M, ::Type{Val{N}}) where N
    s = 0
    for j = 1:M
        let j = j
            x = ntuple(i -> i + j, Val{N})
            s += g(x)
        end
    end
    return s
end
5 Likes

Thanks. I’ve read that issue before but apparently not actively enough to make the connection. This solves my non-simplified problem as well. Great help.

1 Like