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…
A Tour of Safe Tracing GC Designs in Rust
31–40 of 61 posts
Re: A Tour of Safe Tracing GC Designs in Rust
#32Earlier quoted context omitted.
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…
Seems to me it is impossible to have both automatic precise release of a resources and collection-based GC?
As I understand it, even the documentation for IDisposable in .NET says as much at https://docs.microsoft.com/en-us/dotnet/api/system.idisposab...:
> The primary use of this interface is to release unmanaged resources. The garbage collector automatically releases the memory allocated to a managed object when that object is no longer used. However, it is not possible to predict when garbage collection will occur. Furthermore, the garbage collector has no knowledge of unmanaged resources such as window handles, or open files and streams.
> Use the Dispose method of this interface to explicitly release unmanaged resources in conjunction with the garbage collector. The consumer of an object can call this method when the object is no longer needed.
So this is the interface you can use to explicitly release a resource, because the GC gets around to it only later at some unspecified time.
About SafeHandle it says at https://docs.microsoft.com/en-us/dotnet/api/system.runtime.i...:
> The SafeHandle class provides critical finalization of handle resources, preventing handles from being reclaimed prematurely by garbage collection and from being recycled by Windows to reference unintended unmanaged objects.
Doesn't seem it's at all helpful for automatic precise release of resources.
Re: A Tour of Safe Tracing GC Designs in Rust
#33Re: A Tour of Safe Tracing GC Designs in Rust
#34Wouldn'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.
Yes, it makes total sense for the garbage collector to be tightly coupled to the memory allocation system, which is also tightly coupled to code generation (as is done in the case of various high-performance managed languages: Java, C#, Haskell, …). I remember hearing a talk a few years ago about a system of the kind you’re talking about - a kind of “managed language” micro-core that ran alongside LLVM so that it cou…
Re: A Tour of Safe Tracing GC Designs in Rust
#35Earlier 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…
Note that you don't need GC to reap these benefits, if desired. You can allocate an arena and do secondary allocations inside it, then deallocate everything in a single operation. Arena deallocation is not timely or precise, but it does happen deterministically.
Re: A Tour of Safe Tracing GC Designs in Rust
#36Earlier 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…
> 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 available for collection. Note that you don't need GC to reap these benefits, if desired. You can allocate an arena and do secondary allocations inside it, then deallocate everything in a single operation. Arena dealloc…
And there is of course the question of safety; should you release an arena too early, you may have introduced a bug. Worse: it might not crash immediately.
There is actually some work for doing arena management automatically, called region inference: http://www.mlton.org/Regions
But the way I see it, it's just a way to make memory management even more efficient; it's not about precise release of resources, and indeed not all programs can be expressed so that releases can happen only in batches of an arena (assuming those arenas themselves aren't dynamically managed, which certainly is a valid strategy as well, but manual).
Re: A Tour of Safe Tracing GC Designs in Rust
#37Earlier quoted context omitted.
> 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 available for collection. Note that you don't need GC to reap these benefits, if desired. You can allocate an arena and do secondary allocations inside it, then deallocate everything in a single operation. Arena dealloc…
True, but GC gives those benefits automatically, compared to a naive program doing e.g. RC-based memory management. And there is of course the question of safety; should you release an arena too early, you may have introduced a bug. Worse: it might not crash immediately. There is actually some work for doing arena management automatically, called region inference: http://www.mlton.org/Regions But the way I see it, it…
A memory safe programmming language will detect any such bugs and reject the program. This is not hard, it's a clean application of existing lifetime checks.
Re: A Tour of Safe Tracing GC Designs in Rust
#38Earlier quoted context omitted.
Of course competent programmers using the top K bits of the pointer would make sure that the kernel and memory allocator operate so that they don't allocate memory outside a (64 - K)-bit address space, regardless of what the CPU supports (e.g. Linux mmap as one would expect won't allocate beyond 2^48 unless explicitly requested). And obviously any intelligent CPU architect, if they make a CPU that masks the upper bit…
> Of course competent programmers ... And obviously any intelligent CPU architect Interesting to see the future is so "obvious" to you. You are correct however, that this is not a problem at the present because no architecture uses more then 48-bits of address space in practice. And since this is such a common practice now, there will have to allowances made in the future for when the address space is expanded.
That being said, I don't know why the increase from 256TB to 128PB is at all useful but it's enabled in my Arch Linux kernel on a 32GB laptop, so clearly adopted by the broader ecosystem even outside whatever niche use-case it's for.
That being said, the kernel maintains backward compatability by restricting the allocation address space it hands out by default. Userspace needs to explicitly ask for the higher bits [2], so it's probably OK to just continue as-is assuming the 48-bit limit in practice.
[1] https://en.wikipedia.org/wiki/Intel_5-level_paging
[2] https://www.kernel.org/doc/html/latest/x86/x86_64/5level-pag....
Re: A Tour of Safe Tracing GC Designs in Rust
#39Earlier quoted context omitted.
True, but GC gives those benefits automatically, compared to a naive program doing e.g. RC-based memory management. And there is of course the question of safety; should you release an arena too early, you may have introduced a bug. Worse: it might not crash immediately. There is actually some work for doing arena management automatically, called region inference: http://www.mlton.org/Regions But the way I see it, it…
> should you release an arena too early, you may have introduced a bug. A memory safe programmming language will detect any such bugs and reject the program. This is not hard, it's a clean application of existing lifetime checks.
Re: A Tour of Safe Tracing GC Designs in Rust
#40Earlier quoted context omitted.
Until the virtual address space gets so big that there are no bits left at the top that are "free". As soon as you have a 64 bit virtual address space, pointer tagging the top bits no longer works. Your program is now broken on the new CPU.
Of course competent programmers using the top K bits of the pointer would make sure that the kernel and memory allocator operate so that they don't allocate memory outside a (64 - K)-bit address space, regardless of what the CPU supports (e.g. Linux mmap as one would expect won't allocate beyond 2^48 unless explicitly requested). And obviously any intelligent CPU architect, if they make a CPU that masks the upper bit…
x86-64 does this by requiring all accesses to be correctly sign-extended, which means that valid virtual addresses are unique and there is full forward compatibility even with an extended virtual address space. The "number of bits that get masked" is a pure system/ABI concern, the CPU support only defines a maximum.