Resize! and empty! immediately, why?

hello, found someone else’s code like this

abstract type AbstractFoo{T} end
struct Foo{T} <: AbstractFoo{T}
    a::Vector{T}
    function Foo{T}(size::Int) where T
        foo = new(T[])
        resize!(foo.a, size)
        empty!(foo.a)
        return foo
    end
    Foo{T}() where T = new(T[])
end

why is he doing this?

resize!(foo.a, size)
empty!(foo.a)

This is somewhat the same idea in Go as slice’s having a different “length” than “capacity”. When you call empty! on a Julia Vector, it sets the length to zero, but the underlying memory stays the same. This means that later when you do push! or append, they avoid reallocating the array by trying to “right size” it once up front. They could skip one step here, however, by doing:

foo = new(Vector{T}(undef, size)
empty!(foo.a)
return foo

which avoids allocating an empty array, resizing it (which mostly likely allocates a new array to the full size), then emptying by just allocating the array to the full size to begin with.

3 Likes

Would

return new(sizehint!(T[],size))

be equivalent to all three lines? Which is preferable?

2 Likes

Yeah, this seems like an un-idiomatic way to do sizehint!

Not quite, because you still have the extra allocation of the initial empty array and then reallocation from size hint. The undef method only allocates once.

That’s what I thought. I like the readability of sizehint!. Possible suggestion:
sizehint(Vector{T}, size) to return an empty array with appropriate hint (note no ! in name).

But is this guaranteed? I do not see the documentation saying that the array cannot be garbage collected after empty! This is unlike sizehint!

You’re right that I don’t think it’s guaranteed, but currently (<1.9 release) won’t shrink the array on empty!. And to me that makes sense: if I’m calling sizehint! on an array w/ a smaller size, I’m signaling “hey, I actually want this array to be smaller now”, so reclaiming the memory makes sense. For empty!, it’s often used in loops where I’m push!ing or append!ing then empty!ing and doing it all over again, so it’s more clear that I’m “zeroing out” the array and will probably reuse the memory again later. That makes sense to me that the memory will stay allocated then. Note that sizehint! didn’t used to be able to shrink arrays, only grow them, so for a long time, it was only possible to reclaim memory by having the array go away all together.