Comment by pron

7 hours ago

> I kind of discovered "why Rust" from first principles and from then on I was hooked.

I get it. The language certainly does appeal to some people, and I can understand why, just as I understand why it does not appeal to others.

> I found myself wondering why I had to keep track of lifetimes, nullability etc in my head when it was so easy to mess those up.

And I agree with that, but my conclusion (after decades of experience with low-level programming) is somewhat different: Don't reach for a low-level language unless precise low-level control over the hardware is the exact thing you're after. And when that is the case, I find that safe Rust doesn't offer the control I need, and unsafe Rust (and/or a lot of custom code) is not what I want to use.

> Maybe if you're willing to run Java/C# JIT you might find some wins

Of course I use the JIT. That's exactly what it's for. Now, I don't care if the buffer from which the CPU reads instructions is memmapped from a file or generated by a JIT, but I do know that some people like the "single native file experience". To that end, we're working with Google to add a small feature to the JDK that would allow it to link the JVM, other native libraries, and Java classes into a single native executable (it's still going to JIT the Java code, but you'd be launching a "native binary").

> (skeptical it's faster across the board)

I wouldn't say it's faster across the board. You sometimes can write large programs in C++ (or Rust) that match and even exceed Java's performance, but it gets harder and harder the larger the program is. On average, I find that the "effort per performance" is, on average, significantly lower in Java in large programs. And it's not just the JIT. Another weak point of low level languages is that their pointers can't move, which means they can't use moving collectors, which also offer superb efficiency, again, mostly in large programs when you have lots of objects of varying sizes and lifetimes (especially now when we no longer have GC pauses).

> but you also don't need dynamic dispatch in performance-critical areas

You certainly don't start out needing it. Over time, however (and important codebases last at least 15-25 years), it either creeps in or it affects sufficiently many less critical paths to make an impact. You can try and re-architect things, but it takes a lot of effort (and it's this evolution effort that was a major reason for C++'s decline).

> But we see even experienced professionals making mistakes with low level languages

Absolutely, but my prescription would be to avoid low level languages altogether, and that has indeed been the industry's trajectory, and it's continuing. And when you absolutely do need to kind of control that low level languages offer, language complexity can also cause (or help hide) mistakes in code that is often very subtle, and the added safety, which is partial at best in those situations, isn't enough to offset that. Again, this isn't universal, but there are reasons to avoid Rust in low level code that are just as good as the reasons to pick it, and so different people will choose differently.

BTW, I've never worked on a browser, and it may well be the Rust is the best language for that, but I would be very curious to try Java. First, modern browsers run a lot of JS so you have a JIT and a GC, anyway, and so it might be both easier and more efficient to have everything use the same GC, and while process isolation would have required Java to re-JIT the rendering pipeline, Java is about to allow sharing JITted code (and even caching it from one run to the next) so that there would be no need to warm up the same code over and over.

I think the only argument I’d make about high level programming languages is that software continues to outpace hardware development in sucking up as much performance gains as possible. One program written in a slower, garbage collected, high level language is ok, when they are all it’s bad. I think eventually we’ll get to a point where we won’t have to think about memory management anymore but we aren't really there yet. Heck, software written in C++ like browsers are dog slow, imagine if they were written in Java…

  • I originally came to Java because of the better performance it offered compared to C++ in large programs. The JVM is specifically designed to remove some of the fundamental performance overheads that low-level languages suffer from, and manifest especially when programs grow large (and a browser is quite large). So when someone talks to me about "GC languages" being slow and low-level languages being fast, I know they've not had much experience with either Java or low level languages, nor do they understand modern compilers and memory management. Java offers strictly more optimisation opportunities than low-level languages, in compilation as well as in memory management. What it gives up in exchange is low-level control (including worst-case performance), but it is low-level languages that sacrifice performance (especially average-case performance) in exchange for the control they need. Not being able to move pointers freely and not being able to deoptimise and recompile at runtime are serious impediments to modern optimisation, but low-level languages gladly give that up because they're not optimised for performance but for precise control.

    In particular, modern moving collectors were designed for the purpose of removing the high overheads of malloc/free allocators that make heap memory management so expensive in low-level languages (and in any language that uses non-moving memory management strategies). The reason code in low-level languages tries to avoid things like heap allocation and virtual dispatch on the hot path is not because these things can't be super-fast (most virtual calls in Java are faster than many static calls in C), but because they are slow in low level languages because of their constraints.

    • I don't think it's true that Java offers strictly more optimisation opportunities than low-level languages, but rather different optimization opportunities. C++ and Rust have other opportunities that Java does not generally have:

      - Explicit object lifetime and deterministic destruction.

      - Stack allocation by default.

      - Value types and direct embedding of values in data structures.

      - Precise control over data layout, alignment, padding, and SIMD-friendly representations.

      - Explicit allocation strategies: arenas, pools, slab allocators, region allocators, custom allocators, memory mapping, etc.

      - No mandatory tracing, write barriers, object headers, or garbage-collector scheduling.

      - Native interoperation without an FFI boundary.

      - More predictable latency behavior.

      - Compile-time specialization through templates in C++ and monomorphized generics in Rust.

      Rust’s ownership and borrowing model can also provide strong non-aliasing and mutability guarantees in safe code which are useful for optimization.

      Saying that "Low-level languages sacrifice performance for control" is also not true imo, since they can avoid allocation entirely, store data contiguously rather than as individually allocated objects, avoid all gc work, control cache behavior and eliminate pointer chasing, and importantly, guarantee hard or soft latency bounds.

      Saying that "Most virtual calls in Java are faster than many static calls in C" is not a meaningful claim either. I think? Cuz "it depends" :)

      And remember that GC is not free. Objects must eventually be traced. Reference writes may require write barriers. Objects have headers and alignment overhead. Heap size must often be larger to maintain throughput. Concurrent collectors consume CPU and memory bandwidth. Stop-the-world phases or other latency issues remain even with modern collectors. Object movement can complicate native interoperability and pinning. Malloc can cause performance issues but Java is not beating an arena allocator in C++ or Rust.

      Keep in mind speed and throughput are not the only performance metrics. So is startup time and memory footprint, where Java loses badly here.

      I wish we had real published research to go off of here but real world examples are all we really have. I'm not seeing AAA game studios building their engines in Java. I don't see any OS's building their kernel (or anything really) in Java. If Java is faster than low level languages, why is that?

      And look, I don't dislike Java or the JVM, and I'm actually a really big fan of C#. I just like Rust more.