# Performance of resize! vs pre-allocating with zeros(...)

**URL:** <https://discourse.julialang.org/t/performance-of-resize-vs-pre-allocating-with-zeros/80258>\
**Category:** Performance\
**Created:** [April 29, 2022, 3:00pm UTC](https://discourse.julialang.org/t/performance-of-resize-vs-pre-allocating-with-zeros/80258 "2022-04-29T15:00:53Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![fcdimitr](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fcdimitr/32/26613_2.png) [@fcdimitr](https://discourse.julialang.org/u/fcdimitr)\
**Post date:** [April 29, 2022, 3:00pm UTC](https://discourse.julialang.org/t/performance-of-resize-vs-pre-allocating-with-zeros/80258/1 "2022-04-29T15:00:53Z")

</div>

I observe a factor of 5x time difference in writing over a vector with two different preallocation strategies.

1. Using `x = zeros(Int64, n)`
2. Using `x = Int64[]; resize!(x,n)`

The benchmark code is bellow

```julia
using BenchmarkTools

function test_access!(x,n)
  @inbounds for i = 1 : n
    x[i] = i
  end
end

function init(n)
  x = Int64[]
  resize!( x , n )
end

n = 10_000_000;

@btime test_access!(x,$n) setup=(x = zeros(Int64,$n))
  5.025 ms (0 allocations: 0 bytes)

@btime test_access!(x2,$n) setup=(x2 = init($n))
  26.381 ms (0 allocations: 0 bytes)

```

I was expecting these codes to have similar performance. Any ideas on why there is such a big difference?

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [April 29, 2022, 3:05pm UTC](https://discourse.julialang.org/t/performance-of-resize-vs-pre-allocating-with-zeros/80258/2 "2022-04-29T15:05:41Z")

</div>

I can confirm this. It is very odd.

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [April 29, 2022, 4:46pm UTC](https://discourse.julialang.org/t/performance-of-resize-vs-pre-allocating-with-zeros/80258/3 "2022-04-29T16:46:43Z")

</div>

`zeros(Int64, n)` does initialize the `Vector` with zeros, `Int64[]; resize!(x,n)` only ask for the memory (if you print it before overwriting you will see garbage). Probably just for the setup the `resize!` solution is faster.

The difference in access performance you are seeing probably comes from `zeros` having already materialized and brought the `Vector` to the L1 cache, while accessing the positions in the `Vector` expanded by resize will cause a page fault (i.e., the OS was lazily waiting for you to access any position to only then really allocate the memory), and then some cache misses of bringing the `Vector` slowly into the L1 cache.

---

<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:** [April 29, 2022, 5:29pm UTC](https://discourse.julialang.org/t/performance-of-resize-vs-pre-allocating-with-zeros/80258/4 "2022-04-29T17:29:26Z")

</div>

Looks like @Henrique_Becker is right, the MWE

```julia
using BenchmarkTools

function test_access!(x,n)
  @inbounds for i = 1 : n
    x[i] = i
  end
end

function init1(n)
  x = Int64[]
  resize!( x , n )
  test_access!( x, n )
  return x
end

function init2(n)
    x = zeros(Int64, n)
    test_access!( x, n )
    return x
end

function init3(n)
    x = Vector{Int64}(undef, n)
    test_access!( x, n )
    return x
end

const n = 10_000_000;

@btime init1($n)
@btime init2($n)
@btime init3($n)

```

shows the expected

```julia
  13.780 ms (2 allocations: 76.29 MiB)
  18.666 ms (2 allocations: 76.29 MiB)
  15.528 ms (2 allocations: 76.29 MiB)

```

---

<div class="post-metadata">

**Author:** ![fcdimitr](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fcdimitr/32/26613_2.png) [@fcdimitr](https://discourse.julialang.org/u/fcdimitr)\
**Post date:** [April 29, 2022, 5:47pm UTC](https://discourse.julialang.org/t/performance-of-resize-vs-pre-allocating-with-zeros/80258/5 "2022-04-29T17:47:10Z")

</div>

I agree with your explanation. One strategy is lazy allocation, the other is eager. In the lazy case, the pages are not allocated by the OS until needed, hence the penalty when visiting them for the first time.

Thanks!

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [April 29, 2022, 6:04pm UTC](https://discourse.julialang.org/t/performance-of-resize-vs-pre-allocating-with-zeros/80258/6 "2022-04-29T18:04:23Z")

</div>

> [@Henrique\_Becker](#):
>
> Probably just for the setup the `resize!` solution is faster.

The fastest should be `Vector{Int}(undef, n)`, because it doesn’t have to allocate anew and doesn’t have to call into the runtime again.

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [April 29, 2022, 6:05pm UTC](https://discourse.julialang.org/t/performance-of-resize-vs-pre-allocating-with-zeros/80258/7 "2022-04-29T18:05:34Z")

</div>

> [@fcdimitr](#):
>
> One strategy is lazy allocation, the other is eager. In the lazy case, the pages are not allocated by the OS until needed, hence the penalty when visiting them for the first time.

Please refer to the following thread for an in-depth discussion about the (dis-)advantages of using `calloc`, as well as links to further discussions.

> [@Faster zeros with calloc](https://discourse.julialang.org/t/faster-zeros-with-calloc/69860):
>
> Abstract TL; DR zeros\_via\_calloc is a potentially faster version of zeros that is comparable to Array{T}(undef, ...) and numpy.zeros. function zeros\_via\_calloc(::Type{T}, dims::Integer...) where T ptr = Ptr{T}(Libc.calloc(prod(dims), sizeof(T))) return unsafe\_wrap(Array{T}, ptr, dims; own=true) end # Windows benchmark julia\> @btime zeros\_via\_calloc(Float64, 1024, 1024); 12.400 μs (2 allocations: 8.00 MiB) julia\> @btime zeros(Float64, 1024, 1024); 1.652 ms (2 allocations: 8.00 MiB)…

---

<div class="post-metadata">

**Author:** ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)\
**Post date:** [April 29, 2022, 6:50pm UTC](https://discourse.julialang.org/t/performance-of-resize-vs-pre-allocating-with-zeros/80258/8 "2022-04-29T18:50:56Z")

</div>

> [@Sukera](#):
>
> The fastest should be `Vector{Int}(undef, n)` , because it doesn’t have to allocate anew and doesn’t have to call into the runtime again.

I believe `Vector{Int}(undef, n)` can only be the same or faster. However, if we look at the number of allocations I am not sure if `x = Int[]; resize!(x, n)` does not end up being the same; because, maybe, `x = Int[]` does not allocate a memory space in the heap until you start putting things inside it or call `resize!` (i.e., it strongly relies on the asymptotic `O(1)` behaviour of `push!`).

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [April 29, 2022, 7:52pm UTC](https://discourse.julialang.org/t/performance-of-resize-vs-pre-allocating-with-zeros/80258/9 "2022-04-29T19:52:15Z")

</div>

> [@Sukera](#):
>
> Please refer to the following thread for an in-depth discussion about the (dis-)advantages of using `calloc` , as well as links to further discussions.

I started the registration process for [GitHub - mkitti/ArrayAllocators.jl: Allocate arrays with malloc, calloc, or on NUMA nodes](https://github.com/mkitti/ArrayAllocators.jl) yesterday. If you wanted to explicitly initialize the array, you can use `fill!`. This uses the C call `memset` under the hood.

```julia
using ArrayAllocators

julia> @btime test_access!(x,$n) setup=(x = zeros(Int64,$n))
  8.457 ms (0 allocations: 0 bytes)

julia> @btime test_access!(x2,$n) setup=(x2 = init($n))
  17.474 ms (0 allocations: 0 bytes)

julia> @btime test_access!(x2,$n) setup=(x2 = init($n); fill!(x2,0))
  8.026 ms (0 allocations: 0 bytes)

julia> @btime test_access!(x2,$n) setup=(x2 = Array{Int64}(undef, $n))
  19.933 ms (0 allocations: 0 bytes)

julia> @btime test_access!(x2,$n) setup=(x2 = Array{Int64}(undef, $n); fill!(x2, 0))
  8.492 ms (0 allocations: 0 bytes)

julia> @btime test_access!(x2,$n) setup=(x2 = Array{Int64}(calloc, $n))
  16.426 ms (0 allocations: 0 bytes)

julia> @btime test_access!(x2,$n) setup=(x2 = Array{Int64}(calloc, $n); fill!(x2, 0))
  8.472 ms (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [April 29, 2022, 8:28pm UTC](https://discourse.julialang.org/t/performance-of-resize-vs-pre-allocating-with-zeros/80258/10 "2022-04-29T20:28:18Z")

</div>

> [@Henrique\_Becker](#):
>
> because, maybe, `x = Int[]` does not allocate a memory space in the heap until you start putting things inside it or call `resize!` (i.e., it strongly relies on the asymptotic `O(1)` behaviour of `push!` )

If I remember correctly, the minimum size that is always allocated is 32. The `resize!` has to do an additional call into the C runtime, after allocating the array. The `undef` version only does a single one, so you’ll always have at least that as a difference.
