# Reducing allocations when subtracting vectors and multiplying by scalars

**URL:** <https://discourse.julialang.org/t/reducing-allocations-when-subtracting-vectors-and-multiplying-by-scalars/71203>\
**Category:** Performance\
**Tags:** memory-allocation, optimization\
**Created:** [November 9, 2021, 12:03pm UTC](https://discourse.julialang.org/t/reducing-allocations-when-subtracting-vectors-and-multiplying-by-scalars/71203 "2021-11-09T12:03:09Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![jewh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jewh/32/28586_2.png) [@jewh](https://discourse.julialang.org/u/jewh)\
**Post date:** [November 9, 2021, 12:03pm UTC](https://discourse.julialang.org/t/reducing-allocations-when-subtracting-vectors-and-multiplying-by-scalars/71203/1 "2021-11-09T12:03:09Z")

</div>

Is it possible to re-write the following function `foo(x, y)`, such that it only uses one allocation?

```julia
using BenchmarkTools 

function foo(x::Vector{Float64}, y::Vector{Float64})
    (x .- y) .* 3.0
end

```

This currently yields 3 allocations:

```julia
julia> @btime foo([1.0, 1.0, 1.0], [2.0, 2.0, 2.0])
57.477 ns (3 allocations: 336 bytes)
3-element Vector{Float64}:
 -3.0
 -3.0
 -3.0

```

Is it possible to get this down to one allocation with 112 bytes, as one would get with allocating an array of length 3? For instance:

```julia
function give_me_array_please()
    [0.0, 0.0, 0.0]
end

```

yields:

```julia
julia> @btime give_me_array_please()
16.934 ns (1 allocation: 112 bytes)
3-element Vector{Float64}:
 0.0
 0.0
 0.0

```

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [November 9, 2021, 12:25pm UTC](https://discourse.julialang.org/t/reducing-allocations-when-subtracting-vectors-and-multiplying-by-scalars/71203/2 "2021-11-09T12:25:36Z")

</div>

That is a benchmarking artifact, you are benchmarking the creation of the arrays. If you interpolate them, it has only one allocation:

```julia
julia> @btime foo($([1.0, 1.0, 1.0]), $([2.0, 2.0, 2.0]))
  32.036 ns (1 allocation: 112 bytes)
3-element Vector{Float64}:
 -3.0
 -3.0
 -3.0

```

If you use static arrays, you can go to zero allocations:

```julia
julia> using StaticArrays

julia> function foo(x::AbstractVector{Float64}, y::AbstractVector{Float64})
           (x .- y) .* 3.0
       end
foo (generic function with 2 methods)

julia> @btime foo($(SVector{3,Float64}(1.0, 1.0, 1.0)), $(SVector{3,Float64}(2.0, 2.0, 2.0)))
  0.015 ns (0 allocations: 0 bytes)
3-element SVector{3, Float64} with indices SOneTo(3):
 -3.0
 -3.0
 -3.0

```

(which can be very useful if your problem deals with small arrays, as in the example).

---

<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:** [November 9, 2021, 12:28pm UTC](https://discourse.julialang.org/t/reducing-allocations-when-subtracting-vectors-and-multiplying-by-scalars/71203/4 "2021-11-09T12:28:26Z")

</div>

Yeah, 3 allocations is exactly right, because you are creating three arrays. It’s not just an artefact.

> [@lmiq](#):
>
> `0.015 ns (0 allocations: 0 bytes)`

This is a benchmarking artefact, though.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [November 9, 2021, 12:30pm UTC](https://discourse.julialang.org/t/reducing-allocations-when-subtracting-vectors-and-multiplying-by-scalars/71203/5 "2021-11-09T12:30:35Z")

</div>

> [@DNF](#):
>
> This is a benchmarking artefact, though.

the time is, the 0 allocations allocations aren’t, just to be clear

> [@DNF](#):
>
> It’s not just an artefact.

ok, it is an artifact in the sense that the OP was not benchmarking only what was going inside the function, I thought that was clear.

---

<div class="post-metadata">

**Author:** ![jewh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jewh/32/28586_2.png) [@jewh](https://discourse.julialang.org/u/jewh)\
**Post date:** [November 9, 2021, 12:41pm UTC](https://discourse.julialang.org/t/reducing-allocations-when-subtracting-vectors-and-multiplying-by-scalars/71203/6 "2021-11-09T12:41:59Z")

</div>

How could I re-write this such that I’m not creating three arrays?

---

<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:** [November 9, 2021, 12:45pm UTC](https://discourse.julialang.org/t/reducing-allocations-when-subtracting-vectors-and-multiplying-by-scalars/71203/7 "2021-11-09T12:45:04Z")

</div>

OK, I tend to think of ‘benchmarking artefacts’ as those cases where the benchmarking itself is ‘off’, due to for example accessing global variables, i.e. if `@btime` reported 7 allocations instead of 3. The ordinary `@time` macro is plagued by this.

> [@jewh](#):
>
> How could I re-write this such that I’m not creating three arrays?

You can follow @lmiq’s suggestion:

> [@lmiq](#):
>
> `@btime foo($([1.0, 1.0, 1.0]), $([2.0, 2.0, 2.0]))`

or you can create the input arrays separately:

```julia
x = [1.0, 1.0, 1.0];
y = [2.0, 2.0, 2.0];
@btime foo($x, $y)

```

In both cases only the function call on the already existing arrays is measured.

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [November 9, 2021, 12:57pm UTC](https://discourse.julialang.org/t/reducing-allocations-when-subtracting-vectors-and-multiplying-by-scalars/71203/8 "2021-11-09T12:57:18Z")

</div>

> [@DNF](#):
>
> The ordinary `@time` macro is plagued by this.

```julia
julia> @time foo([1.0, 1.0, 1.0], [2.0, 2.0, 2.0])
  0.000001 seconds (3 allocations: 240 bytes)

```

though

---

<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:** [November 9, 2021, 1:01pm UTC](https://discourse.julialang.org/t/reducing-allocations-when-subtracting-vectors-and-multiplying-by-scalars/71203/9 "2021-11-09T13:01:35Z")

</div>

Yes, benchmarking seems to have become a lot better recently. It also seems less necessary to use interpolation with `@btime`.

---

<div class="post-metadata">

**Author:** ![lmiq](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lmiq/32/18314_2.png) [@lmiq](https://discourse.julialang.org/u/lmiq)\
**Post date:** [November 9, 2021, 1:08pm UTC](https://discourse.julialang.org/t/reducing-allocations-when-subtracting-vectors-and-multiplying-by-scalars/71203/10 "2021-11-09T13:08:53Z")

</div>

Being a bit more explicit. When you write something like `[1.0, 1.0, 1.0]` you are creating a new array. Thus, when you did

```julia
@btime foo([1.0, 1.0, 1.0], [2.0, 2.0, 2.0])

```

you benchmarked not only what was going inside `func`, but the creation of the two arrays that you are providing as input. To avoid that you have to create the input arrays outside the `@btime` call:

```julia
julia> foo(x, y) = (x .- y) .* 3.0
foo (generic function with 1 method)

julia> x = [1.0,1.0,1.0]; y = [2.0,2.0,2.0];

julia> @time foo(x,y)
  0.000005 seconds (1 allocation: 112 bytes)
3-element Vector{Float64}:
 -3.0
 -3.0
 -3.0

julia> @btime foo(x,y)
  36.320 ns (1 allocation: 112 bytes)
3-element Vector{Float64}:
 -3.0
 -3.0
 -3.0

```

Additionally, the proper way to use the macros from `BenchmarkTools` is by interpolating the variables (adding the `$`), thus although the one `@btime foo(x,y)` above reported the correct number of allocations, it is not the correct and recommended way to use the macro. That would be what DNF suggested:

```julia
julia> @btime foo($x,$y)
  31.983 ns (1 allocation: 112 bytes)
3-element Vector{Float64}:
 -3.0
 -3.0
 -3.0

```

Why you have interpolate the variables is associated to the internals of `BenchmarkTools` and, to be sincere, I never completely understood it, something related to the parsing of the values and not references to variables by the macro.

---

<div class="post-metadata">

**Author:** ![jewh](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jewh/32/28586_2.png) [@jewh](https://discourse.julialang.org/u/jewh)\
**Post date:** [November 9, 2021, 1:32pm UTC](https://discourse.julialang.org/t/reducing-allocations-when-subtracting-vectors-and-multiplying-by-scalars/71203/11 "2021-11-09T13:32:34Z")

</div>

Ahhh of course, makes sense. Thanks!
