Is v[1:3] .= sin.(v[1:3]) efficient?

question
performance

#1

This is a question about how to use the dot-syntax for in-place assignment for sub-arrays.

Let v = [1., 2., 3., 4., 5., 6.]. I want to apply sin(...) in place to some elements. Specifically, I want to do:

v[1:3] .= sin.(v[1:3])

Is this efficient? I fear we might be creating a temporary sub-array that is not really necessary. Or is the compiler here clever enough to do it really in-place? An alternative is to use view:

view(v, 1:3) .= sin.(view(v, 1:3))

Is this necessary? Which way is better?


#2

This creates a temporary array from the slice on the right-hand-side (but not the left-hand-side). You can use v[1:3] .= sin.(@view v[1:3]). Or @views v[1:3] .= sin.(v[1:3]), or even put @views in front of your function to make the whole function body use views for slices.

@views is a new macro in 0.6, but is supported in 0.4 and 0.5 by the latest Compat.


#3

Is it always faster to use a view? It doesn’t always seem like it for benchmarks and I sometimes doubt when it is best to use them.

EDIT: I had made a mistake in my benchmark code – the views are actually (slightly) faster. Please ignore this post :blush:


#4

Is it still the plan to make v[1:3] return a view, or is that change now dropped?

(I’m in favour of that change, especially since transpose now returns a view in 0.6)


#5

Is is unlikely. With the new @views macro it feels sufficiently easy to make everything views anyway.


#6

No. If the view is discontiguous in memory (e.g. x[1:128:end]) and you are going to do a lot of operations on it, it is more efficient to make a copy, for example.

Note also that if your array is really small, e.g. if you are working with 3-component vectors as in your v[1:3] example, it is more efficient to use something like the StaticArrays package. (In StaticArrays, the compiler knows the length and can completely unroll the loop for you. And since the array is immutable, it can be put on the stack or in registers rather than in heap memory.)


#7

Nice, those are good rules of thumb that are easy to apply in writing code. Thanks!


#8

it just struck me that this is analogous to the choice between a view and a materialized view in a RDB.


#9

Note that the 0.6 manual has much the same advice in the “Performance Tips” section: http://docs.julialang.org/en/latest/manual/performance-tips.html#Consider-using-views-for-slices-1


#10

Keep in mind that transpose is only a view for vectors in v0.6. Matrices will have almost certainly have to wait until the release after. I feel bad about this, but due to the structure of LinAlg it is a surprising amount of code churn to achieve this (and maintain all the fast matrix operations we have now - in itself it is very easy).

If and when inmutables with references (ie pointers managed by the garbage collector) are “inlined” (ie become more efficient, like other inmutables) then the views as default thing might (possibly) be revisited, I dunno, we’ll have to wait and see but I wouldn’t count on it for now.