# Looping through NamedTuple is slow

**URL:** <https://discourse.julialang.org/t/looping-through-namedtuple-is-slow/116745>\
**Category:** General Usage\
**Created:** [July 8, 2024, 2:38am UTC](https://discourse.julialang.org/t/looping-through-namedtuple-is-slow/116745 "2024-07-08T02:38:43Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![z-wang](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/z-wang/32/210507_2.png) [@z-wang](https://discourse.julialang.org/u/z-wang)\
**Post date:** [July 8, 2024, 2:38am UTC](https://discourse.julialang.org/t/looping-through-namedtuple-is-slow/116745/1 "2024-07-08T02:38:43Z")

</div>

Hi, i was comparing the performance between `NamedTuple` and `Dict`, and the results are very confusing.

In the following code, if the elements inside containers are accessed through a loop, the performance of `NamedTuple` is worse than `Dict`.

```jl
using BenchmarkTools

x = (a=1.0, b=1.0)
y = Dict(:a=>1.0, :b=>1.0)
indx = [:a, :b]

function func1(x, indx)
    for i in indx
        x[i]
    end
end

@btime func1($x, $indx) # 17.034 ns (0 allocations: 0 bytes)
@btime func1($y, $indx) # 9.300 ns (0 allocations: 0 bytes)

```

However, if we don’t use loop, the `NamedTuple` will have a huge performance gain. Could someone help me understand why this happens?

```jl
function func2(x)
    x[:a]
    x[:b]
end

@btime func2($x) # 2.700 ns (0 allocations: 0 bytes)
@btime func2($y) # 7.800 ns (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [July 8, 2024, 4:16am UTC](https://discourse.julialang.org/t/looping-through-namedtuple-is-slow/116745/2 "2024-07-08T04:16:00Z")

</div>

The difference is that in `func2`, the index is known at compile-time, but in `func1` it is not.

---

<div class="post-metadata">

**Author:** ![z-wang](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/z-wang/32/210507_2.png) [@z-wang](https://discourse.julialang.org/u/z-wang)\
**Post date:** [July 8, 2024, 4:22am UTC](https://discourse.julialang.org/t/looping-through-namedtuple-is-slow/116745/4 "2024-07-08T04:22:30Z")

</div>

Thank you! I’m curious why it matters much more for `NamedTuple` compared to `Dict`. The performance difference in `func1` and `func2` is quite minimal for the latter, but huge for `NamedTuple`.

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [July 8, 2024, 4:31am UTC](https://discourse.julialang.org/t/looping-through-namedtuple-is-slow/116745/5 "2024-07-08T04:31:39Z")

</div>

My guess is that `Dict` is optimized for the case where indicies are not known at compile-time, but `NamedTuple` is optimized for the case where they are. So you can pick the one that suits your case.

---

<div class="post-metadata">

**Author:** ![Per](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/per/32/10387_2.png) [@Per](https://discourse.julialang.org/u/Per)\
**Post date:** [July 8, 2024, 4:41am UTC](https://discourse.julialang.org/t/looping-through-namedtuple-is-slow/116745/6 "2024-07-08T04:41:08Z")

</div>

In `func2(::NamedTuple)` the compiler is probably able to figure out that `x[:a]` is never used, and the look-up has no side effects, so the function can directly return `x[:b]`. This makes the the difference seem bigger than it actually is.

---

<div class="post-metadata">

**Author:** ![DNF](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnf/32/10191_2.png) [@DNF](https://discourse.julialang.org/u/DNF)\
**Post date:** [July 8, 2024, 5:57am UTC](https://discourse.julialang.org/t/looping-through-namedtuple-is-slow/116745/7 "2024-07-08T05:57:59Z")

</div>

> [@z-wang](#):
>
> ```julia
> function func1(x, indx)
> for i in indx
> x[i]
> end
> end
> 
> ```

When benchmarking, you should avoid situations where the compiler can “optimize away” your code. I’m not sure exactly what happens in this case, since I cannot run your code now, but it’s better to do something like

```julia
function func1(x, indx)
    s = 0.0 # or zero(eltype(x)) 
    for i in indx
        s += x[i]
    end
    return s # important, return something observable
end

```

Then you force the function to do actual work.

Also

```julia
function func2(x)
    return x[:a] + x[:b]
end

```

---

<div class="post-metadata">

**Author:** ![z-wang](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/z-wang/32/210507_2.png) [@z-wang](https://discourse.julialang.org/u/z-wang)\
**Post date:** [July 8, 2024, 8:41pm UTC](https://discourse.julialang.org/t/looping-through-namedtuple-is-slow/116745/8 "2024-07-08T20:41:36Z")

</div>

Thanks! I redo the benchmark but still have the same results.

---

<div class="post-metadata">

**Author:** ![z-wang](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/z-wang/32/210507_2.png) [@z-wang](https://discourse.julialang.org/u/z-wang)\
**Post date:** [July 8, 2024, 8:47pm UTC](https://discourse.julialang.org/t/looping-through-namedtuple-is-slow/116745/9 "2024-07-08T20:47:54Z")

</div>

But even if the indexes are explicitly stated in `func1`, the results still don’t change.

```jl
function func1(x)
    s = 0.0 
    for i in (:a, :b)
        s += x[i]
    end
    return s
end

@btime func1($x) # 16.232 ns (0 allocations: 0 bytes)

```
