5 ms·
This GC you speak of that hides allocations as an implementation detail sounds excellent! Every GC I've worked with - and my day job is orchstrating several tho
by jakewins 6y ago
This GC you speak of that hides allocations as an implementation detail sounds excellent! Every GC I've worked with - and my day job is orchstrating several thousand JVMs - simply moves the time when you need to think of allocations somewhere else.
Either it moves it into PagerDuty, like G1, or it moves it into your GCP bill like Shenandoa and ZGC.
The trade-off has always been latency-throughput-footprint, nothing I've seen yet has changed that. The innovation in Rust is realizing you can do all the tracing work at compile time.
- pron 6y agoAn implementation detail doesn't mean that you don't think about it. We think about implementations all the times. It means that it's a detail, and changing a subroutine's allocation pattern does not create a ripple-effect throughout the code in its transitive consumers. > The innovation in Rust is realizing you can do all the tracing work at compile time. This is spectacularly false. For all non-trivial allocation/deallocation patterns, Rust also uses a runtime reference-counting GC, which is significantly slower than the tracing GCs you find in OpenJDK. The benefit comes from not relying on it too much, but this comes at a considerable cost of lost abstraction, which means more costly maintenance over the years. This is the same for all low-level languages; the difference Rust brings is that it (conservatively!) checks for memory access errors. Another difference is that most people who talk about Rust haven't actually written a significant application in it and had to maintain it for years. I'm not saying it's impossible -- people do this for C and C++, which make a similar tradeoff in this regard -- but it does come at a substantial cost.
- jakewins 6y agoAt this point I think we are arguing somewhat subjective things. I used to be a hard-core zealot for the JVM as the performance platform of the future - and did talks arguing just as you are here why HotSpot outperforms $LANGUAGE in real applications. I feel like I'm hearing myself in your argument.. I wrote a signficant portion of the Neo4j storage engine, which is in Java. Now I'm writing another database engine in Rust (sidenote: not for replacing the Neo4j engine, just because it's interesting). Arguably database engines qualify as "significant applications". I find - subjectively: - Maintaining performant code in Rust is easier. I do the same patterns as I did in Java, except it doesn't rely on easy-to-break assumptions of how HotSpot happens to work (ex: stack allocations) - Like you said elsewhere, the issue is often about fragmentation, ultimately stemming from object churn. I find that Rust makes it, culturally perhaps, easier to maintain low allocation code than Java. (sidenote: I think these two points is also why Go code generally has "better GC behavior"; Go doesn't have a better GC, it has a language that encourages less heap allocation) - The engine I'm writing in Rust is faster than anything I've written in Java and - critically - runs for days and months without notable stalls. Come to the dark side!
- cogman10 6y agoI personally like both languages and think they both have a place. My 2 cents. The JVM works exceptionally well when you are dealing with long lived apps which deal with a lot of allocations and the machine running it has a substantial amount of memory. Things like webservers, for example, are near perfect fits for the JVM. Rust does really well when you need high performance, low memory, and ultra fast startup times. It won't necessarily outperform the JVM when you talk about doing a lot of heap allocations (due to heap fragmentation) and it unfortunately suffers from the same heap fragmentation if you are dealing with a long lived server that does a lot of allocations. But then, maybe that performance loss is acceptable for the ability to very quickly scale up and down servers. Now, the JVM is making great strides towards getting faster startup times and even fast performance at startup (AppCDS). However, those strides often involve trade offs with either build complexity or performance losses (such as Graal's AOT). The benefit for rust is that it is as fast as it ever will be without any special build steps or tweaks. Oh, and let's not forget diagnostics. Flight recorder for the JVM is simply AMAZING. The ability to hook up to a poorly behaving production server, start flight recording, and getting detailed information about things like "where are allocations happening" or "what are the hot methods" is simply amazing. No other platform that I know of has the level of detail you can get right out of the box with flight recorder. Certainly not without restarting the application with additional configuration. For example, you'd need a special build of rust with profiling turned on to even start to get the same level of info, doing such also significantly negatively impacts performance.
- pjmlp 6y agoJust a small correction, AOT and JIT caches like AppCDS were already available in commercial JVMs like J/Rockit or IBM WebSphere Real Time, as two examples from a couple of possible ones. What is happening now is that the free beer Java users are also getting those features on the package.
- pron 6y agoI'm already on the dark side. Rust isn't my cup of tea, but I do a lot of programming in C++, and hope to one day use Zig for low-level programming. But this does come at a considerable cost, so I do it only when I must -- i.e. I'm targeting a constrained environment or I really need low-level control. The cost isn't so much up front (aside from the big loss in observability), when you first write the code, but low-level languages result in rigid programs that are much harder to evolve over years, certainly with large teams, that with Java. Separate from that, JDK 14 doesn't behave like JDK 8. If you need performance and observability and you are not constrained, then Java is an excellent choice that can have a big positive impact on costs.
- feanaro 6y ago> This is spectacularly false. For all non-trivial allocation/deallocation patterns, Rust also uses a runtime reference-counting GC, which is significantly slower than the tracing GCs you find in OpenJDK. Are you talking about `Rc` and similar smart pointer types? If so, the twist is that in Rust almost all allocations in Rust are trivial in this sense.
- pron 6y agoThere's no "twist". There was a claim that Rust does tracing at compile time; it does not. It's allocation patterns and costs are similar to all other low-level languages. The cost and benefits of such languages are well known.
- feanaro 6y agoMy point was that Rust's compile-time machinery and conventions allow you to avoid using referencing counting smart pointers like `Rc` in almost all cases. What you call trivial patterns of allocation is in fact the prevailing type of allocation in Rust.
- thenewwazoo 6y ago> For all non-trivial allocation/deallocation patterns, Rust also uses a runtime reference-counting GC... Lest anyone read this and think it's true, it's not. Using Rc is a design choice, and not one that is a given. I have written tens of thousands of lines of Rust code doing very heavy data processing and used Rc only a handful of times. In fact, I find using Rc without a very good reason to usually be a bad idea that enables lazy thinking.
- pjmlp 6y agoI on the other hand have used Rc quite a lot, it is either that or having to twist the application architecture to somehow fit into Gtk-rs expectations.
- imtringued 6y ago>This is spectacularly false. For all non-trivial allocation/deallocation patterns, Rust also uses a runtime reference-counting GC, which is significantly slower than the tracing GCs you find in OpenJDK. This is not really a good comparison because you can't write the same code in either language. In Java practically everything gets allocated on the heap barring some optimizations. Meanwhile Rust programs can selectively allocate memory on the stack when it makes sense to do so. Reference counting is is just one of many different allocation strategies available to Rust. It is not the first tool you grab when you want to allocate memory in Rust, therefore absolute throughput of reference counting might not be as relevant in Rust as the absolute performance of the GC in Java.