It is weird with sort!(x) going to the same function as sort!(view(x, 2:4)) and still view alone not allocation. So it reminded me of another issue, that a debugged, and showed to be very weird:
So I suggest you may file an issue on this, but is this a problem in practice? We often want to eliminate allocations entirely, a worth goal, and sometimes (e.g. for real-time) needed, but here since it’s just 2, not growing, maybe not a huge issue for you?
It indeed wouldn’t be a problem if I had to do this once. In my case I have a big array (millions of elements) and have to sort specific parts of it (in-place ideally). Since this will happen in a loop the allocs will build up quickly. Something like:
using BenchmarkTools
function test(arr::Vector{Int64}, cuts::Vector{UnitRange{Int64}})
for cut in cuts
sort!(view(arr, cut))
end
end
function main()
data = reverse(collect(1:20))
cut_at = [1:2, 4:8, 11:14, 16:18]
@btime test($data, $cut_at)
end
main()
Look like there is a special sorting function for Int64—could these two allocations be caused by min, max = extrema(v) on line 714 of the source for sort!
Looks like line 729 could actually allocate quite a lot more… that being said it’s not my impression that the sort! promises to be non-allocating, only that it promises to mutate the input in-place. Likely this integer sort optimization (Counting sort) is faster than generic sort algorithms, especially on large vectors, by enough to make up for the extra allocations.
As pointed above, it may be that those allocations are worthwhile, because the sorting algorithm is faster. You should benchmark that with the actual size of your arrays and views to see. If you are sorting small arrays of Ints, you may need some customized sorting method for optimal efficiency.
I think you are right, if we take the sort from here and implement it, we have 0 allocs and are faster, at least for this case:
function s!(v::AbstractVector)
# function sort!(v::AbstractVector, lo::Integer, hi::Integer, ::InsertionSortAlg, o::Ordering)
lo, hi = extrema(v)
lo_plus_1 = (lo + 1)::Integer
@inbounds for i = lo_plus_1:hi
j = i
x = v[i]
while j > lo
y = v[j-1]
if !( x < y )
break
end
v[j] = y
j -= 1
end
v[j] = x
end
return v
end
function ins_sort(arr::AbstractVector, cuts::Vector{UnitRange{Int64}})
for cut in cuts
s!(view(arr, cut))
end
end
function base_sort(arr::AbstractVector, cuts::Vector{UnitRange{Int64}})
for cut in cuts
sort!(view(arr, cut))
end
end
data = reverse(collect(1:20))
cut_at = [1:2, 4:8, 11:14, 16:18]
@btime ins_sort($data, $cut_at)
# 34.997 ns (0 allocations: 0 bytes)
@btime base_sort($data, $cut_at)
# 134.454 ns (8 allocations: 384 bytes)
ins_sort(data, cut_at) == base_sort(data, cut_at)
# true