Live data from Hacker News

A Tour of Safe Tracing GC Designs in Rust

manishearth.github.io

11–20 of 61 posts

Re: A Tour of Safe Tracing GC Designs in Rust

#11

One day, Rust needs a GC. Reference counting is a just crappy GC. Modern GC can perform better than this so Rust is actually hurting its own performance by not having one. A good GC would make heavily concurrent apps much easier to build with Rust. And would have better performance than the typical Arc Mutex objects passed around right now

Tracing GC is troublesome for any non-memory resource, such as network connection or file handle, due to its untimely release, but otherwise I actually agree: reference counting is a GC mechanism—not a very good one, but it's the only one I'm aware of that works both for memory and resources.

I would enjoy someone test a model where the type system guarantees (or at least lets you detect the situation) that you cannot store such non-memory objects behind a traced GC node (these would include plain memory objects that need to be registered/unregistered precisely).

It might be that it would be needlessly annoying to use just compared to just RC. Or maybe it would be best of both worlds?

Re: A Tour of Safe Tracing GC Designs in Rust

#12

One day, Rust needs a GC. Reference counting is a just crappy GC. Modern GC can perform better than this so Rust is actually hurting its own performance by not having one. A good GC would make heavily concurrent apps much easier to build with Rust. And would have better performance than the typical Arc Mutex objects passed around right now

Tracing GC has poor memory performance because it has to access rarely used or swapped out pages to scan them for pointers. And of course, the peak memory use is much higher since it doesn't free everything as soon as possible.

There may be advantages if you can use it to add compaction, but I don't think you need a GC to do that necessarily.

Re: A Tour of Safe Tracing GC Designs in Rust

#13
post #9

>In Go, the compiler needs to automatically insert code that checks the “pulse” of the heap every now and then, and potentially runs garbage collection. It also needs to automatically insert code that can tell the scheduler “hey now is a safe time to interrupt me if a different goroutine wishes to run” Is it still the case after Go 1.14 (February 2020) when asynchronous preemption was implemented? As far as I know, g…

The signal based mechanism is only used for stragglers.

Go still uses the function call check. The runtime devs found a way to combine it with a check to see if the goroutine's stack needs to be resized, so it doesn't actually cost anything.

Re: A Tour of Safe Tracing GC Designs in Rust

#14

One day, Rust needs a GC. Reference counting is a just crappy GC. Modern GC can perform better than this so Rust is actually hurting its own performance by not having one. A good GC would make heavily concurrent apps much easier to build with Rust. And would have better performance than the typical Arc Mutex objects passed around right now

I would need some data on that but I have to say that it always makes me laugh when people only take about the GC in threads about D. It's so good for productivity. I don't really like it but I can't describe just how much of a non-issue it is for us (The company I work for)

Re: A Tour of Safe Tracing GC Designs in Rust

#15
post #6
post #4

Wouldn't it make more sense to have garbage collectors at the level of LLVM? That way, you could have direct access to all the pointers in a program without jumping through the language's hoops. And other languages could benefit from it too.

The concept of what a pointer "is" is somewhat language-dependent, particularly as people finally attempt to mitigate the waste of moving to 64-bit by using tagged pointers (which maybe could try to be modelled in LLVM also, but I think you will rapidly realize that LLVM just isn't modeling a lot of things that affect scanning).

> particularly as people finally attempt to mitigate the waste of moving to 64-bit by using tagged pointers

It's very fraught to try to "mitigate the waste" at the top of the pointer. Everybody involved is clear that what you're doing there is saving up pain for yourself because periodically more of those "wasted" bits become significant and each time that happens if you've been using them now you've incurred a maintenance burden.

The waste at the bottom of the pointer is a far more sensible place to hide information because it won't get taken away when somebody ships a new CPU, it was already there in 32-bit architectures.

LLVM itself makes use of the latter trick extensively as I understand it, but doesn't use the former. If they've got a 64-byte structure, they can align them, then steal six bits at the bottom of each pointer to those structures for bit flags, and when Intel ships a new CPU their code still works. Whereas if I steal ten bits at the top of the pointer, when Intel ships Ice Lake CPUs my compiler now crashes. Good luck debugging that!

Re: A Tour of Safe Tracing GC Designs in Rust

#16
post #11

One day, Rust needs a GC. Reference counting is a just crappy GC. Modern GC can perform better than this so Rust is actually hurting its own performance by not having one. A good GC would make heavily concurrent apps much easier to build with Rust. And would have better performance than the typical Arc Mutex objects passed around right now

Tracing GC is troublesome for any non-memory resource, such as network connection or file handle, due to its untimely release, but otherwise I actually agree: reference counting is a GC mechanism—not a very good one, but it's the only one I'm aware of that works both for memory and resources. I would enjoy someone test a model where the type system guarantees (or at least lets you detect the situation) that you canno…

Not when the language also supports value types and region allocators (e.g. IDispose in .NET).

You can even turn it into RAII proper, by turning into a compilation error not handling those interfaces properly.

Again with .NET, there are SafeHandles as well, alongside the MarshalInterop APIs.

This is nothing new actually, Mesa/Cedar for Xerox PARC used reference counting with a cycle collector, while other descendent languages down to Modula-3 and Active Oberon always combined value types, tracing GC and C++ like resource handling capabilities.

Oh Common Lisp also has similar capabilities, specially the ZetaLisp predecessor from Lisp Machines.

Then Eiffel not only had this, it was also probably the first Algol like language to support non nullable references.

Sadly they decided to ignore all of this in Java, and then its world domination kind of made everyone else ignore it as well.

Thankfully even Java is improving their story in this regard, while languages like D, Nim and yes .NET kind of show what was already available for several decades.

Re: A Tour of Safe Tracing GC Designs in Rust

#17
post #6

Earlier quoted context omitted.

The concept of what a pointer "is" is somewhat language-dependent, particularly as people finally attempt to mitigate the waste of moving to 64-bit by using tagged pointers (which maybe could try to be modelled in LLVM also, but I think you will rapidly realize that LLVM just isn't modeling a lot of things that affect scanning).

> particularly as people finally attempt to mitigate the waste of moving to 64-bit by using tagged pointers It's very fraught to try to "mitigate the waste" at the top of the pointer. Everybody involved is clear that what you're doing there is saving up pain for yourself because periodically more of those "wasted" bits become significant and each time that happens if you've been using them now you've incurred a maint…

> Whereas if I steal ten bits at the top of the pointer, when Intel ships Ice Lake CPUs my compiler now crashes. Good luck debugging that!

That's not how it works. The x86-64 ISA requires these "top" bits to be properly sign extended when actually accessing addresses, so any binary code that's using this "trick" is forward compatible wrt. future CPU's that might enable a bigger virtual address space. The only concern wrt. compatibility is purely ABI related but that can be managed at the OS and system-software layer.

Re: A Tour of Safe Tracing GC Designs in Rust

#18

One day, Rust needs a GC. Reference counting is a just crappy GC. Modern GC can perform better than this so Rust is actually hurting its own performance by not having one. A good GC would make heavily concurrent apps much easier to build with Rust. And would have better performance than the typical Arc Mutex objects passed around right now

Tracing GC has poor memory performance because it has to access rarely used or swapped out pages to scan them for pointers. And of course, the peak memory use is much higher since it doesn't free everything as soon as possible. There may be advantages if you can use it to add compaction, but I don't think you need a GC to do that necessarily.

Actually it is the other way around.

https://github.com/ixy-languages/ixy-languages

No wonder that M1 has specific architecture optimizations that help streamline ARC boilerplate code, while Swift 5.5 will bring more aggressive optimizations (disabled by default, because application can crash if weak/owned references are annotated improperly => WWDC 2021 talk)

Re: A Tour of Safe Tracing GC Designs in Rust

#19

The OP does not seem to mention CactusRef https://crates.io/crates/cactusref which provides deterministic deallocation (on par with simple refcounting) of reference cycles, with minimal tracing requirements and minimal memory overhead.

Probably because cactusref was only published 5 days ago and this article is from three months ago...

Re: A Tour of Safe Tracing GC Designs in Rust

#20
post #18

Earlier quoted context omitted.

Tracing GC has poor memory performance because it has to access rarely used or swapped out pages to scan them for pointers. And of course, the peak memory use is much higher since it doesn't free everything as soon as possible. There may be advantages if you can use it to add compaction, but I don't think you need a GC to do that necessarily.

Actually it is the other way around. https://github.com/ixy-languages/ixy-languages No wonder that M1 has specific architecture optimizations that help streamline ARC boilerplate code, while Swift 5.5 will bring more aggressive optimizations (disabled by default, because application can crash if weak/owned references are annotated improperly => WWDC 2021 talk)

This isn't representative of application code and there isn't even any mention of the metrics I mentioned…

> No wonder that M1 has specific architecture optimizations that help streamline ARC boilerplate code

No it doesn't. I told you it didn't the last time you said this.

Post reply on HN