Live data from Hacker News

Identifying Rust's collect: >() memory leak footgun

blog.polybdenum.com

51–60 of 129 posts

Re: Identifying Rust's collect:<Vec<_>>() memory leak footgun

#51
I’ve read the article and I’m still lost as to how this optimization results in a 200x discrepancy between capacity and length. Was the mapped element size 200x smaller in this case? Or is there some bug where repeated mappings like this cause a 2x growth based on the previous element size instead of the target element size?

Re: Identifying Rust's collect:<Vec<_>>() memory leak footgun

#52

I don't think it's a memory leak. It's re-using the same space of the underling array. It's allocated. Dropping the Vec will release the memory. In v1 you put in 128 containers is with each 1024 boxes. Then v2 is taking out the first box out of each container, tossing the container and putting the box at the space where the container was, packing them. The fact that capacity doubles when you remove the as u8 is... no…

It seems like you have a better grasp on the problem than I. I understand why capacity in terms of number of elements doubles. But why did he end up using 200x more memory? Is it that the transient memory used the 18GiB and then he reduced it to vector of elements that were 200x smaller? Or is it that there’s some other problem that when you collect, it’s reallocating 2x using the wrong type to do the computation?

Because it doubles in every node and OP has 300k of them[1]. It's not a leak, since memory is...well...accounted for and easily freed by either dropping Vec or calling shirk_to_fit. IMO it's expected behavior.

Honestly, if I had 300k Vecs, I would call `shrink_to_fit` even without that optimization.

1: Since the edge list calculation logic is run for one node at a time in sequence, the fact that it temporarily uses 132kb of memory would normally not be a problem at all. However, the new collect() optimization meant that that internal storage was being preserved in the final vec which got inserted into the list of edge lists, thus keeping around that 132kb of obsolete memory forever. Multiply that by 300k nodes and suddenly you’ve leaked the computer’s entire RAM.

Re: Identifying Rust's collect:<Vec<_>>() memory leak footgun

#53

I don't think it's a memory leak. It's re-using the same space of the underling array. It's allocated. Dropping the Vec will release the memory. In v1 you put in 128 containers is with each 1024 boxes. Then v2 is taking out the first box out of each container, tossing the container and putting the box at the space where the container was, packing them. The fact that capacity doubles when you remove the as u8 is... no…

No, if that's all it was, the excess memory usage would be 2x. But it's 200x. Right?

Please read the article. His produces 200x intermediary values. Clickbait title, since it wasn't a leak.

Re: Identifying Rust's collect:<Vec<_>>() memory leak footgun

#54
post #38

I appreciate the mention of Box . I've never (or at least very rarely?) seen people using it, even in rustc, although I'm sure there's somewhere in the compiler that uses it. I've kind of gotten the feeling that Box is frowned upon as a needless optimization because the one-word storage difference is so minimal, but I appreciate having the immutable size semantics as a way of ensuring no extra allocations occur by wa…

Another rust type you should look out for is BoxIn my rust code I find a lot more uses of Box rather than Box. Strings are often fixed length identifiers or usernames that get passed around frequently and stored in data structures. Box can be a drop-in replacement in a surprising amount of code

Re: Identifying Rust's collect:<Vec<_>>() memory leak footgun

#55
post #53

Earlier quoted context omitted.

No, if that's all it was, the excess memory usage would be 2x. But it's 200x. Right?

Please read the article. His produces 200x intermediary values. Clickbait title, since it wasn't a leak.

I did read the article.

> the memory waste from excess capacity should always be at most 2x, but I was seeing over 200x.

So the 200x analysis is his problem?

Re: Identifying Rust's collect:<Vec<_>>() memory leak footgun

#56
post #53

Earlier quoted context omitted.

No, if that's all it was, the excess memory usage would be 2x. But it's 200x. Right?

Please read the article. His produces 200x intermediary values. Clickbait title, since it wasn't a leak.

I agree that this is not a memory leak.

However, the semantic distinction between "this uses much more memory than expected" and "this is a memory leak" is a little subtle, and it seems pretty rude to call it clickbait.

Re: Identifying Rust's collect:<Vec<_>>() memory leak footgun

#57
post #38

I appreciate the mention of Box . I've never (or at least very rarely?) seen people using it, even in rustc, although I'm sure there's somewhere in the compiler that uses it. I've kind of gotten the feeling that Box is frowned upon as a needless optimization because the one-word storage difference is so minimal, but I appreciate having the immutable size semantics as a way of ensuring no extra allocations occur by wa…

Another rust type you should look out for is Box In my rust code I find a lot more uses of Box rather than Box . Strings are often fixed length identifiers or usernames that get passed around frequently and stored in data structures. Box can be a drop-in replacement in a surprising amount of code

Arc is an atomically ref-counted string, also quite useful in async or multi-threaded code where (unlike w/ Box) you sometimes can't know in advance which task will drop the last reference to some data.

Re: Identifying Rust's collect:<Vec<_>>() memory leak footgun

#58

I’ve read the article and I’m still lost as to how this optimization results in a 200x discrepancy between capacity and length. Was the mapped element size 200x smaller in this case? Or is there some bug where repeated mappings like this cause a 2x growth based on the previous element size instead of the target element size?

I think the behavior is that if you have a Vec of u128 (say 1000), filter that to fewer elements (say 10), and then collect it into a Vec of u32 you might expect the resulting value to be around 40 bytes but in beta Rust it is 16000 bytes. In current Rust the collect would cause a new 10 element Vec of u32 to be allocated, in beta it reuses the original larger allocation. The author's code is doing a bit more but essentially when moving to beta Rust the new optimization caused memory usage to increase by a big multiple.

Re: Identifying Rust's collect:<Vec<_>>() memory leak footgun

#59
post #38

I appreciate the mention of Box . I've never (or at least very rarely?) seen people using it, even in rustc, although I'm sure there's somewhere in the compiler that uses it. I've kind of gotten the feeling that Box is frowned upon as a needless optimization because the one-word storage difference is so minimal, but I appreciate having the immutable size semantics as a way of ensuring no extra allocations occur by wa…

Another downside of Box is that Vec::into_boxed_slice copies all elements from the source vector. Vec::shrink_to_fit does not appear to copy. This is based on the stable docs.

Re: Identifying Rust's collect:<Vec<_>>() memory leak footgun

#60
post #59
post #38

I appreciate the mention of Box . I've never (or at least very rarely?) seen people using it, even in rustc, although I'm sure there's somewhere in the compiler that uses it. I've kind of gotten the feeling that Box is frowned upon as a needless optimization because the one-word storage difference is so minimal, but I appreciate having the immutable size semantics as a way of ensuring no extra allocations occur by wa…

Another downside of Box is that Vec::into_boxed_slice copies all elements from the source vector. Vec::shrink_to_fit does not appear to copy. This is based on the stable docs.

It doesn't copy the elements if the source vector is already at max capacity, which you can often arrange for beforehand.
Post reply on HN