Why does this Rust program leak memory?

Поділитися
Вставка
  • Опубліковано 26 жов 2024

КОМЕНТАРІ • 149

  • @khoda81
    @khoda81 Рік тому +251

    Knowing about fragmentation is one thing, but seeing it happen in practice is so useful! Thanks a lot

    • @fasterthanlime
      @fasterthanlime  Рік тому +51

      That's why I was so excited when it landed in my inbox! I often struggle to find good real-world examples of concepts and this was a gift ☺️

    • @monad_tcp
      @monad_tcp Рік тому +3

      I'm impressed programming language runtimes are still letting the OS manage the buckets of memory allocation instead of always over-commiting pages and doing the correct thread-off that saves kernel invocations for malloc buckets.

    • @blinking_dodo
      @blinking_dodo Рік тому

      @@fasterthanlime would this fragmentation be exploitable?

    • @94Quang
      @94Quang Рік тому

      @@blinking_dodoyes, if there would be any kind of remote code execution exploitation and my goal would be to slow the system down, this would be a funny thing to do :)

  • @faeancestor
    @faeancestor Рік тому +47

    your content always plants a massive smile on my face ; you have such a kind soul ; thank you for this content !!!!!
    !!!!!!!!

  • @Jplaysterraria
    @Jplaysterraria Рік тому +99

    I've had to implement malloc/free for a Uni course, all the ways you can do things is very interesting!
    It is also interesting to debug... I was using rust at first, but even `printf` allocates memory, which is great if your `malloc` is crashing :)

    • @edgeeffect
      @edgeeffect Рік тому +2

      Sounds like a Jacob Sorber video. ;)

    • @emmavdev
      @emmavdev Рік тому

      Thats unfortunate yea

    • @shambhav9534
      @shambhav9534 Рік тому

      Well, you could've easily worked around using Rust's printf.

    • @Jplaysterraria
      @Jplaysterraria Рік тому +12

      @@shambhav9534 C's printf has the same problem, you have to use fprintf(stderr, msg).
      The problem isn't that you can work around it, is that you have a crash, so you use printf to see where it crashes, but it seems to crash before any printf (because allocating memory is what crashes the system)

    • @ccgarciab
      @ccgarciab Рік тому +14

      "I have no tools because I've destroyed my tools with my tools"
      - James Mickens, The Night Watch (recommended)

  • @abdelhakimakodadi3073
    @abdelhakimakodadi3073 Рік тому +20

    (5:44) Je malloce, tu mallocs, vous m'allocez lol

  • @jarrednicholls
    @jarrednicholls Рік тому +27

    An allocator backed by a memory arena/slab (if one knows the amount of memory they will need) is a good “have your cake and eat it too” solution to avoid fragmentation and maintain low latency.
    Your deep dive here was very very well done! Super valuable and approachable by all experience levels. Keep up the great work!

  • @p0lyglot
    @p0lyglot Рік тому +29

    See this is why we build compacting garbage collectors ;) It's not just for the improvement in cache locality...

    • @fasterthanlime
      @fasterthanlime  Рік тому +20

      Someone in chat rightfully pointed out we probably could've gotten out of this one with an arena, but I agree compacting GCs are the generic solution to this.

    • @mxbc_ebk5086
      @mxbc_ebk5086 Рік тому +8

      Then your GC leaks even more memory 🤡

    • @pfeilspitze
      @pfeilspitze Рік тому +10

      Well, GCs don't solve memory leaks either. And whether they actually provide *useful* cache locality is kinda a crapshoot.
      GCs are great sometimes, but they're not magical either. They solve use-after-free, but memory-use-after-free is arguably the easiest thing to solve. (After all, I need features to not use-after-close my file descriptors and use-after-FIN my socket handles, and use-after-unlock my mutexed data and ...)

    • @vanjazed7021
      @vanjazed7021 Рік тому +1

      @@pfeilspitze Moving garbage collectors specifically fix fragmentation too.

  • @khuiification
    @khuiification Рік тому +6

    I fucking love your videos. Especially rust ones. Amazing! I feel like i've always had a hunch about this as my mental model, but seeing it concretely explained is so good.

  • @MarcusBrito
    @MarcusBrito Рік тому +5

    This was great; definitely something that will stay with me, and remember when designing solutions.

  • @hikson7
    @hikson7 Рік тому +1

    Great content! Loved seeing a light deep-dive into kernel at work (+bonus with Rust!) with satisfying explanation

  • @zactron1997
    @zactron1997 Рік тому +31

    Another great example of why I love Rust: the control to choose whether to care about problems like this or not. For the type of work I do, I'll have RAM to spare, so I can afford to be a little more implicit with my management. But for some stuff, I can also go the complete opposite direction and maximize every last byte of memory.

    • @nickwilson3499
      @nickwilson3499 Рік тому +6

      yeah the choice of leaking memory is always handy lmao

    • @zactron1997
      @zactron1997 Рік тому +17

      @@nickwilson3499 as the video explains tho, it's not a memory leak, it's a worst-case fragmentation.in a more practical program, all those gaps in memory would be filled with smaller data structures anyway.

    • @0LoneTech
      @0LoneTech Рік тому

      You mean like the Allocator template argument in C++ std::unordered_map? Looks kind of like Rust only does it at a global level. The example here also shows it making thousands of little allocations when populating a hashmap of known size, which would have been possible to preallocate unboxed. I'm also not seeing the strategic options of e.g. Gregory Collins' hashtables package. This is the sort of tuning I'd expect to see in Chapel, but its associative maps appear fairly limited too.

  • @dexterman6361
    @dexterman6361 Рік тому +1

    Thank you. This was a really detailed, and highly interesting walkthrough. I really would like to thank you for sharing this.
    Saved to my library
    you have a good day smart kind human

  • @HyperFocusMarshmallow
    @HyperFocusMarshmallow Рік тому +3

    This was very interesting. I followed it roughly but I probably wouldn’t ace test questions about this from just watching the video.
    It’s all about trade offs. The thing is, it quickly becomes more complicated than what I would want to reason about when writing most programs.
    Since this is on top of libc-malloc all these issues would be there in c as well. But there are so many levels of indirection. Operating system, allocator, maybe garbage collector and then language abstractions.
    The levels of indirection most often saves you from having to deal with really stupid stuff like implementing your own paging in your little app that has a bunch of data in it.
    Sometimes it seems like you want to talk directly to the hardware but the second you want to run anything on multiple different systems all the levels of abstraction softens the rough edges.
    It’s just not clear at what point you would want to make choices about these kinds of issues. And it’s very hard to make general automatic defaults.
    I guess the reasoning should be, the defaults are very good and if you really run into trouble, profile and pick the lowest hanging fruit.

  • @Erhannis
    @Erhannis Рік тому +30

    Ahh, ok - so not a leak like, "memory is mysteriously disappearing and only the OS knows where", but rather, "memory inefficiently allocated". I was a little worried going into this that the promises I'd been told about Rust's memory safety would turn out to be disappointing lies, haha. Glad that's not the problem.

    • @fasterthanlime
      @fasterthanlime  Рік тому +15

      Leaking memory wouldn't be unsafe though! There's even a method in the standard library for that: Box::leak

    • @Erhannis
      @Erhannis Рік тому +6

      @@fasterthanlime I mean, if your apparently-correctly-written program, in normal usage, gradually grows in memory size until it (or the system) is forced to quit, that sounds fairly unsafe to me. Not as bad as leaking sensitive info or corrupting data, granted.

    • @jcdyer3
      @jcdyer3 Рік тому +6

      @@Erhannis Rust's memory safety promises makes no guarantee that that won't happen.

    • @Erhannis
      @Erhannis Рік тому

      @@jcdyer3 How so? My understanding was that things have one owner at any given time, and once execution leaves the scope containing the object, or the owner is collected, the thing is collected, too. Without invoking explicitly unsafe behavior, how would you permanently leak memory?

    • @aedieal
      @aedieal Рік тому +3

      @@Erhannis You never permanently leak memory, as it would be reclaimed at program exit, but you can lose track of your objects and keep allocating more without letting the old ones go our of scope

  • @desuburinga
    @desuburinga Рік тому

    Really appreciate these technical deep dives! Thanks!

  • @Erhannis
    @Erhannis Рік тому +6

    "I didn't hide my email address well enough, and received a fascinating puzzle." ...Good end???

  • @miketube208
    @miketube208 Рік тому +5

    pmap -X gives nicer output than cat /proc//maps :)

  • @AlwaysStaringSkyward
    @AlwaysStaringSkyward Рік тому

    I learned TONS from this. Thank you!!!

  • @shashanksharma21
    @shashanksharma21 Рік тому

    this was enlightening! thank you!

  • @altairbueno5637
    @altairbueno5637 Рік тому +4

    Arena allocators such as Bumpalo in rust mitigate this issue

    • @fasterthanlime
      @fasterthanlime  Рік тому +10

      Please no spoilers, I'm still working on that one

  • @Dygear
    @Dygear Рік тому

    Excellent work as always Amos!

  • @mikhailmikheev8043
    @mikhailmikheev8043 Рік тому

    Thank you very much for the vid!

  • @Verrisin
    @Verrisin Рік тому +1

    well ... all I can say it: Another reason I'm glad I can use compacting GC at work, instead worrying about stuff like this. It's INTERSTING, yes. But I don't have time for puzzles, when a client expects results...

  • @TheBrazilRules
    @TheBrazilRules Рік тому +1

    "It does not use very much memory at all" "1GB"
    Me:"WTF"?

  • @getoutofhereinternet
    @getoutofhereinternet Рік тому +1

    A truly herculean effort

  • @oyewodayo
    @oyewodayo Рік тому

    Write a book. Don't waste time on this. You're a good teacher with good mastery.

  • @flippert0
    @flippert0 9 місяців тому

    Should be worthwhile to note that Rust's memory safety guarantees are about dangling pointers, double free and such, but not about memory leakage. Structures with mutual pointers between instances cannot be freed easily, must use "Weak" references.

  • @AJMansfield1
    @AJMansfield1 Рік тому

    How would this fare using the MESH allocator? That one that was going around a few years back that used some virtual memory tricks to let it merge together partially-empty memory pages if each page's holes overlapped with the other's data.

    • @fasterthanlime
      @fasterthanlime  Рік тому

      I'm planning on doing a video about arenas & the MESH allocator :) Excited for that.

  • @Bvngee
    @Bvngee Рік тому

    This was fascinating!!!

  • @9SMTM6
    @9SMTM6 Рік тому +1

    Yessss, gonna feel nice for guessing fragmentation, even if I had no idea why and had to watch the explanation parts a few times:).
    Regarding what to do about it, I'm not certain that's generally applicable - tho it seems so - , but in this case you had "interlacing lifetimes" of memory allocations as root cause, no? Method 2 doesn't interlace them and because of that that free memory isn't used.
    Another solution should be separate allocators for the separate uses of course.

  • @sysop073
    @sysop073 Рік тому +1

    I thought Rust had the ability to rearrange memory, and that's what Pin prevented. Have I totally misunderstood the purpose of pinning?

    • @fasterthanlime
      @fasterthanlime  Рік тому +2

      Rust doesn't have a compacting GC that will move memory by itself. However, Rust code can move values, which would break async code. Async functions are self-referential state machines, and Pin makes it impossible to move them.

    • @Verrisin
      @Verrisin Рік тому

      @@fasterthanlime I'm still confused what is "move values" if it doesn't rewrite pointers into it... or also, disallow moving values if there exist references into it, Pin being kind of implicit by that?

  • @arrux4822
    @arrux4822 Рік тому

    Awesome vid!

  • @felipellrocha
    @felipellrocha Рік тому

    Is the example code available somewhere? I would love to dive into it

  • @shershahdrimighdelih
    @shershahdrimighdelih Рік тому +1

    loved it

  • @monad_tcp
    @monad_tcp Рік тому +2

    26:04 paying for drops to the operating system ? that's insane, we don't pay that with garbage collectors, there's a thread to do that so our current thread will run smoothly

    • @tesfabpel
      @tesfabpel Рік тому

      It won't make a syscall every time a value is dropped... glibc's malloc and free only do so when the memory they have at disposal isn't enough, otherwise it's just simple bookkeeping... Also, having destructors run in a separate thread is a very BAD idea when having native resources... I had to fight C#'s GC because it didn't have to run the finalizer of a OpenGL-related object in a different thread than the one it was created.

    • @monad_tcp
      @monad_tcp Рік тому

      @@tesfabpel oh yes "simple" book keeping.
      It's not that simple either way.
      It's usually a sparse linked list or some red/black tree. I'm some cases it's much more complex than that with buckets and lots of things because they try really hard to avoid fragmentation.
      Meanwhile GCs take a much larger heap block from the OS and manage it themselves. And they can (usually) just do heap compaction and hardly suffer from fragmentation.
      They also pay much less in syscalls.
      As committing pages is basically free, there's no reason why you wouldn't prealloc a huge amount of heap, unless you're worried about fragmentation of the heap of the process.
      The real difference between GC and alloc/dealloc inline is where you pay the cost, on allocating or when deallocating.
      With manual memory management you pay nothing when deallocating, but allocation isn't cheap or even that predictable.
      GCs are ridiculously fast on allocation, it's not even a joke. It's as cheap as a barrier and a pointer increment.
      And they have a good side effect, no memory crashes.
      I find it funny that people think GCs made things slow when it fact it's always double dispatching and excessive use of objects. (which causes allocations that have to be paid either way).
      But GCs make it very evident the cost as they have their thread spinning and doing things. Meanwhile in C++ you happily pay constructor calls and never notice. But deallocating is "free", pun intended, just let it leak...

    • @monad_tcp
      @monad_tcp Рік тому

      @@tesfabpel also what you were doing to C#, I never had that problem of having to free things in the same thread.
      Well, don't use destructors for that, they're meant to managed objects, not unmanaged system resources.
      The fact that you can use memory management to manage other kinds of resources is a impedence mismatch on programming languages.
      GCs don't replace RAII on system resources, ironically. You just have to implement Disposing(false) properly and sprinkle "using (opengl) {}" everywhere.
      And I think that the fact C++ uses RAII for everything is a mistake that complicates the runtime.
      C# IDisposable isn't great, it's one of the few things the Java/JVM has better.
      But I bet they didn't had to deal with COM compatibility, so there's a reason for the IDisposable design.
      Or even simpler, use a Object Pool class and reference count objects, like the COM does.
      You're not required to use the GC for everything. Sometimes doing things manually is fine. Just do a proper ".Dispose()" and suppress collection. Do it from the thread you want to dispose, don't fight the GC. It's simple as that.
      You probably were using the IDisposable wrong. I think you have to use a mashall reference and run your Thread render in the STA apartment if you really want the GC to fire destructors there.
      What you're doing is highly unusual on that environment. That's not a fault of the GC but of OpenGL being stupid. (as usual)

  • @fntr
    @fntr Рік тому

    what vscode extensions are you using?

  • @andrewdunbar828
    @andrewdunbar828 Рік тому

    I wonder if Alice knows about this co-Alicing.

  • @bsm117532
    @bsm117532 Рік тому

    Seems to me there should be a way to do this this uses 3 orders of magnitude less memory. Why or why not?

  • @severinheugabler
    @severinheugabler Рік тому

    What is the name of that font in VSCode?

  • @Otakutaru
    @Otakutaru Рік тому +10

    What I take away from all this: "Phew!... Thought for a moment it was Rust's fault". Also, don't litter, keep it solid

  • @returnzero7492
    @returnzero7492 Рік тому

    What font do you use? ;)

  • @Zooiest
    @Zooiest Рік тому

    How differently would it behave on Windows?

    • @fasterthanlime
      @fasterthanlime  Рік тому

      Hard to tell since it has s completely different allocator, but the basic idea is the same. Wouldn't be too hard to find out, if you're curious!

  • @nathanoy_
    @nathanoy_ Рік тому

    what VSCode theme are you using?

    • @fasterthanlime
      @fasterthanlime  Рік тому

      Usually GitHub light / GitHub dark

    • @nathanoy_
      @nathanoy_ Рік тому

      @@fasterthanlime thank you for the reply!

  • @kawsxj681
    @kawsxj681 Рік тому

    is that a custom font for your vsc?

  • @tacticalassaultanteater9678

    It's like glibc assumes a variable page size OS

  • @baxiry.
    @baxiry. Рік тому +1

    I have used a lot of C and C++ applications. I haven't had a memory leak.
    I used two Rust apps. One of them was leaking memory

  • @Morimea
    @Morimea Рік тому +1

    Cool low level stuff.
    Lack of actually useful tools - everyone on "big" tasks playing with ML/AI/Clouds...
    And there is just no reward for making small "tools" that actually doing something useful now.

  • @porky1118
    @porky1118 Рік тому

    Are there really people who find inlay hints useful and not confusing?

  • @Whatthetrash
    @Whatthetrash Рік тому

    As someone who's now learning Rust, a title like this is NOT encouraging. >_< LOL I thought Rust was supposed to save you from yourself in regards to memory safety.

    • @tomtravis858
      @tomtravis858 Рік тому

      Memory leaks are actually memory safe.

  • @error200http
    @error200http Рік тому

    dhat-heap is a very good memory profiler.

  • @punpck
    @punpck Рік тому

    glad it's clickbait and not a real memory leak^^ if it was a real memory leak, memory still would be gone after clear or reset^^ so it doesn't leak memory but how 3rd method was implemented is not good. Nonetheless, great video 👍

  • @actuallyasriel
    @actuallyasriel Рік тому

    Wait, wouldn't the plural of Linux be "Linuces?"

  • @srgi
    @srgi Рік тому

    Why the heck indeed

  • @cheaterman49
    @cheaterman49 Рік тому +2

    Tu malloques ! xD

    • @fasterthanlime
      @fasterthanlime  Рік тому +3

      Avec l'orthographe correcte et tout, c'est nickel

  • @stevenhe3462
    @stevenhe3462 Рік тому

    The point is not to allocate those little vecs.

  • @begga9682
    @begga9682 Рік тому

    Double it and give it to the next person

  • @1111757
    @1111757 Рік тому

    I'd really like to see you on @Computerphile! I think it would fit perfectly.

  • @phenanrithe
    @phenanrithe Рік тому

    To be frank, it's a bit confusing to watch. What's the trim code, what's the reset code? I see short glimpses of code that jump from left to two columns, then back to left, then split to show the graph... gah! Then it goes straight to the measurements without knowing what any of those actually does... that's where you lost me. 😅

    • @fasterthanlime
      @fasterthanlime  Рік тому +1

      The description has a link to the repository! That might help :)

  • @monad_tcp
    @monad_tcp Рік тому

    30:42 garbage collectors and memory compression !
    check mate

    • @fasterthanlime
      @fasterthanlime  Рік тому

      This is literally the next thing mentioned in the video

  • @isaac_shelton
    @isaac_shelton Рік тому

    ...so it doesn't leak memory at all, it just allocates inefficiently. Misleading title

    • @fasterthanlime
      @fasterthanlime  Рік тому

      The title is the question I was asked - the video elucidates it. If every commenter being salty about titles whilst still learning something useful in the video spent that energy elsewhere, we would have solved the climate crisis already.

  • @IanMaayrkas
    @IanMaayrkas 11 місяців тому

    Ohno, it's MADV_DONTNEED :O
    ua-cam.com/video/bg6-LVCHmGM/v-deo.html#t=58m23s

  • @EzequielRegaldo
    @EzequielRegaldo Рік тому +2

    So we can go back to C++ ? Feels weird overcomplicated lang

    • @fasterthanlime
      @fasterthanlime  Рік тому +11

      The video emphasizes at the end that this is something common to all memory allocators - only moving/compacting garbage collectors solve that problem generally.

    • @shambhav9534
      @shambhav9534 Рік тому +5

      Sadly, you'd have the same problem in C++ too.

    • @EzequielRegaldo
      @EzequielRegaldo Рік тому +1

      @@shambhav9534 thats why i preffer stay with C++, i dont see a real benefit changing

    • @error200http
      @error200http Рік тому

      @@EzequielRegaldo It might be harder to debug in C++ since you'd have to write 10x more lines on code to get a grasp on what's going on.
      In Rust you just use dhat heap profiler. In C++ you frequently need to create tools yourself (from my experience)

    • @shambhav9534
      @shambhav9534 Рік тому +4

      @@EzequielRegaldo For every problem that Rust doesn't solve, it solves a thousand others.

  • @gaurangshukla8235
    @gaurangshukla8235 Рік тому

    Isn't this just a mediocre programmer writing stupid code? Do people really think you can completely ignore how memory works and still write efficient software?

    • @fasterthanlime
      @fasterthanlime  Рік тому +6

      It's not - the example was golfed down from a real-world codebase to something small enough to study in isolation. A lot of people were stumped by exactly what was going on. Calling people mediocre and their code stupid doesn't make you look cool and isn't welcome on this comment section.

    • @gaurangshukla8235
      @gaurangshukla8235 Рік тому +2

      ​@@fasterthanlime If the code was not critical in the first place then why are we talking about it? If it was indeed critical then whoever wrote this didn't know what they were doing. Also just because a code excerpt comes from a real-world codebase doesn't magically make it perfect. The code was a textbook example of memory fragmentation, it really is dumb.
      EDIT: After reading the code myself, I think mediocre was an understatement. It was super obvious from reading the code itself. Even the real-world code was awful. They were collecting an entire database to create the inverse_map, which is obviously a memory fragmentation issue. A commit fixes it *accidentally*, by trying to save memory by streaming instead of collecting the entire db.

  • @stintaa
    @stintaa 9 місяців тому

    What font do you use?