# Writing code that works allocation-free on both vectors and SVectors

**URL:** <https://discourse.julialang.org/t/writing-code-that-works-allocation-free-on-both-vectors-and-svectors/127048>\
**Category:** General Usage\
**Tags:** vector, staticarrays\
**Created:** [March 17, 2025, 10:27am UTC](https://discourse.julialang.org/t/writing-code-that-works-allocation-free-on-both-vectors-and-svectors/127048 "2025-03-17T10:27:58Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![user664303](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/user664303/32/37843_2.png) [@user664303](https://discourse.julialang.org/u/user664303)\
**Post date:** [March 17, 2025, 10:27am UTC](https://discourse.julialang.org/t/writing-code-that-works-allocation-free-on-both-vectors-and-svectors/127048/1 "2025-03-17T10:27:58Z")

</div>

I’d like to write generic code that operates on abstract vectors, and is non-allocating. A specific example is a function that subtracts the mean and normalizes a vector. This can be done in-place. However, the problem with writing code that specifically does operations in-place is that it will not work on immutable types, such as SVectors (static vectors from the StaticArrays package).

So I’m having difficulty understanding how I can write such methods that is both efficient and generic.

I have tried the following, hoping the compiler would be smart enough to in-place the operations:

```julia
using StaticArrays

mean0(vec) = vec .- (sum(vec) / length(vec))
norm1(vec) = vec .* (1 / sqrt(vec' * vec))
mean0norm1(vec) = norm1(mean0(vec))

function test()
    A = SVector{16, Float64}(randn(16))
    println(@allocated A = mean0norm1(A))

    B = MVector{16, Float64}(randn(16))
    println(@allocated B = mean0norm1(B))

    C = randn(16)
    println(@allocated C = mean0norm1(C))
end

test()

```

the output of which is:

```julia
0
288
384

```

This shows the compiler won’t get rid of the allocations.

Is there any way around this? It feels like I have to either give up generality, with two code paths, one for mutable and one for immutable objects, or give up on avoiding allocations and having performant code. Is that really the case?

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [March 17, 2025, 10:31am UTC](https://discourse.julialang.org/t/writing-code-that-works-allocation-free-on-both-vectors-and-svectors/127048/2 "2025-03-17T10:31:25Z")

</div>

I have used

> **[GitHub - JuliaFolds/BangBang.jl: Immutables as mutables, mutables as immutables.](https://github.com/JuliaFolds/BangBang.jl)**
>
> Immutables as mutables, mutables as immutables.

before. It handles some cases, but unfortunately not all. Sometimes I insert a simple branch in the code to handle the remaining cases.

See particularly [Home · BangBang.jl](https://juliafolds.github.io/BangBang.jl/dev/#BangBang.@!-Tuple%7BAny%7D)

---

<div class="post-metadata">

**Author:** ![user664303](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/user664303/32/37843_2.png) [@user664303](https://discourse.julialang.org/u/user664303)\
**Post date:** [March 17, 2025, 10:50am UTC](https://discourse.julialang.org/t/writing-code-that-works-allocation-free-on-both-vectors-and-svectors/127048/3 "2025-03-17T10:50:16Z")

</div>

Thanks! Good to know I’m not being stupid, and that this is an actual problem!

I just tried BangBang, as follows, but it doesn’t completely remove the allocations:

```julia
using StaticArrays, BangBang

mean0(vec) = add!!(vec, -(sum(vec) / length(vec)))
norm1(vec) = lmul!!(vec, 1 / sqrt(vec' * vec))
mean0norm1(vec) = norm1(mean0(vec))

function test()
    A = SVector{16, Float64}(randn(16))
    println(@allocated A = mean0norm1(A))

    B = MVector{16, Float64}(randn(16))
    println(@allocated B = mean0norm1(B))

    C = randn(16)
    println(@allocated C = mean0norm1(C))
end

test()

```

giving:

```julia
0
288
192

```

---

<div class="post-metadata">

**Author:** ![user664303](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/user664303/32/37843_2.png) [@user664303](https://discourse.julialang.org/u/user664303)\
**Post date:** [March 17, 2025, 10:57am UTC](https://discourse.julialang.org/t/writing-code-that-works-allocation-free-on-both-vectors-and-svectors/127048/4 "2025-03-17T10:57:58Z")

</div>

Using the `ismutabletype` function, I can do a relatively simple dual code path solution in my particular case:

```julia
using StaticArrays

mean0!(vec) = vec .-= sum(vec) / length(vec)
mean0(vec) = vec .- sum(vec) / length(vec)
norm1!(vec) = vec .*= 1 / sqrt(vec' * vec)
norm1(vec) = vec .* 1 / sqrt(vec' * vec)
mean0norm1!!(vec::T) where T = ismutabletype(T) ? norm1!(mean0!(vec)) : norm1(mean0(vec))

function test()
    A = SVector{16, Float64}(randn(16))
    println(@allocated A = mean0norm1!!(A))

    B = MVector{16, Float64}(randn(16))
    println(@allocated B = mean0norm1!!(B))

    C = randn(16)
    println(@allocated C = mean0norm1!!(C))
end

test()

```

which does achieve 0 allocations in all 3 cases. Still, it’s complexity I’d hope to avoid.

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [March 17, 2025, 11:15am UTC](https://discourse.julialang.org/t/writing-code-that-works-allocation-free-on-both-vectors-and-svectors/127048/5 "2025-03-17T11:15:30Z")

</div>

It turns out that I linked the wrong package, sorry for that. I didn’t use BangBang.jl, I used the macro `@bangbang` from MaybeInplace.jl

Here’s working code that is allocation free:

```julia
using StaticArrays, MaybeInplace

mean0(vec) = @bangbang vec .-= (sum(vec) / length(vec))
norm1(vec) = @bangbang vec .*= (1 / sqrt(vec' * vec))
mean0norm1(vec) = norm1(mean0(vec))

function test()
    A = SVector{16, Float64}(randn(16))
    println(@allocated A = mean0norm1(A))

    B = MVector{16, Float64}(randn(16))
    println(@allocated B = mean0norm1(B))

    C = randn(16)
    println(@allocated C = mean0norm1(C))
end

test()

```

---

<div class="post-metadata">

**Author:** ![rveltz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rveltz/32/2707_2.png) [@rveltz](https://discourse.julialang.org/u/rveltz)\
**Post date:** [March 17, 2025, 11:22am UTC](https://discourse.julialang.org/t/writing-code-that-works-allocation-free-on-both-vectors-and-svectors/127048/6 "2025-03-17T11:22:50Z")

</div>

SciML must be using this functionality or another one, no?

---

<div class="post-metadata">

**Author:** ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)\
**Post date:** [March 17, 2025, 11:59am UTC](https://discourse.julialang.org/t/writing-code-that-works-allocation-free-on-both-vectors-and-svectors/127048/7 "2025-03-17T11:59:58Z")

</div>

Yes  
 ![image](https://global.discourse-cdn.com/julialang/original/3X/d/2/d2b72e57d08c1b19bb43a50f4d7898958c04b335.png)
