-
Notifications
You must be signed in to change notification settings - Fork 11
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
Restriction to the last dimension #3
Comments
It's currently due to design -
|
Oh, I forgot: One important design criterion is that an |
I did a few tests and it looks like But changing the size of any dimension but the last would be very expensive, as it would require memory allocation and data movement. @ChrisRackauckas, do you have any specific use case(s) in mind? |
I want to change the size of Jacobian matrices but attempt to re-use some of the same memory / not re-allocate the whole thing. I am not sure if it's possible. |
You could try manually with julia> buf = Vector{Int}(10)
10-element Array{Int64,1}:
0
0
0
0
0
0
0
0
0
0
julia> a = Base.ReshapedArray(buf, (5, 2), ())
5×2 reshape(::Array{Int64,1}, 5, 2) with eltype Int64:
0 0
0 0
0 0
0 0
0 0
julia> push!(buf, 3)
11-element Array{Int64,1}:
0
0
0
0
0
0
0
0
0
0
3
julia> a
5×2 reshape(::Array{Int64,1}, 5, 2) with eltype Int64:
0 0
0 0
0 0
0 0
0 0
julia> buf[2] = 7
7
julia> a
5×2 reshape(::Array{Int64,1}, 5, 2) with eltype Int64:
0 0
7 0
0 0
0 0
0 0 You have to create a new wrapper every time you want to resize. But this approach lets you live dangerously and circumvent the "cannot resize array with shared data" error. The best reason to try it this way and see if it makes a difference is that you might save yourself some time: I'd be a little surprised if this helps that much, unless you're talking very small arrays. These days Julia's GC is pretty impressive. |
Although I just realized I pulled such tricks in TiledIteration, so maybe... |
It might be - if it turns out that Implementing the necessary data movement by hand would be a bit of work, though we could just do it via temorary views for now and live with the alloc/GC views (I wish we had stack-allocated views already ...). And it might not cost anything in certain use cases where the array contents doesn't need to be conserved during resize - I guess that's that case for you, Chris? In such cases, one could resize to size (0,0,...) and then blow the array up again to the desired new size - would result in no data movement. And memory allocation would only occur sometimes if the new total length is greater than ever before in the array's history. Would that fit your use case, Chris? I'm still not sure about the mutable/immutable struct question, though. |
@timholy: These days Julia's GC is pretty impressive. GC can still quickly become the limiting factor when running on many threads, though, in my experience. |
One way to make any dimension elastic, without sacrificing much efficiency, would simply be to allocate padding in each dimension (growing geometrically as needed whenever data is appended). This would mean that the data is no longer contiguous in memory, but:
(Access is exactly equivalent to a
There's nothing about this growth/shrink algorithm that you can't implement natively in Julia for multiple dimensions — it is just allocating padding and accessing a view. |
Hmhm - that would require up-front knowledge of the maximum size, right? |
No. You do exactly the same thing as (Because there are only a logarithmic number of re-allocations, you get O(1) amortized time to increase any dimension by 1.) |
Ah, yes - but that would indeed require There's on other problem though: Resized can then invalidate existing views into the array - from what I understand, that's one of the reasons why standard multi-dim Maybe we should add a separate type, like |
A project I'm working on requires me to iteratively update a matrix
Hopefully it's useful to you all. Naturally, any other views into |
Thanks @alejandroschuler - would you like to try your hand at a PR? It would be nice if we could share as much code as possible between |
I'd like to but I actually only barely know what I'm doing in julia. Re: a supertype: all the shared methods would then be dispatched on that supertype, yeah? In terms of naming, perhaps Related question: I noticed that in my implementation matrix multiplication were painfully slow if I didn't manually define |
We'll have a try if you like. :-) I can put this on my to-do list, but I'm afraid quite a few high-priority things are already overdue, so I probably won't be able to get to this myself soon.
Yes
That would force us to go to v2.0.0, because it would break backward compatibility. We shouldn't change the name of the existing type. I'd be fine with
Linear algebra is fast for the current
Take a look at https://discourse.julialang.org/t/method-forwarding-macro/23355 , but it's not a magic pill. In the end, if storage can't be compact, I don't think you can reach optimal linear algebra speed. But if you want to extend the first dimension and still keep things compact im memory, vcat on a standard |
Sounds good. We'll see who gets to it first! Re: naming: can you tell I'm not a software engineer? haha. Yes, preserving backwards compatibility makes sense. Thanks a bunch for that link, seems like TypedDelegation is exactly what I need here if I can figure out how to except |
in my application it's lots of little chunks so this ended up being a huge bottleneck and that's why I'm here :) |
No worries! :-) If you're interested, here's some more info on Julia and semantic versioning: https://pkgdocs.julialang.org/v1/compatibility/#Version-specifier-format
Ah, yep, then @stevengj's scheme (see above) will be the way to go. And you'll have to do linear algebra operations with it while it grows, not just at the end, right? In that case, there's no way around generic, pure-Julia linear algebra. However, there are quite a few fast packages in that direction now that you can try out (list likely incomplete): https://github.com/mcabbott/Tullio.jl, https://github.com/MasonProtter/Gaius.jl, https://github.com/chriselrod/PaddedMatrices.jl We can have ElasticArrays depend on any of them, they're far too "heavy", but your application could use them directly the new "super-elastic" arrays. |
You mean |
The naive implementation looks like this:
|
Would this work for you, performance-wise? Store using LinearMaps
...
X_ea = ElasticArray(...) # Initial X
X = LinearMap(X_ea) # X.lmap === X_ea
P = Xm' * X # O(1) operation, doesn't run any calculation, just creates a new LinearMap
...
append!(Xm.lmap, ...) # As Xm.lmap === X_ea grows, P grows as well
result = heavy_computation(X, P)
|
Thanks @oschulz, but no dice :( |
Ah, yes, that's a problem when using LinearMaps sometimes, I suggested to make |
Yes, if there's explicit factorization and so on, LinearMaps won't help. But in that case, will an elastic array really safe much memory allocation? After all, these factorization will then be allocated very frequently anyhow and be of similar size, right? Or is all of it in-place? |
Yeah it all has to be reallocated to make this happen, but I think the speedup comes from having to do it once instead of twice. Empirically it's much faster than the |
My you do have a tricky use case there! :-) |
Why the restriction to the last dimension? Is this due to the design or could that be changed?
The text was updated successfully, but these errors were encountered: