# Reusing buffer arrays by providing them as arguments to functions

**URL:** https://discourse.julialang.org/t/reusing-buffer-arrays-by-providing-them-as-arguments-to-functions/21942
**Category:** Performance
**Created:** [March 16, 2019, 5:22pm UTC](https://discourse.julialang.org/t/reusing-buffer-arrays-by-providing-them-as-arguments-to-functions/21942 "2019-03-16T17:22:00Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![jonas-schulze](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonas-schulze/32/9228_2.png) [@jonas-schulze](https://discourse.julialang.org/u/jonas-schulze)
#### Post date: [March 16, 2019, 5:22pm UTC](https://discourse.julialang.org/t/reusing-buffer-arrays-by-providing-them-as-arguments-to-functions/21942/1 "2019-03-16T17:22:00Z")

</div>

I have a function that needs a `Vector{Int}` for buffering, which I would like to reuse when I call that function over and over again in a loop. My idea was to create that vector outside and clearing it before every use. I had hoped that `empty!` doesn’t change the vector’s capacity (similar to C++) and would have constant complexity (omit calling `Int` destructors) without allocations (simply overwriting some length attribute).

However, `empty!` isn’t “free”:

```julia
julia> tmp = Int[];

julia> @time empty!(tmp);
  0.000036 seconds (4 allocations: 160 bytes)

```

How do I prevent allocation when clearing buffers?  
What is the proper way to reuse those buffers?

* * *

A more elaborate example: consider computing the prime factorization of an integer

```julia
function factorize(n,
                   primes = Primes(2_000_000),
                   factors = Int[],
                   exponents = Int[])
  n >= 4 || return [n], [1]

  limit = floor(Int, sqrt(n))
  for p in primes
    p <= limit || break
    n % p == 0 || continue

    push!(factors, p)
    push!(exponents, 0)
    while n % p == 0
      exponents[end] += 1
      n ÷= p
      n % p == 0 || break
      exponents[end] += 1
      n ÷= p
      limit ÷= p
    end
    n != 1 || break
  end

  if n != 1
    push!(factors, n)
    push!(exponents, 1)
  end

  return factors, exponents
end

```

which is called very often when looking for the first number `x = bar(n)` having at least 500 divisors:

```julia
function foo()
  primes = Primes(2_000_000)

  # buffers
  f = Array{Int}(undef, length(primes)÷2)
  e = similar(f)

  n = 1
  x = bar(n)
  _, e = factorize(x, f, e)
  while numdivisors(e) < 500
    empty!(f)
    empty!(e)
    n += 1
    x = bar(n)
    _, e = factorize(t, primes, f, e)
  end
  x
end

```

An alternative would be to rewrite `factorize` to be used like `for (f, e) in primefactors(n, primes)` but that would be a rather big undertaking. Another way would be to replace all the `push!`es and do all the index operations manually (plus returning the length in some form as `resize!` isn’t free either).

---

<div class="post-metadata">

### Author: ![tkluck](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkluck/32/15769_2.png) [@tkluck](https://discourse.julialang.org/u/tkluck)
#### Post date: [March 16, 2019, 5:29pm UTC](https://discourse.julialang.org/t/reusing-buffer-arrays-by-providing-them-as-arguments-to-functions/21942/2 "2019-03-16T17:29:14Z")

</div>

> However, `empty!` isn’t “free”

It actually is; you’re seeing some overhead from using `tmp` as a global variable. Here’s a better way to benchmark:

```julia
julia> tmp = Int[];

julia> using BenchmarkTools

julia> @btime empty!($tmp)
  3.054 ns (0 allocations: 0 bytes)
0-element Array{Int64,1}

```

---

<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: [March 16, 2019, 5:52pm UTC](https://discourse.julialang.org/t/reusing-buffer-arrays-by-providing-them-as-arguments-to-functions/21942/3 "2019-03-16T17:52:30Z")

</div>

Also, running `@show` itself in global scope will allocate.

---

<div class="post-metadata">

### Author: ![bennedich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bennedich/32/4894_2.png) [@bennedich](https://discourse.julialang.org/u/bennedich)
#### Post date: [March 16, 2019, 8:01pm UTC](https://discourse.julialang.org/t/reusing-buffer-arrays-by-providing-them-as-arguments-to-functions/21942/4 "2019-03-16T20:01:47Z")

</div>

> [@jonas-schulze](#):
>
> `empty!` doesn’t change the vector’s capacity

This might be how it’s currently implemented, but is that behavior documented? If not, I’d be quite wary of relying on that.

I would instead do something similar to your final suggestion (indexed operations), but instead of doing it all manually, create a small wrapper around an array and use that as a reusable buffer. Indexed operations are also slightly faster than `push!`, since `push!` does a library ccall for each invocation. Usually that’s not something you need to worry about, but since you’re concerned about allocations and buffering in the first place, it sounds like you’re after maximum performance.

You can read about the performance issue associated with `push!` [here](https://github.com/JuliaLang/julia/issues/24909), and on that page there’s also a sample implementation of a wrapped array (`PushVector`) that might serve as inspiration.

Before refactoring too much though – please make sure that you’ve benchmarked your code and established that these allocations are actually a bottleneck and the right thing to optimize. I’m a bit concerned that you fret over 160 bytes allocated which doesn’t even seem to be in a hot code path.

---

<div class="post-metadata">

### Author: ![jonas-schulze](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonas-schulze/32/9228_2.png) [@jonas-schulze](https://discourse.julialang.org/u/jonas-schulze)
#### Post date: [March 16, 2019, 9:08pm UTC](https://discourse.julialang.org/t/reusing-buffer-arrays-by-providing-them-as-arguments-to-functions/21942/5 "2019-03-16T21:08:53Z")

</div>

Thanks for the hints! I was wrong about where the allocations came from. After some proper [profiling](https://docs.julialang.org/en/v1.1/manual/profile/#Memory-allocation-analysis-1) I discovered that `return factors, exponents` was the issue, so I refactored my code:

```julia
function factorize(n, primes = Primes(2_000_000))
  f = Int[]
  e = Int[]
  factorize!(f, e, n, primes)
  return f, e
end

function factorize!(factors, exponents, n, primes)
  # deleted: n >= 4 || return [n], [1]
  if n < 4
    push!(factors, n)
    push!(exponents, 1)
    return
  end

  # ...

  # deleted: return factors, exponents
  nothing
end

```

---

<div class="post-metadata">

### Author: ![jonas-schulze](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonas-schulze/32/9228_2.png) [@jonas-schulze](https://discourse.julialang.org/u/jonas-schulze)
#### Post date: [March 17, 2019, 9:43am UTC](https://discourse.julialang.org/t/reusing-buffer-arrays-by-providing-them-as-arguments-to-functions/21942/6 "2019-03-17T09:43:06Z")

</div>

> [@bennedich](#):
>
> This might be how it’s currently implemented, but is that behavior documented? If not, I’d be quite wary of relying on that.

No, it’s not documented I think. Calling `empty!` eventually leads to `jl_array_del_at_end(a, n - dec, dec, n);` (in [`src/array.c`](https://github.com/JuliaLang/julia/blob/68db87147f9806db605c4df9f32b0869554cf4b7/src/array.c#L1031-L1054)). My guess was that it behaved as I assumed originally, but I didn’t research how `STORE_ARRAY_LEN` is used/resolved.

> [@bennedich](#):
>
> it sounds like you’re after maximum performance.

Yes I am, I really enjoy squeezing all the performance I can get out of my code (without “hacking” too much 😉). I’m learning the language while solving some Project Euler riddles.

Your link mentions some effort porting arrays to Julia. As I understand it, the whole runtime is written in C. Are there any plans to port the runtime to Julia?

> [@bennedich](#):
>
> You can read about the performance issue associated with `push!` [here](https://github.com/JuliaLang/julia/issues/24909), and on that page there’s also a sample implementation of a wrapped array ( `PushVector` ) that might serve as inspiration.

Nice! All that is left is

```julia
@inline function Base.setindex!(v::PushVector, val, i)
  @boundscheck checkbounds(v, i)
  @inbounds v.v[i] = val
end

function Base.empty!(v::PushVector)
    v.l = 0
end

function Base.similar(v::PushVector)
  PushVector(similar(v.v), 0)
end

```

which leads to a ~5% performance increase in my example (measured after the ~50% increase due to reusing the buffers). In another riddle I earned ~18% by using `PushVector`s.
