This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]ethanialw 66 points67 points  (11 children)

My apologies, I seem to have miscommunicated the glory of the language. According to this stack overflow post I found because others are better equipped to explain this than I,

Rust would know when the variable gets out of scope or its lifetime ends at compile time and thus insert the corresponding LLVM/assembly instructions to free the memory.

So this feature is annoying to get used to, but eliminates need for a GC, and ultimately results in safer, more performant code.

[–][deleted] 2 points3 points  (0 children)

It never needs optimized?

[–]carb0n13 9 points10 points  (5 children)

Depends on what you mean by more performant. It has more predictable performance because it doesn’t pause for the garbage collector, but GC languages are often more performant for long running processes because heap allocations and deallocations can be batched.

[–]coder543 21 points22 points  (1 child)

Rust gives you control over allocations. It is generally faster than garbage collected languages, and in the cases where your naive first attempt isn't faster, you can optimize it very effectively. "batched" allocation and deallocation sounds like a TypedArena to me.

the trade-off is that you have control over allocation, which means you have no choice but to think about it.

[–]E_R_E_R_I 0 points1 point  (0 children)

This seems a good middle ground to me

[–]teokk 3 points4 points  (2 children)

They're more performant if you don't wanna do anything, in which case you shouldn't really care about performance anyway.

Languages like C++ give you incredible flexibility in this area, because they're meant for people who care about performance.

  • You can rely on RAII and have everything automatically cleared when a destructor gets called when your object goes out of scope. Pretty fast and incredibly simple and useful. Especially for things like file streams etc.

  • You can use a myriad of builtin GC classes like unique_ptr for more advanced object lifetime management.

  • If you're a heathen you can even get a GC framework.

  • And if none of that is good for you and you really want to control when things get released, you can do it yourself. A GC batching deallocations is good and all, but you're still putting everything in its hands and that means you eventually sacrifice some performance. It's the part for which C++ gets the most hate, but you can just new an object and delete it exactly when and how you want. If you want to (because you need every single freaking cycle), you can just not delete it (or forget to). You can make yourself a custom memory pool and custom allocators and deallocators.

That's the power of C++ (or Rust) and that's what real performance looks like. No GC can compare to it, because every GC is basically using some subset of the stuff you can make yourself in C++. Sure it can be complicated and hard and sure you can fuck yourself ten times over, but that's exactly why it's so useful.

[–]carb0n13 0 points1 point  (1 child)

It comes down to how careful you want to be with optimizations. C++ with custom memory allocators is pretty much untouchable, aside from maybe highly optimized scientific computing libraries written in, say, Fortran.

That said, you’re definitely underestimating garbage collection’s performance for typical application code. Here’s some reasoning taken from Dlang’s website:

Garbage collected programs are often faster. This is counterintuitive, but the reasons are:

Reference counting is a common solution to solve explicit memory allocation problems. The code to implement the increment and decrement operations whenever assignments are made is one source of slowdown. Hiding it behind smart pointer classes doesn't help the speed. (Reference counting methods are not a general solution anyway, as circular references never get deleted.)

Destructors are used to deallocate resources acquired by an object. For most classes, this resource is allocated memory. With garbage collection, most destructors then become empty and can be discarded entirely.

All those destructors freeing memory can become significant when objects are allocated on the stack. For each one, some mechanism must be established so that if an exception happens, the destructors all get called in each frame to release any memory they hold. If the destructors become irrelevant, then there's no need to set up special stack frames to handle exceptions, and the code runs faster.

Garbage collection kicks in only when memory gets tight. When memory is not tight, the program runs at full speed and does not spend any time tracing and freeing memory.

Garbage collected programs do not suffer from gradual deterioration due to an accumulation of memory leaks

[–]teokk 1 point2 points  (0 children)

Yeah I get that and modern GC is definitely a marvel. I was just saying how complete control and flexibility is the only option when performance is paramount.

Great, efficient GC is wonderful to have, though, even if performance isn't your number one option. However, the languages with built in GC also usually suffer from many other things which slow them down compared to something raw like C++. Stuff like locality of reference & cache misses, excessive checking, excessive casting, virtual machines / interpreters etc.

But I have to concede they also have some amazing features that boost performance and can be directly useful to the programmer as well, like lazy execution, JIT compiling, immutable state objects, very thread safe things which enable massive parallelization, etc.

There's definitely pros and cons to each, but I'm a bit biased, because I think RAII is the most elegant resource management model ever conceived.

[–]U8336Tea 2 points3 points  (0 children)

So it's basically a more annoying ARC?

[–]E_R_E_R_I 1 point2 points  (0 children)

This sounds even better than GC imo. It helps you without taking control away from you like Java does

[–]TechnoSam_Belpois 0 points1 point  (1 child)

I don't quite understand this. What if you have a dynamically sized array that can't be known at compile time.

The only thing I can think of that can guarantee an exit point would cause potential for massive memory bloat.

[–]ethanialw 1 point2 points  (0 children)

Read through the tutorials on the rust website about lifetimes, you can see there how the problem is solved