FractalFir / rustc_codegen_clr

This rust compiler backend(module) emmits valid CIL (.NET IR), enabling you to use Rust in .NET projects.
MIT License
1.39k stars 30 forks source link

Use of unmanaged memory. #20

Closed nikii-me closed 10 months ago

nikii-me commented 10 months ago

Hello, I am not sure as to where to ask about this, so I'm opening an issue as there isn't a discussion tab on this repo. What is the philosophy behind the usage of unmanaged memory in place of managed? Correct me if I'm wrong, but from what I know this brings a few issues onto the table, the biggest being performance hits (allocation is slower in unmanaged memory than managed), as well as interop with other .NET languages. Is there a reason why managed memory isn't used instead?

FractalFir commented 10 months ago

It is, technically speaking, true that allocating GC memory is cheaper than allocating non GC one. This picture changes diametrically once we take the cost of freeing that memory. GC is relatively costly, when compared to freeing the memory in traditional ways.

Rust's borrow checker can be, roughly speaking, described as a compile-time GC. At the cost of being less flexible and taking longer to compile, Rust is able to automatically manage memory without GC. This tends to be significantly faster than a runtime GC.

Rust also has very strict rules on the order memory is being freed in. This can't be mimicked with finalizers.

The project does provide ways of interacting with managed memory (by using the interop layer mycorrhiza), although they are separate from unmanaged versions. One of the principles of Rust is to be explicit about how allocations happen, so managed allocations are easy to spot.

I have also enabled discussions.

nikii-me commented 10 months ago

Is the issue of freeing memory important in the context of the GC however? While it is true that it's more expensive than in unmanaged memory, I'm not sure if that exactly matters, given the GC does its own thing memory-wise. Unless its an issue impossible to adapt to Rust's systems, I don't think freeing memory would be that important

FractalFir commented 10 months ago

The borrow checker is a big selling point of Rust, and it is significantly faster than GC. See this post about discord switching from Go to Rust. Admittedly, the Go GC and .NET implementation differ, but this can provide a rough estimate of performance benefits.

I plan for this project to fit the niche of writing memory intensive .NET code. Rust not only would reduce the frequency of GC passes, it would also speed up each GC pass. During GC, a lot of memory needs to be scanned and moved. By storing most data in unmanaged memory, we avoid those moves.

Rust memory model is also not very compatible with the .NET one. Rust can have references to references, which is something that .NET forbids.

This is valid Rust code:

struct Inner{
   a:i32,
}
struct Outer{
    inner:Inner,
    b:f32,
}
struct RefInner{
   ref:&Inner,
}
fn test(){
  let outer = Outer::default();
  // Taking a reference to a field - OK in .NET and Rust
  let ref = &outer.inner;
  // Storing a reference to a field in a struct - valid Rust, invalid .NET!
  let ref_inner = RefInner{ref:ref};
  // Geting a reference to a reference - valid Rust, invalid .NET!
  let ref_inner = &ref;
}
nikii-me commented 10 months ago

Fair enough, got nothing else to add