View and Slices: comparison of speed

question

#1

I measured the speed for mapping a function of several variables on slices and views of arrays.
The speed of slicing seems to be higher than views!
And using the macro form, @view, increases the speed compared to the explicit functional form, view(...) !
The size of allocations are the same for all three forms, but the number of allocations is considerably lower for the slicing.

Is there an explanation for this?

function secant(x1, x2, y1, y2)
    s1 = (y2 - y1) / (x2 - x1)
    return s1
end

N0 = 10^6;
xs = 1:N0;
ys = 2.5xs;

@time map(secant, xs[1:end-1], xs[2:end], ys[1:end-1], ys[2:end]);
          
# 0.022587 seconds (27 allocations: 7.631 MiB)
          
@time map(secant, @view(xs[1:end-1]), @view(xs[2:end]), @view(ys[1:end-1]), @view(ys[2:end]) );

# 0.035256 seconds (99 allocations: 7.634 MiB)

@time map(secant, view(xs, 1:endof(xs)-1), view(xs, 2:endof(xs)), view(ys, 1:endof(ys)-1), view(ys, 2:endof(ys)) );

# 0.062632 seconds (99 allocations: 7.634 MiB, 33.77% gc time)

Times are measured on Intel Core i3 under Ubuntu 16 LTS.


#2

That’s because you’re not really allocating new slices in the first place. Both xs and ys look to be ranges – and slicing a range produces another range. Ranges are compact representations of vectors with evenly increasing values. Try it with xs=collect(1:N0) instead.

Even still, the speed of views isn’t always a win – that’s one reason why we decided not to make it the default.


#3

could you comment on the number of allocations in each case?


#4

Allocating a new range is about the same cost and size as allocating a view into a range. Try it with vectors instead of ranges and you’ll see a difference in memory allocated but the allocation count may be similar since we still cannot always eliminate allocating view objects (but a lot of work has been done on that).


#5

That’s just an artifact of doing your timing at global scope:

julia> fslices(xs, ys) = map(secant, xs[1:end-1], xs[2:end], ys[1:end-1], ys[2:end])

julia> fviews(xs, ys) = map(secant, @view(xs[1:end-1]), @view(xs[2:end]), @view(ys[1:end-1]), @view(ys[2:end]) )

julia> fslices(xs, ys); @time fslices(xs, ys);
  0.024084 seconds (6 allocations: 7.630 MiB)

julia> fviews(xs, ys); @time fviews(xs, ys);
  0.029539 seconds (6 allocations: 7.630 MiB)

julia> xs′ = collect(xs);
       ys′ = collect(ys);

julia> fslices(xs′, ys′); @time fslices(xs′, ys′);
  0.030760 seconds (18 allocations: 38.148 MiB)

julia> fviews(xs′, ys′); @time fviews(xs′, ys′);
  0.027073 seconds (14 allocations: 7.630 MiB)