-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: add views founded on cartesian indexing #8235
Conversation
Thank you for preparing this request. I have just tried it and one problem seems to be that
and not
to the I also think that the ability to handle views from views properly is important for views to work well for linear algebra, which is what I am mostly concerned about. |
Adding that declaration would preclude making a view of a |
Also meant to add: I completely agree that being able to create views-of-views is essential. The main purpose here is to round out the menu of options: (1) ArrayViews, (2) this (ArrayViewsAPL without the stagedfunctions), (3) ArrayViewsAPL. I felt this was far enough to get some feedback & clarity of which of these we want. (And they are not mutually exclusive, we could combine 1 with either 2 or 3. But there are some disadvantages there, too; which do you create by default?) |
This is interesting, and impressively compact. Any idea what performance is like vs. ArrayViews.jl? As I sit here running code that is spending a ridiculous amount of time in GC mostly because of allocation in array view creation, I'm also wondering whether we should think about storing the indexes/dims in n-field immutables instead of tuples until we have more efficient tuple storage so that they can be stored inline. At least then there would be fewer objects to keep track of, although it's still a problem that the view itself has to be heap-allocated because of the reference to the underlying array. |
I actually didn't do any benchmarking/optimization before pushing this. But surprisingly, it looks almost indistinguishable (I'm a bit puzzled, frankly): using Base.Cartesian
import ArrayViews
@ngenerate N Float64 function sum_cart{T,N}(A::AbstractArray{T,N}, n)
s = 0.0
for j = 1:n
@nloops N i A begin
@inbounds s += @nref N A i
end
end
s
end
function sum_linear(A, n)
s = 0.0
for j = 1:n
for i = 1:length(A)
s += A[i]
end
end
s
end
function create_v(A, n, index1, index2)
B = Base.Views.subview(A, index1, index2)
for i = 1:n
B = Base.Views.subview(A, index1, index2)
end
B
end
function create_av(A, n, index1, index2)
B = ArrayViews.view(A, index1, index2)
for i = 1:n
B = ArrayViews.view(A, index1, index2)
end
B
end
A = rand(1000,10000);
println("Contiguous")
B = create_v(A, 1, 1:1000, 1:10000);
gc(); @time B = create_v(A, 10^5, 1:1000, 1:10000);
C = create_av(A, 1, 1:1000, 1:10000);
gc(); @time B = create_av(A, 10^5, 1:1000, 1:10000);
sum_cart(A, 1)
@time sum_cart(A, 100)
sum_cart(B, 1)
@time sum_cart(B, 100)
sum_cart(C, 1)
@time sum_cart(C, 100)
sum_linear(A, 1)
@time sum_linear(A, 10)
sum_linear(B, 1)
@time sum_linear(B, 10)
sum_linear(C, 1)
@time sum_linear(C, 10)
println("Strided")
B = create_v(A, 1, 1:3:1000, 1:10000);
gc(); @time B = create_v(A, 10^5, 1:3:1000, 1:10000);
C = create_av(A, 1, 1:3:1000, 1:10000);
gc(); @time B = create_av(A, 10^5, 1:3:1000, 1:10000);
sum_cart(A, 1)
@time sum_cart(A, 100)
sum_cart(B, 1)
@time sum_cart(B, 100)
sum_cart(C, 1)
@time sum_cart(C, 100)
sum_linear(A, 1)
@time sum_linear(A, 10)
sum_linear(B, 1)
@time sum_linear(B, 10)
sum_linear(C, 1)
@time sum_linear(C, 10) Results:
|
Ah, right; if I were using the ContiguousView properly, I would have used
but all others remain the same. One could, of course, augment |
I don't understand this comment. Your suggestion is pretty much what I thought I suggested. I don't want to restrict Have you done experiments with moving the inclusion of |
Oh, shoot, I totally missed the second part of your statement. The part in code is strikingly similar to the declaration of the And no, I haven't tried seriously integrating this. Mostly I'm interested in finding out what people think of the tradeoff between:
stagedfunctions fix both of these. But if folks are OK with the compromises here, I can add the code to create views-of-views (that's a must-have) and look seriously at merging this. |
Adding five minutes to compilation time is probably not acceptable and neither is different behavior for different values of |
Now that we have staged functions, I'm redoing this. |
Here's a demo of what a View type founded on cartesian indexing would look like. This is a complete implementation of the constructor and scalar indexing (i.e.,
V[1,2,3,4]
returning a scalar rather than an array); the only big missing piece is creating a view from a view. And has been discussed elsewhere, for some AbstractArray types (notably, BitArrays) you'd want specialized implementations of certain functions.Very briefly, the way these work is dirt simple:
Given this,
B[1,3]
simply gets directly translated intoA[2,5]
. The big advantage is that this works for any AbstractArray. You won't get optimal performance for BitArrays, but SparseMatrices should work perfectly well straight out of the box, as well as many, many other types of AbstractArrays.If you look at the code, you'll see that the large majority of functions are one-liners. The one exception is that if you say
B[4]
, then it has to do linear indexing, so it generates the equivalent ofBut notice there are no loops in here---everything is generated specifically for the problem at hand.
The main issue is that to support both
sub
andslice
(does ArrayViews.jl even support slices?), you need a lot of methods. In the table below,ncreate
is the number of constructors generated (actually one forsubview
and one forsliceview
), andnindex
is the number of indexing methods (actually one forgetindex
and one forsetindex!
).The reason I was holding out for stagedfunctions is that you could support arbitrary dimensionality without waiting 5 minutes for the file to finish getting through code lowering. But if people are eager to move forward with this, perhaps we shouldn't wait.