# Avoid calling push! in Base.find

**URL:** https://discourse.julialang.org/t/avoid-calling-push-in-base-find/1336
**Category:** Internals & Design
**Created:** [January 6, 2017, 10:06pm UTC](https://discourse.julialang.org/t/avoid-calling-push-in-base-find/1336 "2017-01-06T22:06:54Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![Diego\_Javier\_Zea](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/diego_javier_zea/32/1858_2.png) [@Diego\_Javier\_Zea](https://discourse.julialang.org/u/Diego_Javier_Zea)
#### Post date: [January 6, 2017, 10:06pm UTC](https://discourse.julialang.org/t/avoid-calling-push-in-base-find/1336/1 "2017-01-06T22:06:54Z")

</div>

I don’t understand why the actual `find` function fills a 0 length array using `push!` to finally create other array and copy data to it. However, I write this version which avoid calling `push!` and it’s faster than the actual implementation. Should I make a PR with this implementation? Best,

```julia
julia> using BenchmarkTools

julia> function find2(testf::Function, A)
           len = 0 
           inds = Base._index_remapper(A)
           out = Array(Int, length(inds))
           for (i,a) = enumerate(A)
               if testf(a)
                   len += 1
                   out[len] = inds[i]
               end
           end
           resize!(out, len)
           return I
       end
find2 (generic function with 1 method)

julia> @benchmark find(x -> x > 0.5, rand(10000))
BenchmarkTools.Trial: 
  memory estimate: 244.61 kb
  allocs estimate: 17
  --------------
  minimum time: 130.360 μs (0.00% GC)
  median time: 143.276 μs (0.00% GC)
  mean time: 152.181 μs (3.08% GC)
  maximum time: 694.662 μs (76.48% GC)
  --------------
  samples: 10000
  evals/sample: 1
  time tolerance: 5.00%
  memory tolerance: 1.00%

julia> @benchmark find2(x -> x > 0.5, rand(10000))
BenchmarkTools.Trial: 
  memory estimate: 156.41 kb
  allocs estimate: 4
  --------------
  minimum time: 79.431 μs (0.00% GC)
  median time: 83.703 μs (0.00% GC)
  mean time: 89.274 μs (2.98% GC)
  maximum time: 558.110 μs (72.61% GC)
  --------------
  samples: 10000
  evals/sample: 1
  time tolerance: 5.00%
  memory tolerance: 1.00%

```

---

<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: [January 6, 2017, 11:48pm UTC](https://discourse.julialang.org/t/avoid-calling-push-in-base-find/1336/2 "2017-01-06T23:48:29Z")

</div>

This implementation pre-allocates an index array of the same size as the whole input array. The `Base.find` function only allocates storage proportional to the size of its _output_.

Of course, in cases like your test case where the output array is half the size of the input array, this doesn’t save any memory, but in cases where the `find` output is much smaller it makes a difference.

Note that when you benchmark, you should interpolate the arguments of the function you are benchmarking, for example:

```julia
julia> A = rand(10^6);

julia> @benchmark find(x -> x < 0.01, $A)
BenchmarkTools.Trial: 
  memory estimate: 334.03 kb
  allocs estimate: 16
  --------------
  minimum time: 931.427 μs (0.00% GC)
  median time: 975.280 μs (0.00% GC)
  mean time: 1.004 ms (1.67% GC)
  maximum time: 2.772 ms (50.97% GC)
  --------------
  samples: 4975
  evals/sample: 1
  time tolerance: 5.00%
  memory tolerance: 1.00%

julia> @benchmark find2(x -> x < 0.01, $A)
BenchmarkTools.Trial: 
  memory estimate: 7.63 mb
  allocs estimate: 2
  --------------
  minimum time: 870.728 μs (0.00% GC)
  median time: 926.463 μs (0.00% GC)
  mean time: 1.021 ms (11.63% GC)
  maximum time: 2.344 ms (27.54% GC)
  --------------
  samples: 4890
  evals/sample: 1
  time tolerance: 5.00%
  memory tolerance: 1.00%

```

In this case, there is only a 7% performance penalty to the `Base.find` strategy of incremental allocation of the output, and it saves a factor of 20 in memory allocation.

---

<div class="post-metadata">

### Author: ![Diego\_Javier\_Zea](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/diego_javier_zea/32/1858_2.png) [@Diego\_Javier\_Zea](https://discourse.julialang.org/u/Diego_Javier_Zea)
#### Post date: [January 7, 2017, 12:20am UTC](https://discourse.julialang.org/t/avoid-calling-push-in-base-find/1336/3 "2017-01-07T00:20:28Z")

</div>

Yes, It’s true! `find2` is only fine when there are a lot of true elements or for short arrays where allocations aren’t a problem. Thanks!
