Live data from Hacker News

A Tour of Safe Tracing GC Designs in Rust

manishearth.github.io

41–50 of 61 posts

Re: A Tour of Safe Tracing GC Designs in Rust

#41

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.

It requires unsafe.

Re: A Tour of Safe Tracing GC Designs in Rust

#42

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.

It requires unsafe.

It used to require unsafe. The project has been reworked to add safer alternatives (at the cost of some increased overhead).

Re: A Tour of Safe Tracing GC Designs in Rust

#43

Earlier quoted context omitted.

> 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…

> That's not how it works. The x86-64 ISA requires these "top" bits to be properly sign extended when actually accessing addresses. On Coffee Lake no more than 48 bits of the virtual address are "real". If I steal ten of the 64 bits from the top, all the actual address information is preserved and I've lost nothing. My compiler works just fine. On Ice Lake up to 57 bits of the virtual address are "real". Since I'm st…

Yes—and my concern is that old binaries will suddenly stop working on newer hardware. This has happened before!

The whole reason the top bits are sign-extended in the first place is to discourage these kinds of shenanigans. I remember the pain of switching 24-bit to 32-bit on M68K Macs.

Re: A Tour of Safe Tracing GC Designs in Rust

#44
post #3

Some history of the question of whether Rust should have GC as standard: 2014-09. RFC 256 https://rust-lang.github.io/rfcs/0256-remove-refcounting-gc-... « Remove the reference-counting based Gc type from the standard library and its associated support infrastructure from rustc. Doing so lays a cleaner foundation upon which to prototype a proper tracing GC » Contemporary with that: https://news.ycombinator.com/item?i…

It’s amazing how open and organic this evolved.

Re: A Tour of Safe Tracing GC Designs in Rust

#45
post #32
post #16

Earlier quoted context omitted.

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…

I must be missing something. How is it possible to precisely collect a resource with tracing GC? And if you need to update counters when you make duplicates of object references, you are not using a tracing GC where the benefits are the cheap duplication of object references, cheap allocations and cheap (batched) releases, but the downside is not being able to precisely and automatically do it when the value is avail…

You aren't reading it properly, the documentation you are reading is for the case you leave the work to the GC, you can take it yourself C++ RAII style:

   {
      using my_socket = new NetworkSocket()

   }

   // my_socket no longer exists when code arrives here

Or even better if NetworkSocket is a struct, it gets stack allocated, zero GC.

Re: A Tour of Safe Tracing GC Designs in Rust

#46
post #30

Earlier quoted context omitted.

> 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…

Isn't using bottom bit is how most runtimes represent their ints taking advantage of the fact that gc pointers are aligned? This is common practice afaiu.

Sure, but for the bottom bit, 64-bit addressing doesn't come into it. If you only want to steal bits at the bottom and have pointers with greater than single byte alignment this worked just fine on a 32-bit CPU already.

I was reacting to the claim that this mitigates the overhead (larger pointer size) of 64-bit addressing. You can steal bits at the top but it's probably a bad idea.

Re: A Tour of Safe Tracing GC Designs in Rust

#47
post #31

Earlier quoted context omitted.

> 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…

The longer I stare at your comment the deeper the feeling that you are hung up on a semantic issue feels. While the intermediate states I went through were maybe a bit more interestingly-explanatory with respect to specific techniques, the final state I landed in is simply: "if you only used the bottom single bit to tag that the entire value is a value instead of a pointer, that alone mitigates the waste of moving to…

The 32-bit platform can do this same trick, so, your 64-bit pointers are still wider than theirs, and you didn't gain anything unless you also steal high bits.

This isn't quite entirely true, for them address space is very tight and so they daren't just align everything and risk wasting too much address space, whereas on a 64-bit platform you can afford to be very generous with address space. This is akin to how it's fine to have randomised privacy addressing in IPv6, but that'd be a problem in IPv4. But this gain is hard to give a specific value to, whereas the simple fact is that 64-bit pointers are exactly twice as big as 32-bit pointers.

This can give you the incentive to build a different data structure, it's surprising how often "Just use a vector" is correct these days, Chandler Carruth has talked about that at length (in C++). If you have at least some idea how many Foozles you need, and Foozles aren't some crazy multi-page data structure, chances are you should put all the Foozles in a vector, and not carry around actual pointers to Foozles at all.

Re: A Tour of Safe Tracing GC Designs in Rust

#49
post #45
post #32

Earlier quoted context omitted.

I must be missing something. How is it possible to precisely collect a resource with tracing GC? And if you need to update counters when you make duplicates of object references, you are not using a tracing GC where the benefits are the cheap duplication of object references, cheap allocations and cheap (batched) releases, but the downside is not being able to precisely and automatically do it when the value is avail…

You aren't reading it properly, the documentation you are reading is for the case you leave the work to the GC, you can take it yourself C++ RAII style: { using my_socket = new NetworkSocket() } // my_socket no longer exists when code arrives here Or even better if NetworkSocket is a struct, it gets stack allocated, zero GC.

    NetworkComponent foo = new NetworkComponent();

    {
       using my_socket = new NetworkSocket();
       foo.socket = my_socket;
    }

    foo.do_sth_with_socket(); // oops, runtime failure, socket closed

Re: A Tour of Safe Tracing GC Designs in Rust

#50
post #45

Earlier quoted context omitted.

You aren't reading it properly, the documentation you are reading is for the case you leave the work to the GC, you can take it yourself C++ RAII style: { using my_socket = new NetworkSocket() } // my_socket no longer exists when code arrives here Or even better if NetworkSocket is a struct, it gets stack allocated, zero GC.

NetworkComponent foo = new NetworkComponent(); { using my_socket = new NetworkSocket(); foo.socket = my_socket; } foo.do_sth_with_socket(); // oops, runtime failure, socket closed

[deleted]
Post reply on HN