# Vectors vs. Tuples and multiple dispatch

**URL:** <https://discourse.julialang.org/t/vectors-vs-tuples-and-multiple-dispatch/59281>\
**Category:** New to Julia\
**Tags:** performance\
**Created:** [April 14, 2021, 3:07pm UTC](https://discourse.julialang.org/t/vectors-vs-tuples-and-multiple-dispatch/59281 "2021-04-14T15:07:35Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![jbshannon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jbshannon/32/38838_2.png) [@jbshannon](https://discourse.julialang.org/u/jbshannon)\
**Post date:** [April 14, 2021, 3:07pm UTC](https://discourse.julialang.org/t/vectors-vs-tuples-and-multiple-dispatch/59281/1 "2021-04-14T15:07:35Z")

</div>

This is my first major project in Julia and I have a core function that will be evaluated many times that I want to optimize. The function accepts a vector and returns a vector, but I only anticipate using it with vectors of 2-6 units. There’s an inner function which I define to accept only `Float64` scalars:

```julia
F(x::Float64, a::Float64) = 1.2*exp(-a*x)

```

The simplest, most MATLAB-like definition of the function can be done in one line:

```julia
f(x,a) = vcat(1, F.(cumsum(x),a))

```

However, I’ve found significant performance improvements by defining additional methods for different-sized inputs as tuples rather than vectors, e.g.:

```julia
function f(x::Float64, a::Float64)
    y₁ = 1
    y₂ = F(x,a)
    return (y₁, y₂)
end

function f(x::NTuple{2,Float64}, a::Float64)
    y₁ = 1
    y₂ = F(x[1],a)
    y₃ = F(x[1]+x[2],a)
    return (y₁, y₂, y₃)
end

function f(x::NTuple{3,Float64}, a::Float64)
    y₁ = 1
    y₂ = F(x[1],a)
    y₃ = F(x[1]+x[2],a)
    y₄ = F(x[1]+x[2]+x[3],a)
    return (y₁, y₂, y₃, y₄)
end

```

The speed/memory differences are significant:

```julia
julia> using BenchmarkTools

julia> @btime f([0.1,0.2],0.5);
  1.313 μs (19 allocations: 1.00 KiB)

julia> @btime f((0.1,0.2),0.5);
  13.197 ns (0 allocations: 0 bytes)

```

Since I’m still learning the rope of Julia, I’d appreciate any feedback on two points:

1. Is this the most idiomatic way to do something like this in Julia? Are there some nuances of the compiler I’m missing that would make all of this unnecessary?
2. If this is the correct way to do things, would it be an acceptable use of metaprogramming to define these methods rather than having everything typed out in the module? If so, what would be a good way to accomplish that?

Looking forward to learning how to use and understand a beautiful language!

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [April 14, 2021, 3:12pm UTC](https://discourse.julialang.org/t/vectors-vs-tuples-and-multiple-dispatch/59281/2 "2021-04-14T15:12:56Z")

</div>

> [@jbshannon](#):
>
> Is this the most idiomatic way to do something like this in Julia? Are there some nuances of the compiler I’m missing that would make all of this unnecessary?

Use [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl) instead of tuples. Then you can use your original `f` definition and it will be fast (by avoiding heap allocations and unrolling loops):

```julia
julia> using BenchmarkTools, StaticArrays

julia> F(x, a) = 1.2*exp(-a*x)
F (generic function with 1 method)

julia> f(x,a) = vcat(1, F.(cumsum(x),a))
f (generic function with 1 method)

julia> x = @SVector[0.1, 0.2]
2-element SVector{2, Float64} with indices SOneTo(2):
 0.1
 0.2

julia> @btime f($x, 0.5)
  0.051 ns (0 allocations: 0 bytes)
3-element SVector{3, Float64} with indices SOneTo(3):
 1.0
 1.1414753094008567
 1.0328495717100694

```

In general, StaticArrays are a good idea when you have small (≲ 10 element) arrays whose size is more-or-less fixed (typically it should be inferred at compile-time from the function inputs).

---

<div class="post-metadata">

**Author:** ![stillyslalom](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stillyslalom/32/45687_2.png) [@stillyslalom](https://discourse.julialang.org/u/stillyslalom)\
**Post date:** [April 14, 2021, 3:35pm UTC](https://discourse.julialang.org/t/vectors-vs-tuples-and-multiple-dispatch/59281/3 "2021-04-14T15:35:19Z")

</div>

In this case, it’s necessary to use the `Ref` trick to prevent the compiler from optimizing away the static argument:

```julia
julia> @btime f($(Ref(x))[], 0.5)
  13.313 ns (0 allocations: 0 bytes)
3-element SVector{3, Float64} with indices SOneTo(3):
 1.0
 1.1414753094008567
 1.0328495717100694

```

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [April 14, 2021, 3:52pm UTC](https://discourse.julialang.org/t/vectors-vs-tuples-and-multiple-dispatch/59281/4 "2021-04-14T15:52:08Z")

</div>

And, if you do want to work with tuples, you can write things recursively instead of writing out cases. These should get unrolled by the compiler to pretty much what you had:

```julia
julia> g(xs::Tuple, a) = (g(Base.front(xs), a)..., F(sum(xs), a))
       g(::Tuple{}, a) = (1,);

julia> g((1.2, 3.4, 5.6), 0.78) == f((1.2, 3.4, 5.6), 0.78)
true

julia> @btime g($(Ref(Tuple(x)))[], 0.5) # same as SVector

```
