Live data from Hacker News

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

blog.polybdenum.com

111–120 of 129 posts

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

#111
post #109

Earlier quoted context omitted.

You can use slices.Grow for that purpose in Go. Exact sizing is only possible during slice creation.

It's unclear which policy slices.Grow exhibits, and so maybe it's not guaranteed whether you get the equivalent of Vec::reserve or Vec::reserve_exact ? My guess since it's not mentioned and no alternative is supplied is that you get Vec::reserve_exact - the same pessimisation footgun.

You get reserve, not reserve_exact. You are right that it would be allowed to implement reserve_exact semantics instead, given the method comment. But that’s not how it works, if you want reserve_exact semantics, you need to take extra steps. That said, the semantics are not exactly the same as reserve. The go compiler may optimize since allocations (I don’t know if it does though).

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

#112

Earlier quoted context omitted.

Someone here linked an open ticket for this issue. In the comments at least one person made basically the same argument that holding on to a potentially large % of memory is a surprising sharp edge, meanwhile shrinking the Vec and perhaps allocating is unsurprising behavior. Requiring many additional defensive shrink_to_fit calls to avoid this problem seems like the wrong tradeoff but I don't write enough Rust to hav…

The question is how much overhead would adding a check to determine to shrink if > % of freedom to all code that doesn’t need this optimization entail? The reason it’s important to consider is that I can always add a shrink_to_fit even if it’s a sharp edge. I can’t remove the conditional within the std library even if I know it doesn’t apply. And adding explicit APIs to control this nuance is a bit much (whether thro…

I'm guessing allocator time, memory usage, and cache residency are the major performance considerations. Vec knows what size it is so the comparison is cheap and in any case collect is already expected to allocate depending on what it is fed.

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

#113

Earlier quoted context omitted.

The question is how much overhead would adding a check to determine to shrink if > % of freedom to all code that doesn’t need this optimization entail? The reason it’s important to consider is that I can always add a shrink_to_fit even if it’s a sharp edge. I can’t remove the conditional within the std library even if I know it doesn’t apply. And adding explicit APIs to control this nuance is a bit much (whether thro…

I'm guessing allocator time, memory usage, and cache residency are the major performance considerations. Vec knows what size it is so the comparison is cheap and in any case collect is already expected to allocate depending on what it is fed.

The comparison is "cheap" if you can speculate through it & even then isn't free. If your comparison is 50/50 on branching then you're going to pay a penalty every time you hit that code path. It's entirely possible that the map operation is going to dominate the check, I'm just highlighting it's not free and it's a good idea to validate the cost somehow first (but also recognize there are applications you're going to penalize without them realizing it).

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

#114
post #86

Earlier quoted context omitted.

C# doubles the size of a List each time you hit the capacity (if you didn't set it correctly when you created the list or left it at the default). Does Go do something similar or does it only increase it by 1 each time?

It grows by 2x for small sizes, then it transitions to growing it by 1.25x

Using 1.25 creates more copies along the way than 2x.

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

#115
post #86

Earlier quoted context omitted.

It grows by 2x for small sizes, then it transitions to growing it by 1.25x

Using 1.25 creates more copies along the way than 2x.

Sure. It's a tradeoff to avoid wasting huge amounts of memory once the list gets huge.

No matter what the growth factor, you want to set the capacity up front if you know it. But 2x and 1.25x are both reasonable.

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

#116

Earlier quoted context omitted.

… Box::leak¹ is a function that exists. That seems like a memory leak, no? Less tongue-in-cheek, if a program allocates far more memory than expected of it, I going to colloquially called that a "memory leak". If I see a Java program whose RSS is doing nothing but "up and to the right" until the VM runs out of memory and dies a sweet sweet page thrashing death, I'm going to describe that as a "memory leak". Having so…

> " If I see a Java program whose RSS is doing nothing but "up and to the right" until the VM runs out of memory and dies a sweet sweet page thrashing death, I'm going to describe that as a "memory leak". " By this definition, if a program reads in a file and you point it to a small file then the program does not have a memory leak, but if you point it to a large enough file, then the program does have a memory leak.…

There is an implied steady state in what the program is doing. If it goes right from "loading" to "exit" then you need a more complicated analysis.

When you have that steady state, that definition looking at uncontrolled growth is more useful than trying to dissect whether the memory is truly unreachable or only practically unreachable.

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

#117

Earlier quoted context omitted.

… Box::leak¹ is a function that exists. That seems like a memory leak, no? Less tongue-in-cheek, if a program allocates far more memory than expected of it, I going to colloquially called that a "memory leak". If I see a Java program whose RSS is doing nothing but "up and to the right" until the VM runs out of memory and dies a sweet sweet page thrashing death, I'm going to describe that as a "memory leak". Having so…

> I don't care? You're just forcing me to wordsmith the problem description Yes, because if you don't define the problem clearly, the problem won't be solved. Java being inefficient with memory use doesn't mean any memory was leaked. Memory leaks can be tricky to track down, and if I spent 6 hours looking for a memory leak only to come back and found out you meant it uses more memory than what's efficient I'd be piss…

"uses more memory than what's efficient"

There is a hidden memory store using orders of magnitude more RAM than the live data. Why do we need to nitpick exactly how hidden it is? Are you going to be mad if I don't know whether it's literally inaccessible or not?

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

#118

Earlier quoted context omitted.

Someone here linked an open ticket for this issue. In the comments at least one person made basically the same argument that holding on to a potentially large % of memory is a surprising sharp edge, meanwhile shrinking the Vec and perhaps allocating is unsurprising behavior. Requiring many additional defensive shrink_to_fit calls to avoid this problem seems like the wrong tradeoff but I don't write enough Rust to hav…

The question is how much overhead would adding a check to determine to shrink if > % of freedom to all code that doesn’t need this optimization entail? The reason it’s important to consider is that I can always add a shrink_to_fit even if it’s a sharp edge. I can’t remove the conditional within the std library even if I know it doesn’t apply. And adding explicit APIs to control this nuance is a bit much (whether thro…

> The question is how much overhead would adding a check to determine to shrink if > % of freedom to all code that doesn’t need this optimization entail?

Once per collect? Damn near nothing.

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

#119
post #101
post #86

Earlier quoted context omitted.

It grows by 2x for small sizes, then it transitions to growing it by 1.25x

Indeed: https://go.googlesource.com/go/+/master/src/runtime/slice.go...

Interesting that it considers >= 256 entries to be 'not small'. That would be accurate for larger structs, not so much if the element is a bool, int, pointer, etc.

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

#120

Earlier quoted context omitted.

> I don't care? You're just forcing me to wordsmith the problem description Yes, because if you don't define the problem clearly, the problem won't be solved. Java being inefficient with memory use doesn't mean any memory was leaked. Memory leaks can be tricky to track down, and if I spent 6 hours looking for a memory leak only to come back and found out you meant it uses more memory than what's efficient I'd be piss…

"uses more memory than what's efficient" There is a hidden memory store using orders of magnitude more RAM than the live data. Why do we need to nitpick exactly how hidden it is? Are you going to be mad if I don't know whether it's literally inaccessible or not?

Because there are legitimate reasons why memory can be allocated. This is like calling your OS cache a memory leak when you open up Task Manager and see you only have 400MB free. A memory leak implies memory that is lost for good and it no longer being kept track of.

Consider it this way - if I had a program that connected to a database and used a connection pool to improve performance, would it be a "connection leak" that 5 connections were opened even though the database was idle?

The framing here is similar - Rust, in an attempt to improve performance reused large memory allocations. Some applications do this on purpose and call it buffer pools.

Post reply on HN