# Is it possible to get allocations from 1 to 0 in this code?

**URL:** <https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056>\
**Category:** General Usage\
**Created:** [October 19, 2021, 6:00pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056 "2021-10-19T18:00:21Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![curious](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/curious/32/7629_2.png) [@curious](https://discourse.julialang.org/u/curious)\
**Post date:** [October 19, 2021, 6:00pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/1 "2021-10-19T18:00:21Z")

</div>

Hi,

I have been trying to improve the performance of my code. The function below had 7 allocations before and some warning when using `@code_warntype`. I made several changes that reduced allocations from 7 to 1 and removed the red ink from `@code_warntype`. Is it possible to make the allocations to zero in this function?

Using TimerOutputs.jl I could not find which line is allocating. Individually, it seems all lines have zero allocations.

Thank you for reading…

```julia
function construct_tuple(arr1, ::Val{n1}, ::Val{m1}, ::Val{m2}, arr2) where {n1, m1, m2}
	len_arr1 = length(arr1)
	tup1 = (len_arr1 > n1) ? ntuple(i -> (@inbounds arr1[end-n1+i]), n1) : 
												   ntuple(i -> i <= len_arr1 ? (@inbounds arr1[i]) : :N, n1)

	len_arr2 = length(arr2)
	tup2 = (len_arr2 > m1) ? ntuple(i -> (@inbounds arr2[end-m1+i]), m1) : 
							   ntuple(i -> i <= len_arr2 ? (@inbounds arr2[i]) : :N, m1)

	tup3 = len_arr2 >= m2 ? ntuple(i -> (@inbounds arr2[i]), m2) :
									ntuple(i -> i <= len_arr2 ? (@inbounds arr2[i]) : :N, m2)	

	(tup1, tup2, tup3)
end

```

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [October 19, 2021, 6:12pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/2 "2021-10-19T18:12:42Z")

</div>

Can you show us what input you’re benchmarking on? When I do this, I get way more allocations than you:

```julia
julia> function construct_tuple(arr1, ::Val{n1}, ::Val{m1}, ::Val{m2}, arr2) where {n1, m1, m2}
               len_arr1 = length(arr1)
               tup1 = (len_arr1 > n1) ? ntuple(i -> (@inbounds arr1[end-n1+i]), n1) :
                                                                                                          ntuple(i -> i <= len_arr1 ? (@inbounds arr1[i]) : :N, n1)

               len_arr2 = length(arr2)
               tup2 = (len_arr2 > m1) ? ntuple(i -> (@inbounds arr2[end-m1+i]), m1) :
                                                                  ntuple(i -> i <= len_arr2 ? (@inbounds arr2[i]) : :N, m1)

               tup3 = len_arr2 >= m2 ? ntuple(i -> (@inbounds arr2[i]), m2) :
                                                                               ntuple(i -> i <= len_arr2 ? (@inbounds arr2[i]) : :N, m2)

               (tup1, tup2, tup3)
       end
construct_tuple (generic function with 1 method)

julia> let arr1 = rand(10), arr2 = rand(8)
           @btime construct_tuple($arr1, Val(5), Val(9), Val(11), $arr2)
       end
  619.801 ns (22 allocations: 976 bytes)
((0.4254217904105211, 0.13241443472571168, 0.869700471092793, 0.7124209560321589, 0.5954160420851926), (0.737801411380624, 0.556271836516959, 0.021540151975602106, 0.7126850002734751, 0.31805793324648257, 0.4001497672303781, 0.6672644424251242, 0.707907493489921, :N), (0.737801411380624, 0.556271836516959, 0.021540151975602106, 0.7126850002734751, 0.31805793324648257, 0.4001497672303781, 0.6672644424251242, 0.707907493489921, :N, :N, :N))

```

---

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [October 19, 2021, 6:22pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/3 "2021-10-19T18:22:22Z")

</div>

Hey Mason,

when I try

```julia
function test1()
	sv1 = SVector{3}(rand(3))
	sv2 = SVector{3}(rand(3))
	ans = construct_tuple(sv1, Val(1), Val(2), Val(3), sv2)
end

@btime test1()

```

I get 2 allocs

```julia
  88.030 ns (2 allocations: 224 bytes)
((0.9505273873529649,), (0.7657941423837595, 0.2197983373943313), (0.38703121887758996, 0.7657941423837595, 0.2197983373943313))

```

and they seem related to rand(). Is it possible to randomize an SVector?

---

<div class="post-metadata">

**Author:** ![curious](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/curious/32/7629_2.png) [@curious](https://discourse.julialang.org/u/curious)\
**Post date:** [October 19, 2021, 6:24pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/4 "2021-10-19T18:24:19Z")

</div>

Sorry, I forgot to include arr1 and arr2.

```julia
arr1 = [rand((:A, :B)) for i = 1:100]
arr2 = [rand((:A, :B)) for i = 1:100]

construct_tuple(arr1, Val(2), Val(2), Val(2), arr2)

```

From the repl:

```julia
julia> arr1 = [rand((:A, :B)) for i = 1:100];

julia> arr2 = [rand((:A, :B)) for i = 1:100];

julia> @btime construct_tuple(arr1, Val(2), Val(2), Val(2), arr2)
  26.214 ns (1 allocation: 64 bytes)
((:A, :B), (:B, :A), (:A, :B))

```

---

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [October 19, 2021, 6:29pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/5 "2021-10-19T18:29:31Z")

</div>

If I do this

```julia
function test1()
	arr1 = [rand((:A, :B)) for i = 1:100]
	arr2 = [rand((:A, :B)) for i = 1:100]
	construct_tuple(arr1, Val(2), Val(2), Val(2), arr2)
end

Profile.clear()
test1()
@profile (for i in 1:1000000; x = test1(); end)
Juno.profiler()

```

I see two allocations in the call stacks of

```julia
	arr1 = [rand((:A, :B)) for i = 1:100]
	arr2 = [rand((:A, :B)) for i = 1:100]

```

and `@btime` reports

```julia
  721.094 ns (2 allocations: 1.75 KiB)

```

which seems consistent to me.

Edit: without randomization it is difficult to even get a profile. And if you start with the `Ref[]` trick, you get some allocations for them. Only way out would probably be to `--track-allocation`, but I’m too lazy to start that process.

---

<div class="post-metadata">

**Author:** ![curious](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/curious/32/7629_2.png) [@curious](https://discourse.julialang.org/u/curious)\
**Post date:** [October 19, 2021, 6:34pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/6 "2021-10-19T18:34:18Z")

</div>

Are those two allocations occuring for `arr1` and `arr2`? I am using @btime directly on `construct_tuple`, so it should not count the allocations for arr1 and arr2 (is that right?).

Are you saying that result of running `@btime` directly on `construct_tuple` is not reliable? I will search for `--track-allocation`. Thank you.

---

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [October 19, 2021, 6:36pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/7 "2021-10-19T18:36:18Z")

</div>

Constant propagation could make your test case compile time evaluated. But Mason knows the details of this stuff way better than me. Reference: [Home · BenchmarkTools.jl](https://juliaci.github.io/BenchmarkTools.jl/dev/) (search for ‘cheat’)

---

<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:** [October 19, 2021, 6:38pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/8 "2021-10-19T18:38:03Z")

</div>

> [@goerch](#):
>
> `sv1 = SVector{3}(rand(3))`

Use

```julia
rand(SVector{3,Float64})

```

---

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [October 19, 2021, 6:40pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/9 "2021-10-19T18:40:20Z")

</div>

Beautiful:

```julia
function test1()
	arr1 = rand(SVector{100,Float64})
	arr2 = rand(SVector{100,Float64})
	ans = construct_tuple(arr1, Val(2), Val(2), Val(2), arr2)
	ans
end

ans = nothing
@btime ($ans = test1())

```

results in

```julia
  292.394 ns (0 allocations: 0 bytes)

```

Looks OK to me.

---

<div class="post-metadata">

**Author:** ![curious](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/curious/32/7629_2.png) [@curious](https://discourse.julialang.org/u/curious)\
**Post date:** [October 19, 2021, 6:47pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/10 "2021-10-19T18:47:50Z")

</div>

> [@curious](#):
>
> ```julia
> julia> arr1 = [rand((:A, :B)) for i = 1:100];
> 
> julia> arr2 = [rand((:A, :B)) for i = 1:100];
> 
> julia> @btime construct_tuple(arr1, Val(2), Val(2), Val(2), arr2)
> 26.214 ns (1 allocation: 64 bytes)
> ((:A, :B), (:B, :A), (:A, :B))
> 
> ```

Can someone please explain why this code I tried is not giving zero allocations? Am I testing this wrong in some way?

---

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [October 19, 2021, 6:50pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/11 "2021-10-19T18:50:33Z")

</div>

Now we get into `@macroexpand` territory. Nice. But what I’ve learned elsewhere: you better isolate your benchmarks from the environment and bring the results back into it: this way a compiler has more difficulties to eliminate your code (but it will try nevertheless).

---

<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:** [October 19, 2021, 8:29pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/12 "2021-10-19T20:29:54Z")

</div>

You have to interpolate the input variables (check the `$`):

```julia
julia> @btime construct_tuple($arr1, Val(2), Val(2), Val(2), $arr2)
  3.273 ns (0 allocations: 0 bytes)
((:B, :A), (:A, :A), (:B, :B))

```

---

<div class="post-metadata">

**Author:** ![curious](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/curious/32/7629_2.png) [@curious](https://discourse.julialang.org/u/curious)\
**Post date:** [October 19, 2021, 10:42pm UTC](https://discourse.julialang.org/t/is-it-possible-to-get-allocations-from-1-to-0-in-this-code/70056/13 "2021-10-19T22:42:37Z")

</div>

Thank you, everyone and @lmiq. I will check the interpolation. I am learning lot of new things (I thought interpolation was only for strings :))
