-
-
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
Make length(A.nzval)==nnz(A) #30662 #30676
Conversation
I suspect that we'd have to audit the code more carefully for places that might use the current assumption. We might also want to add more checks for this since if we make this change then people should be able to rely on it consistently. First of all, we'd have to decide if we want to make this change. |
Sure. I was not suggesting to rush things up. Thought that having a patch could help the discussion. |
Compared to #30435 there is essentially one difference: |
Wasn't the old guarantee looser than the new one? I.e. previously you could assume that |
Yes, exactly. Note that even though |
My opinon is, the |
I agree. But given that there is no general consensus on removing the allocation, I think that at least the changes in this PR are an improvement in terms of consistency. I may have misunderstood, but I think that @stevengj's suggestion was not to remove the allocation from similar (just a bit of refactoring so as to leave a |
The new |
@andreasnoack what kind of checks would you suggest? |
Crossposting from #30662:
|
Maybe we want to add some |
|
Should the proposed checks be part of this PR? |
I think it would make sense to include them here but it might not be obvious where to but them. However, it would be great if @abraunst would skim through the code for uses of |
Maybe the best would be just to throw on construction if the buffer sizes are not right. If the user modifies the buffers post-construction, he's on his/her own, no? |
Mostly but it wouldn't hurt if we could detect an inconsistency before a segfault. However, I was mainly thinking that these checks would be useful internally in |
So I hopefully implemented @andreasnoack's request. It was harder than what I planned... With respect to the previous version:
|
Forgot EDIT: indeed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fully agree, with a few exceptions:
- The new function name
capacity
might be misleading - I would prefersizehint
, because it is in a way the getter function corresponding tosizehint!
. - The function
sortBuffers!
might be better calledsort_CSC_buffers!
or similar.
Could someone kindly invoke @nanosoldier to check performance regressions against master? |
As exactly as possible of course. This was referred to enlarging the buffer, I think we should do nothing if the value passed to |
Anything is possible, but giving the exact buffer size you asked for is often bad for performance. It's the memory subsystem's job to worry about these things and do a good job, trading off speed and overhead appropriately. Insisting on exactness is a form of micromanaging the memory system. |
In my opinion, the role of |
The purpose of What if Julia's memory subsystem decides that it only gives array memory regions whose sizes are multiples of 16 bytes? Should calling |
Sure, my point is that the user is likely much more informed about what is the distribution of this possible "off by a bit" length than the memory subsystem. I'm probably too new to julia to be discussing this (if I'm talking nonsense, I apologize). But I feel the lack of fine control on memory allocation a bit unsettling (not for casual use of course, but for libraries and performance-critical code).
I feel like a straw man argument is being used here. Of course it's not really important to be precise to the bit (that was in my mind roughly the meaning of "as exactly as possible"); the point is that we are doing predictive pre-allocation and purposely allocating more (sometimes twice as much) than the user prediction. EDIT: maybe it would be better to move this particular discussion somewhere else (e.g. Discourse), as it is separate from the PR. I still don't understand the outcome of triage WRT. this PR (which is not about performance at all). |
I found this spot in julia/stdlib/SparseArrays/src/sparsematrix.jl Lines 3132 to 3133 in 60457de
This one assumes (only) that julia/stdlib/SparseArrays/src/sparsematrix.jl Lines 3081 to 3087 in 60457de
That underlines @StefanKarpinski 's statement
|
Yes, this is too error prone. Another example is the recently fixed #31187. |
Though the data structure doesn't fundamentally change, moving away from in some sense explicitly managing memory, and towards reliance on the memory management system / concomitant use of higher level operations, is something of a paradigm shift for those of us used to a world where CSC/CSR by definition involves the former :). It implies writing different sorts of code and perhaps different sorts of thinking. It also seems like the logical conclusion of this direction would be removal of the last element of |
I don't think that's really necessary—the one extra value isn't costing that much and allows these vectors to be passed as-is to libraries that expect this representation. This also relates to the reason, in my understanding, that |
It's not intentionally a strawman argument at all. I was taking "as exactly as possible" literally, which would seem to object to this kind of small over-allocation. So it seems that the only issue is regarding large over-allocation. I would point out that large over-allocation is only a problem if you do it yourself and insist on filling values with something. If you let the system do it, it's free since the kernel doesn't actually allocate memory until you use it. So sure, we seem to be allocating 2x what's required, but most of those memory pages are not allocated at the time and will never be allocated if they're not used. |
I didn't know (or recall) this, thanks for taking your time to explain. I'm starting to understand then :-) What confused me the most is the fact that g++ seems to exactly allocate the requested size on |
I agree that practically speaking it's neither necessary or advantageous to remove the last element of |
I'm curious about how you're determining this? If you're writing C or C++ code, you're allocating memory with |
What do you mean by shrink-wrapped? What I mean is just that the standard library does not seem to ask to #include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> v(1000000);
cout << v.capacity() << endl;
v.reserve(1000001);
cout << v.capacity() << endl;
} gives as output here
In contrast, in julia (using
I understand now that the pages are not necessarily mapped to physical memory until access, but still it seems to me that the c++ policy is different wrt how much to |
Bump - what do we need to do here to get this over the hump? |
Bumping here. What should we do here? |
Bump @andreasnoack and @abraunst |
I kind of lost the stamina to insist pushing on this, as the outcome of triage wasn't clear (I asked for clarifications but did not receive one). I got simultaneously overswamped with other stuff. What I recall about this is:
I don't think it would be wise to work on this anymore until a decision is made on how to proceed... |
Agree, sorry for the unused effort on your part! It may still end up getting used but you’ve certainly done your part and then some. |
NP, I'll try to contribute somewhere else when I get some time back, at least until the direction is more clear here 😄 |
Yes, I was also unclear on what the final resolution was here. Let's wait for @andreasnoack to chime in. It would be nice to resolve and get this done. |
The issue was not the doing (it's done) but the disagreement about whether or not it's desirable. |
In the current form, I believe that this PR depends on #31287. Might be an option to just have an internal capacity function for now. |
Can we eliminate the addition of |
I can try to have a look at this again as soon as I get some time if no one else wants to do it. Unfortunately it seems that the julia> function f()
x=zeros(0);
sizehint!(x, 10^7)
for i=1:10^7
push!(x, i)
end
x
end
f (generic function with 1 method)
julia> function g()
x=zeros(0);
resize!(x, 10^7)
@inbounds for i=1:10^7
x[i]=i
end
x
end
g (generic function with 1 method)
julia> @btime f();
127.796 ms (2 allocations: 76.29 MiB)
julia> @btime g();
36.686 ms (2 allocations: 76.29 MiB) |
Moved to #40523 |
Adresses #30662. See also: #26560, #30435