I have a couple of times had the following problem. An algorithm which uses a vector v and does some push! and some pushfirst!. I know in advance the maximum number of pushfirst!, and the maximum number of push! that might possibly be needed. Moreover, this is a critical part of the code, and I don’t want reallocations. I can of course make a large vector, and manipulate two indices into it to handle this more manually.
In 1.11 there is a first argument to sizehint! which makes sure there is some room before the start of the vector to accomodate pushfirst! without reallocation. However, I need both push! and pushfirst!. In 1.11 with Memory-backed vectors, it’s quite easy to manipulate v.ref to move the start of the vector to a specific position in the backing Memory. This is what’s done with first=true. However this is undocumented.
It isn’t very hard to support arbitrary room before and after the vector inside sizehint!, but before I make a PR, does anyone have opinions on the API? My idea is to use the existing first argument, but with an integer where to start. I.e. something like sizehint!(v, 50, first=20). This is a bit unfortunate since true is and Integer equal to 1, but first=1 and first=true would be different.
Alternatively, one could implement a new function for direct placement of the start of the vector inside the Memory.
Where total gives the total number of elements that should fit inside v (current behavior) and spaceatfront (or perhaps some shorter name) gives how many elements should fit in front of the current elements within v.
IIRC, then the current first=true places all new space in front? That would be equal to sizehint!(v, total, total-length(v)) and is perhaps a bit wordy but I don’t mind being explicit with this I think.
Thought process:
There are 2 bits of information necessary: How much space there should be in front and how much space there should be in the back
The current design however only considers the total amount of space
So imo the most natural extension of the current (documented) behavior is the one I proposed.
The sizehint! function isn’t specific to vectors, though. Other collections, e.g., Dict, also support it. IMO it would be messy to have many keyword argument that only make sense for some classes of collections, but not for others.
Furthermore, I think it should be possible to implement the desired feature without expanding Julia’s interfaces. Something like this:
julia> v = Vector{Float64}(undef, 4) # allocate
4-element Vector{Float64}:
0.0
0.0
0.0
0.0
julia> v = empty!(v)
Float64[]
julia> v = sizehint!(v, 1, first = true, shrink = false) # now one element should be reserved in the front
Float64[]
I hadn’t thought of that. A good point. On the other hand, the docstring of sizehint! talks about “space before the start of the collection”, not limiting it to vectors. And it already has different keyword arguments for different collections. It’s not a great leap to allow a first::Union{Bool,Integer} to specify how many before the start of the collection.
While it could be possible to somehow shoehorn it in with the existing syntax, I fear this will make the keywords’ meanings dependent on the values of the other arguments, it could easily be confusing.
I like this 3 argument version. With a default value for the third argument it defaults to the current variant. However, I think it’s too late to change the 1.11sizehint! which has got a first keyword argument (it’s already at rc2). So the first keyword argument must be present. And it will be confusing together with the spaceatfront.
However, if we drop the default value for spaceatfront, we can drop the first keyword for this method. That is, for a Vector there are two sizehint! methods, one with two arguments, and one with three arguments:
Overloading first to be a Union{Int, Bool} seems reasonable to me, I feel like the intuitive guess for what’s implied matches the semantics Simen talks about fairly well: it’s either all/none in front, or n in front.