12 ms·
Removing garbage collection from the Rust language
- haberman 13y agoI like it! It sounds like ~ pointers are basically like unique_ptr in C++11 or scoped_ptr at Google (http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Smart_Pointers http://google-styleguide.googlecode.com/svn/trunk/cppguide.x...). In practice, I find that these are the most commonly useful semantics, and as Patrick mentions it is simple and predictable. I fully agree that the landscape of GC/refcounting solutions is diverse. If indeed there is a way to allow for different approaches without favoring one, I think that would definitely make Rust more widely useful and future-proof. One question: one distinguishing factor of GC (vs refcounting) is the need to "stop the world" during the mark phase. Would there be a way of doing this from the standard library, or would the GC scheme itself be implemented outside the language? Likewise with any barriers that might be required for mutations, scanning the stack, and other tricky parts of implementing GC?
- eru 13y ago> one distinguishing factor of GC (vs refcounting) is the need to "stop the world" during the mark phase. That's a distinguishing factor only in practice. In theory there are real time garbage collectors.
- bad_user 13y agoIn practice there are also hybrids that try to delay long stop-the-world phases as much as possible, but still need to stop-the-world when compacting the heap, like JVM's CMS. And I was under the impression that there already are GC implementations that never go into stop-the-world phases, like Azul's Pauseless GC [1]. There's also the new G1 from JDK 7 that promises to make pauses fewer and more predictable than CMS, while avoiding many memory fragmentation issues that CMS has (how well that works out in practice, remains to be seen, as it's not fully matured at this point). Btw, I love how Rust is trying to solve memory management issues by reinventing the problem. That's what new languages should do, instead of reimplementing other languages poorly. [1] http://www.artima.com/lejava/articles/azul_pauseless_gc.html http://www.artima.com/lejava/articles/azul_pauseless_gc.html
- eru 13y agoOn top of that, malloc (and friends) don't make any useful worst-case runtime guarantees either.
- hga 13y agoThat's very important to remember if your heap has a messy enough history. The right language + a good GC can give you much better bounded worse case guarantees, with Azul's as the best I know of for an imperative language (the more functional, the easier it is to do a variety of things).
- hga 13y agoYes, there are ones like Azul's, and don't forget the copying GCs that have different characteristics than mark/sweep(/compact).
- steveklabnik 13y ago> It sounds like ~ pointers are basically like unique_ptr in C++11 or scoped_ptr at Google I am not mega familiar with all of the details of {unique,scoped}_ptr, but my current understanding of ~ is this: basically, the compiler inserts a malloc before and a free after something declared with ~ goes into and out of scope, and it's the only pointer allowed to that memory.
- dbaupp 13y ago> it's the only pointer allowed to that memory Not quite true with borrowing (i.e. & and &mut), which allows a temporary pointer to the memory to be created. Unlike C++ however, the compiler makes sure all borrows are safe via a fairly intricate borrow checker, that guarantees (among other things) that the borrowed pointers don't outlive the original object. (i.e. no dangling references.)
- steveklabnik 13y agoAh! Yes. That's what I get for posting at 3am, thank you. :)
- kibwen 13y ago> one distinguishing factor of GC (vs refcounting) is the need to "stop the world" during the mark phase I can't answer the rest of your question, but note that Rust doesn't allow shared memory between tasks. Therefore, in the worst case, any garbage collector would only need to "stop the task" to mark, while the remainder of the tasks proceed uninhibited.
- ben0x539 13y agoThat's only really true for the current GC design. I don't know if there is anything stopping someone from sharing memory via unsafe pointers and trying to manage that with a custom GC.
- kibwen 13y agoTrue, I don't know if anything's going to stop you, but in my uninformed opinion you'd probably be violating so many built-in assumptions that you'd have to perform invasive surgery on the runtime to get it to work. But I'm hardly an expert in this area.
- jeltz 13y agoI look forward to the proposed solution. Because while I understand the arguments about simplifying the language and allowing for external garbage collectors, I am not sure I am convinced that the GC type will be as simple to work with as the current built in GC:ed type. Sometimes you want to have GC when coding and as it is currently implemented in Rust it is easily accessible and simple to understand with readable code.
- chad_oliver 13y agoThe article mentions that Rust is at least partially designed for low-level applications such as writing kernels. If garbage collection is shifted into the standard library, would this allow Rust to be used on real-time embedded systems such as the newer ARM microcontrollers?
- pjmlp 13y agoAt least for ARM Cortex-M3 and NXP LPC2000 microcontrollers you do have GC enabled languages available (Oberon) http://www.astrobe.com/default.htm http://www.astrobe.com/default.htm But having Rust as option is also great.
- jhasse 13y ago"This could be fixed by switching to keywords, which I prefer for this reason" ~ could be named unique_ptr and @ something like shared_ptr. Genius!
- anonymous 13y agoOr maybe we could allow types or type annotations to be written using only non-alphabetic characters and then ~ could be ~ and @ something like @ and my fingers won't break from typing. I do prefer having sigils for types that enrich other types to make them obviously different from types that are parametrized by another. Compare ~string to list<string> - the first is a uniquely owned string, the second is a list of string. ~ is an adjective and list is a noun. ~string is a string with such-and-such memory semantics; list<string> is a list that happens to contain strings.
- copx 13y ago..or given that they like 1970s style abbreviated identifiers so much: "upt" and "spt" Looking forward to writing: "let mut foo = upt .."
- dbaupp 13y agoOne of the best bits of Rust is the low syntactic noise of ~T instead of unique_ptr<T>, since it's so common. (And similarly with @, which is less common, except in the compiler itself, where it is used all the time.) Maybe I'm exaggerating a bit, but it really seems to work well after using it for a little.
- copx 13y agoRust continues to shape up as a real C++ competitor. And C++ certainly needs some competition. It is way too lonely in its domain. I hope the next version of Rust will have a proper Windows package, though. I don't understand why they don't bundle the version of MinGW they depend on. I was pretty disappointed to be greeted by missing DLL errors after running Rust's Windows installer. There are countless MinGW builds, with different thread libraries, exception models, etc. I guess when they say "MinGW" they mean the binaries from the original MinGW project. However those get updated all the time. Thus the packages the MinGW installer would download today might not be compatible with the Rust 0.6 binaries which were build some time ago .. probably with a different version of said packages. Thus they should really release a complete package. Even if there is no compatibility issue, it is just bad policy. I actually program C/C++ on Windows AND have a MinGW build on my machine.. but not the one Rust needs. Which is common because other builds are much more up to date and full-featured than those from the original MinGW project. And I really didn't feel like doing an additional MinGW installation just to play around a little with Rust.
- Ygg2 13y agoI asked a similar question on the IRC and got the answer that they are aiming for Microsoft C compiler so it would work with natively with system as much as possible. MinGW has some slight issues when launching application IIRC.
- copx 13y agoBy the way, why does Rust need a C compiler anyway? I admit I am ignorant here because I never cared. I know that many academic/niche programming languages bundle MinGW/depend on a MinGW or even Cygwin installation.. I always assumed that was to cut some corners. I mean, I have used multiple programming languages on Windows which compile to native code and have no dependency on a C compiler.
- Ygg2 13y agoSo it (Firefox with Servo) would work on Windows as close to native as possible? It would be funny to start Firefox and have a console windows poping in/out of existence or some other visual quirk.
- pron 13y agoThis seems like the right decision: simplify the language, flatten the learning curve, and delegate complex functionality to libraries. But it's important to point out the general importance of good garbage collectors. GCs are importnat not only because they help avoid a common type of bugs, but because they are essential to many concurrent data structures. Performance of modern software is now (and more so in the future) largely determined not by the single-thread speed of running an algorithm, but the scalability of an algorithm as it parallelizes across cores. Many of the most scalable concurrent data structures are almost impossible to implement robustly without a really good garbage collector. As of now, the only really-good GCs are found in the JVM, and Java does indeed provide very low-level access to important memory primitives (direct control of memory fences is now possible through an undocumented API, and will be made public probably in the forthcoming Java 8). But as good and as powerful the JVM is, such algorithms will be necessary in circumstances where the JVM is not the best choice, namely embedded systems. So a garbage collector as good as those available for the JVM (or almost as good) but for a low-level systems language will be a boon. A good GC for C++ would probably be a headache because of the language's inherent unsafety in just about anything. Go is not an option in those circumstances, either. For one, while Go compiles to native binaries, the language actually operates at a higher level than Java (for example, it offers no control over thread scheduling like Java does). More importantly, choices made by the language designers might preclude a GC implementation good enough for high-performance concurrent data structures. Rust is a great language to try and build a good GC for. If it would be possible to do so in a library and not in the core language - all the better.
- steveklabnik 13y agoThere is a branch with a GC written in Rust: https://github.com/graydon/rust/tree/gc https://github.com/graydon/rust/tree/gc
- kmasters 13y agoWhile the JVM has an excellent garbage collector (maybe the best on the planet), it's performance is linearly degraded by the amount of memory a process is using. If you go into any Java shop (which is where I live) and try to run a highly available system with more than 8GB of RAM in a server instance, you will be lucky to do that. Servers on the cheap exist with 256GB of RAM, if you can't get to 12GB without OOMs, and GC pauses of 30 seconds, you will never get to a process that scales to this hardware. Your GC pause on a 100GB system would be over a minute complete stop the world probably more. And that's a shame, because the only antidote, is to over-distribute, and over cluster, and not only slow everything down by 1000X compared to a single server, but the engineering and operational costs to distribute everything into tiny 4GB buckets are also unacceptable. I think the industry should rethink GC, if it can get past this boundary. 4GB walls and 8GB walls compared to the current availability of hardware is embarrassing. I would rather not build a server that can't use all the memory I want it to without falling over. My minimum required RAM metric is 256GB for a process. That could even be a fairly small (code wise) system that just needs a lot of RAM in which case manual memory management is a piece of cake.
- oddthink 13y agoIn every C++ project I've worked on, the vast majority of allocations go immediately into a shared_ptr. This article seems to assert that most C and C++ programs stick to malloc/free or auto_ptr-style semantics. This seems to be a contradiction, so I'm confused. I can see it being true for C, but definitely not for C++. Am I misunderstanding the thrust of this article? Edit: deleted comment about cycles, since they are discussed a bit at the end.
- plorkyeran 13y agoshared_ptr used to be commonly accepted as a reasonable default choice, but that hasn't been the case for years. unique_ptr/scoped_ptr is nontrivially faster (thread-safe reference counting is fairly expensive), and much less error prone. These days the usual advice is to only use shared_ptr if you absolutely need it.
- oddthink 13y agoI'm sure there are many places where other pointer types would be better, but, like I said, I only tend to see shared pointers, usually typecast to something like FooPtr and used indiscriminatly. It's an uphill battle to even use something like a pointer to const. I've never seen refcounting overhead show up in callgrind, so I think the choice to uniformly use the most general version is OK.
- marshray 13y agoBut the insidious thing is they'll be inlined all over the place and may not show up on callgrind. The atomic operations will contribute to cache lock contention inside the processor. Of course it all depends on often you perform operations on the shared_ptrs. Use them only as handles to large components and keep them out of inner loops and you'll be fine.
- andrewflnr 13y agoI suspect he would say that most of the C++ programs you mention don't really need to use shared_ptr, and would be better off with something more like unique_ptr.
- specialist 13y agoNo mention of escape analysis. That's where I'm placing my bets. http://en.wikipedia.org/wiki/Escape_analysis http://en.wikipedia.org/wiki/Escape_analysis Most short-lived objects can be stack allocated. Then the heap gets much less of a work out. The runtime can figure out heap vs stack. Progressively better over time. Definitely better than I can do. C# has (had?) the option of explicit stack allocated data (pseudo objects). Terrible idea. Premature optimization that prevents the runtime from doing a better job. If I was doing embedded, realtime, or kernel dev work, I'd want to fiddle the bits myself. But I don't so I don't.
- pron 13y agoJava does escape analysis, but if I remember correctly, it is no longer used for stack allocation (it is used for other advanced stuff, like lock elision, and replacing objects with scalars) because the JVM GCs have gotten so good that stack allocation provided no significant benefit. The reason is that stack allocation is relevant only for short-lived objects anyway, and for generational GCs, short-lived objects are (almost) a non-issue. GCs struggle much more with long-lived objects, which eventually require compaction, whose cost rises linearly with the size of the live data set. This is the cause of the problems with large heaps mentioned in the comments below.
- specialist 13y agoVery interesting. Thank you for the update. I'm more than a few years out of date. I may have to place new bets. :) FWIW, Azul has presented to our local user group (seajug.org) a few times, most recently Nov 2012. There's video http://www.nimret.org/seajug/index.jsp?p=2012%2Fnov%2F http://www.nimret.org/seajug/index.jsp?p=2012%2Fnov%2F By all accounts, their allocation and garbage collection implementations are the best available.
- emillon 13y agoRust already does region inference for managing borrowed pointers, so I guess that all the information needed for this is already there.
- snprbob86 13y ago
- portmanteaufu 13y agoI'm really excited that Rust has decided to position itself to be usable at a C-level. Not only will I be able to write a kernel module with strong memory safety guarantees (unless I need otherwise), I'll have access to basic data structures like strings and hashmaps.
- steveklabnik 13y agoHere's a minimal Linux kernel module in Rust, by the way: https://github.com/tsgates/rust.ko https://github.com/tsgates/rust.ko
- ambrop7 13y agoI'm completely in favor of this. Garbage collection has no place in the core definition of any language that targets OS kernels and other high-performance applications. It's sad how we have so many languages that fix many of C++'s problems (e.g. D and Go), but not without the addition of a garbage collector.
- pron 13y agoWith regards to OS kernels you may be right (though not for performance reasons), but some high-performance applications would be very hard to write without a GC, especially if they're to take advantage of multicore. See my explanation above about scalable, concurrent data structures. Even hard real-time systems can benefit from a GC, and Java has a few GC implementations for hard real-time apps. FYI, object allocation and de-allocation for short-lived objects is much faster with a good GC than with dynamic allocation, and even for long-lived objects GC gives a higher throughput than malloc/free. The problem with GC is latency issues (pauses) when maintaining very large heaps with many long-lived objects.
- marshray 13y agoSingle- transferred-ownership pointers are not going away. Reference counted pointers are not going away. I don't care if Java programmers don't know the difference between the stack and the heap, please keep the @ syntax to save me from having to type void f(std::shared_pointer<my_namespace::my_object_type> const & p) another 50,000 times in my career.
- pron 13y agoReference counted pointers may very well be going away. See comments below. TL;DR they totally suck in multi-threaded settings.
- marshray 13y agoHow many times have you seen a large application grind to a halt or become unusably slow due to reference counting overhead? Perhaps it happens sometimes, but compare that to leak-it-all-as-garbage and scan-all-the-address-space style collection.