6 ms·
The frustrating thing is that GCs have come far enough that for most of what folks use Rust for, they’d have been happier using a GC’d language. Weird to justi
by pizlonator 3y ago
The frustrating thing is that GCs have come far enough that for most of what folks use Rust for, they’d have been happier using a GC’d language.
Weird to justify this kind of language limitation when a great technology exists that obviates the need for it.
- keybored 3y agoFrustrating for who?[1] 99% of Rust users have likely used languages that use GC. [1] Trying to wean myself off the archaic “whom”.
- pjmlp 3y agoWhich kind of GC though.
- pvg 3y agooff the archaic “whom”. One easy fix is using the modern 'whomst' everywhere. It has the all the faux-sophistication of 'whom' (with some extra borrowed from 'amongst' and 'whilst') while being equitably wrong to everyone rather than special groups like whom-pedant toffs and who-everywhere anarchists.
- keybored 3y agoPerfect :)
- _se 3y agoThe frustrating thing is that people say this without realizing that GCs haven't, and by necessity cannot provide the type of deterministic and predictable performance that would lead someone to use Rust/C++ and friends. There are few people using Rust who don't have experience in other languages. It's wild that you think you know about their use cases better than they do. Your comment says "I have never worked on a problem where GCs are problematic". That is great for you, but it has little to do with the value of improving the situation for native, no runtime, performant languages.
- pjmlp 3y agoOnly for those that put the word GC on the same bag, without understanding there are tons of approaches, and many languages with automatic resource management also provide deterministic features.
- hgs3 3y agoYou can make garbage collection deterministic. Tracing collectors can be designed to accept a timeout so you can "collect" during downtime and resume execution on a predictable schedule. And with reference counting the cost of RC bookkeeping is smeared predictable across the run of the program. The real problem is many GC implementations lack customization and you're stuck with whatever GC algorithm the language implementers pick.
- alexchamberlain 3y agoYou can do RC memory management without the overhead of a GC runtime though.
- jayd16 3y agoAs described (ie. only run when I say so), the GC is less overhead than ref counting because you're not even paying for the cost to free things.
- murderfs 3y ago> And with reference counting the cost of RC bookkeeping is smeared predictable across the run of the program. The biggest downside of naive reference counting usually isn't the cost of reference counting itself, it's being left stuck holding the bag when you're unlucky enough to be the last deref on a giant object graph. You can move the destruction onto a separate finalizer thread like what most runtimes that use tracing GCs do, but then you end up running into CPU scheduling issues if you're not overprovisioned.
- pizlonator 3y agoThere’s much more to it than you say. The real problem is most GCs are built with a “throughput first” mindset and determinism is bolted on. The details of how to build a deterministic GC are beyond the scope of a HN post lol
- pjmlp 3y agoThey have been great for decades, it has been mostly a religion thing. GC being any kind of automatic memory management, tracing GC, reference counting, mixed scheme with reference counting and cycle collectors. Take any systems language that uses that approach to automatic resource management, coupled with ability to use stack allocation, registers and what have you, and there are very few use cases where something else is required. Like kernels, drivers on hard real time constraints. Even for GPUs I think shading languages are much more ergonomic than trying to fit classical languages into them.
- vlmutolo 3y agoThe thing is that most code has a straightforward, tree-like ownership model. It’s overkill to use a GC for everything. It also sometimes leads to memory bloat. What we really need is an ergonomic way to opt into different kinds of memory allocation and management. The borrow checker is great for when you need no extraneous allocation. GCs are great for many linked lists. Bump allocators are great for web template rendering with lots of temporary strings.
- logicprog 3y agoThis is my position as well. Call me a crank, but while a lot of people say that you don't need to go without a garbage collector for most problems, I believe the converse: that there aren't many problems where you actually need a garbage collector (if you have some other way of freeing yourself from having to manually manage your memory and manually avoid use after free and so on). I think once your mind gets adjusted to working without one and within the constraints of the borrow checker, it's really not that much of a drag, and if you really need to do something outside those bounds (or just want to ignore it and hack something together quickly) that's what Gc and Arc are for. And honestly, in general I like the unique intersection of features that Rust offers as a language, totally independent from its performance or the presence or absence of a garbage collector at all. Even if it was garbage collected and about 2x slower, I think I would still prefer it to most of the 40 or so languages that I've tried over the years. It feels like a version of OCaml with a much better standard library and ecosystem, a much better build system, a vastly better and cleaner way of doing parametric polymorphism, and much better metaprogramming facilities in comparison. Not only that, but I genuinely find the constraints of the borrow checker — most especially only allowing either one mutable reference or multiple read-only references at a time — to be a great help in making my code clearer and more straightforward: it essentially helps me reach a lot of the same benefits as pure functional programming (where the data flow of my application is carefully threaded and there is no spooky action at a distance) while still allowing me to have imperative programming and mutation where I need it, by just forcing me to only have one place in my code be able to mutate anything at a given time, to explicitly mark it whenever I'm giving any piece of my code mutable access to anything, and essentially preventing me from having any persistent mutable access to a data structure be held in some other data structure or portion of code that can then continue to mutate it without my explicit permission and knowledge. So why give up the uniquely productive constraints of the borrow checker, which again to reiterate give me 90% of the benefits of pure functional programming with only 50% of the annoyance, and add in all of the unnecessary bloat and non-determinism of a garbage collector, when I have never really found myself to need it? Furthermore, Rust is significantly more performant than even very fast and systems oriented garbage collected languages with their own runtime such as Go or OCaml or Java, which has the added benefit of allowing me to not really have to worry about performance at all beyond picking reasonable algorithms, and sometimes not even then since the language is often fast enough to make brute force feasible, which paradoxically means that when I am programming in rust, this high performance systems language, it often frees me from the burden of having to worry about performance like I would in something like python. For instance, I've actually implemented programs in Python that end up being so hilariously and frustratingly slow that I eventually gave up on working on them. Moreover, if I do have to concern myself with performance in rust, I feel like working in a language that uses RAII and doesn't wrap everything in pointers for GC by default provides a significantly clearer mental model for me of what my program is actually doing with its memory, and how my data is actually laid out, and I also typically have a clearer mental model of what CPU operations my program is actually doing as well. And in fact just having this clear mental model of what my program is actually doing helps me Implement more efficient algorithms and make cleaner and less wasteful programs in general, which I quite like. So yes, in essence I would turn the question back on the questioner and ask why they think they need a garbage collector and a language runtime and all of this extra stuff. Yes it does make the language marginally easier and quicker to write things in, but if I am sitting down to write a large scale program or long running project then being slowed down slightly in return for increased productivity.
- deleted 3y ago[deleted]
- pornel 3y agoI don’t mind GC per se, but languages based around a GC tend to lack other useful properties that Rust has, like deterministic destruction for resource management, and exclusive ownership that prevents surprising mutation of objects without making them immutable.
- swsieber 3y agoI came for the speed, I stayed for the lack of `ConcurrentModificationException`s. In all seriousness though, I'd say I only have 2 real projects that needed Rust's speed, and the rest I've done is because I bring the other type safety benefits Rust brings.
- sunshowers 3y agoWhich popular GC languages have mutexes owning the state they guard, such that it is structurally impossible to access that inner state without holding the lock? To do that you must have single ownership. Rust achieves correctness at scale, over time, more than most GC languages.
- jandrewrogers 3y agoGCs have not come far enough if you care about performance. A modern "fast" GC has latencies similar to disk I/O on modern storage. We don't make a habit of randomly dropping a blocking disk I/O in the middle of a performance critical path, and disk I/O is less likely to pollute your CPU cache as a side effect than a GC. Given that performance-optimized code is routinely designed to eliminate the overhead of even context switches (e.g. thread-per-core architectures) to great benefit and GC pauses are much more expensive than a context switch even when "fast", it is difficult to develop a theory of how a GC does not have an adverse impact on performance. You could try to make the argument that most people writing Rust don't care about performance so it doesn't matter, but that is likely to be controversial at the least.
- pebal 3y agoYou can have completely concurrent GC, without pausing threads. https://github.com/pebal/sgcl https://github.com/pebal/sgcl
- jandrewrogers 3y agoHow does this work with standard thread-per-core architectures used in high-performance systems? Sharing state with a GC thread defeats the entire purpose even if you had a dedicated GC thread for each working core to reduce contention, since the entire point is to all but eliminate atomics and cache coherency overhead. This doesn't address the issue with GCs in high-performance systems, it just turns it into a different kind of problem.
- pebal 3y agoYou either pause the worker threads or share the state, there's no other way. However, the GC thread does not modify the data state.
- _se 3y agoSo, your pauseless GC pauses or requires some form of resource contention (probably mutex-based) to share the state. It's not possible to have a pauseless GC in thread per core, which immediately rules it out for HPC. These are the types of things that the "GC for everything" crowd admits that they don't understand when making such statements.
- hinkley 3y agoOne of the hills I will die on is the somewhat bold assertion that memcached primarily exists because Java got stuck at 1-2GB max heap sizes when obtainable servers started having 4-8 and then 16GB of memory. They got stuck there for long enough that a cottage industry of off-process lookup tables became tenable (also at the time Java was still UCS-16 encoded so you could save 25% of your memory by shoving text out of process) I'm not ready to assume again that every popular GC-based language will be able to keep up with the available memory on cloud instances, let alone be able to vertically scale to a full bare metal machine. The value of Rust's tradeoffs gets bigger the higher you vertically scale, does it not?