Live data from Hacker News

Zig and Rust

matklad.github.io

131–140 of 247 posts

Re: Zig and Rust

#131
post #74

Earlier quoted context omitted.

You can get the idea that a GC has minimal impact on performance when you just look at things like how fast the GC runs, or what % of time is spent in GC, but that only represents a portion of performance implications, there are lots of indirect effects: 1. More memory is used, which means more CPU cache evictions happen 2. There will be things the language won't let you do, because it is garbage collected 3. There w…

Malloc isn’t free, either.

`malloc` doesn't make up a meaningful percentage of performance-oriented software's execution time. Most of those programs bulk allocate and manage memory internally, so the cost of `malloc` isn't a relevant argument against managing memory manually. Indeed it's entirely possible to write a program that doesn't ever use `malloc` while still using allocations internally, from a stack allocator.

The point isn't to `malloc` every single allocation and pair them up with `free` calls; it's to use allocators that alleviate that burden while also allowing you to pick an allocation pattern that makes sense for the use case (and internal use cases).

As an example, if your total memory usage for your program could be determined at startup to be 1GB, then you could allocate 1GB up front and allocate from that memory in the rest of the program, guaranteeing that nothing after startup ever goes to the OS for memory.

Overall it's much easier to write obviously efficient software in Zig than it is in Rust, IMO, and it's much easier to see at a glance that something won't behave stupidly when it comes to allocation and deallocation. To be fair, this is something Zig has over most other languages as well, so it's not exactly specific to Rust.

Re: Zig and Rust

#132
post #120

Earlier quoted context omitted.

JIT compilers, games, database server. Anything where you need high performance and do a lot of allocations.

JIT compilers don’t actually need that low-level programming, and compilers in general use memory in a very haphazard way so they are likely better off with a GC. In fact, you probably loose way more by not being able to write as many/great optimizations in a low-level language than what you lose on a slight overhead — e.g. Java’s Graal JIT compiler is nowadays performing better (written in Java itself) than the “ori…

Indeed, having played with doing compiler / parser / transformation type work (for relational query transformation, etc) in Rust, and got stuck in a maze of borrow-checker disasters around the tree of references... what I'd say is: this kind of thing is actually better done in a higher level statically typed garbage collected language. For myself, I'd use a mature functional something like OCaml/StandardML or a Lisp or similar. It's just easier to not have to worry about borrow checking references and allocation issues when writing stuff like that.

The case against garbage collection is where latency is key. P95, P99 latency always suffers under any kind of garbage collector, no matter how advanced. You can minimize pauses, you can move them around, you can play with different collector aspects, but in the end... compare to explicit allocation/destruction/lifetime mgmt, it's going to suffer.

I don't think compilers generally have these latency concerns. Throughput matters, but not the odd unpredictable pause.

There's only so many application domains where that is super important: Certain kinds of high traffic servers, database engines [not so much query parsing & planning, but execution, data structures, buffer mgmt, networking], operating systems, and games. Also embedded systems where a tight memory profile is important.

I like Rust (and Zig) but sometimes when people have a good hammer, they go looking for nails where there aren't any...

(All that said, I feel like some tree/DAG-heavy problems in garbage collected languages could benefit from static (and runtime) reference analysis tools that look a lot like Rust's borrow checker, purely for code sanitation/tracing.

Also, GC tracing and some of the programming patterns GC encourages can cause havoc on L1 caches reducing throughput as well as a side-effect, so there's that)

Re: Zig and Rust

#133

I tried zig after reading so many HN articles and I am not impressed. You have to remember to free after each allocation. The language is supposed to be simple, yet there exist try, catch, defer, errdefer. Rust enums make defining errors so simple with arbitrary payload. Using data structures in rust is much nicer. I think unless you have some precise needs about memory allocation, and want to control exactly where w…

"(Zig's) collections are not parametrized by an allocator, like in C++ or (future) Rust. Rather, an allocator is passed in explicitly to every method which actually needs to allocate. This is Call Site Dependency Injection, and it is more flexible.' I'd once considered the idea of having "space" as a type. Space is an array of bytes, and it cannot be read or written. Constructors take in "space" and turn it into an o…

> This separates construction, which is a typed thing, from allocation.

But this is what C++ actually does! C++ ctors run after memory has been allocated (on the stack, on the heap, from a pool, etc.) Or maybe you mean something different?

Re: Zig and Rust

#134

Still not convinced that memory semantics are critical in the vast majority of domains. Incredibly fast speeds can be achieved with simple GC and RC for short-running programs, and programs with sustained runtimes can rely on generational GC. These methods have the advantage of nearly eliminating the need for memory semantics, leaving only business logic behind. The best example might be Nim, which drastically reduce…

You can get the idea that a GC has minimal impact on performance when you just look at things like how fast the GC runs, or what % of time is spent in GC, but that only represents a portion of performance implications, there are lots of indirect effects: 1. More memory is used, which means more CPU cache evictions happen 2. There will be things the language won't let you do, because it is garbage collected 3. There w…

> That said, it is still the case that most programs aren't penalized by GC much. But people do still work on operating systems, games, databases, video editing tools and such where getting great performance is really important.

This statement simply isn't true though, at least in the general sense. Sure, for old school atomic RC and compilers that didn't do RC elision etc it might've been true. But we can and have made smarter compilers, just like Rust has gotten smart about life times.

Like @netbioserror I use Nim, but for embedded and real-time stuff. There is a minimal overhead (one machine word per allocation), but using Nim's ARC "memory management system" gives me results comparable to Zig or Rust though with much less work. Technically ARC without the cycle collector isn't a GC.

There's no reason D and other languages with "memory management" couldn't do similar RC based systems. As the compilers get smarter it approaches the same result as Rust's manual lifetime analysis. In many cases compilers can be smarter than human programmers about memory.

Even then, there's occasions where in Nim one can and will reach for manual memory allocations. It's easy to do when wanted.

> 2. There will be things the language won't let you do, because it is garbage collected

There will be things that systems like Rust's lifetimes makes harder / less performant as well: https://ceronman.com/2021/07/22/my-experience-crafting-an-in...

> 3. There will be extra FFI costs, like when making OS calls or calling into other languages

For generational GC's this is true, but for RC systems not as much.

> There will be cultural tendencies, if a language uses GC, the people who designed it and used it are less likely to be interested in compromising other things for extreme performance.

This is certainly a thing. However, cultures can vary a lot. For example Nim provides zero-cost iterators as well as minimal-cost "closure" iterators. Now Nim's string libraries do a lot of copies by default which makes it easy to ruin performance. However, there's an open RFC to provide CoW strings to reduce this overhead. There's also the new `lent` type too. After all, it's used in games and embedded so there's desire for that.

Certainly if your language is tedious about memory management, you'll spend more time on it. Though as an end user I'm not sure I've noticed a practical difference between Go and Rust programs as compared to C#/Java applications. The latter I avoid running if possible because of their general bloat.

Re: Zig and Rust

#135
post #13

This title is almost perfectly designed to do well on HN, but it's definitely worth reading in it's entirety. Some highlights for me: - The author sees Rust and Zig in different niches. When Rust was created it didn't need to specialise because there were no languages like Rust. It was free to be a general purpose language targeting multiple domains. But that's not the case for Zig. He sees Zig shining at writing sys…

My general understanding is that Zig was made to make video games and Rust was made to make system software, and everything falls out from there.

Did you mean Jai, which indeed is made with game development in mind? Because I've never heard anyone talk about games with Zig, and iirc one of the original motivations for the language was audio software.

Re: Zig and Rust

#136
post #90

> we don’t have a reliability-oriented high-level programming language with a good quality of implementation (modern ML, if you will) Java is fairly popular, I hear.

Calling Java a modern ML is a bit of a stretch :-). It's getting sum types, I hear, but it still has null, and it's not really the feeling of a nice expression oriented Hindley-Milner language. If anything scala 3 might be a closer candidate.

Re: Zig and Rust

#137
post #116

Earlier quoted context omitted.

> ...and want to control exactly where when and how memory is allocated... Isn't this the whole point and the reason why manual memory management is still a thing - and actually getting more important with the ever widening CPU/memory latency gap? There simply is no silver bullet for memory management, you can either have high performance or automatic memory management, but not both. (vastly simplified of course - bu…

> There simply is no silver bullet for memory management, you can either have high performance or automatic memory management That’s only true for workloads where you can optimize the memory layout/allocations. This may not be true in general, where you will end up implementing a shitty GC that will definitely perform worse than a properly written one. Also, the cost of a proper GC is heavily overblown in my opinion.…

GC is not a solved problem and can be costly. We clearly see in a production server written in Go spikes in latency and memory usage. So most likely for the next server-type application we will use Rust.

Then there are benchmarks where Nginx is faster than Caddy by factor of 3.

Re: Zig and Rust

#138
post #71

For those reading only the beginning, one could think that Matt is endorsing Zig over Rust. It's not that, from the article: > It’s not true that rewriting a Rust program in Zig would make it simpler. On the contrary, I expect the result to be significantly more complex (and segfaulty). I noticed that a lot of Zig code written in “let’s replace RAII with defer” style has resource-management bugs. I also have read Mat…

“Matt” is not his name https://matklad.github.io/about.html

Re: Zig and Rust

#139

Earlier quoted context omitted.

> ...and want to control exactly where when and how memory is allocated... Isn't this the whole point and the reason why manual memory management is still a thing - and actually getting more important with the ever widening CPU/memory latency gap? There simply is no silver bullet for memory management, you can either have high performance or automatic memory management, but not both. (vastly simplified of course - bu…

> you need to spend so much effort to reduce memory management overhead that it is usually less work to do it all manually in the first place That has not been the case in the vast majority of applications I've written across a number of domains using garbage collected languages. Yes, there is runtime overhead to GC. But for many many programs, the cost of the overhead is acceptable and you can spend all of your time…

Not allowing you to talk about allocation in any great detail certainly on the surface seems to simplify things, but as with most things of that nature it tends to complicate things in edge cases (or just entire parts of industry). What the GP is alluding to, it seems to me, is that in those cases the effort to remedy the problem tends to become a constant hunt and guessing game of "Where are we accidentally allocating and ruining everything?".

Inevitably some kind of object pooling comes into play in most of these cases and this is arguably a much worse solution to the basic problem. If you know you're going to be landing in this space I don't think there's much purpose to pretending you're better off using a GC that you'll be trying to avoid anyway. It's far easier to sketch out your allocation strategies and do things deliberately.

Fast software allocates and deallocates in bulk, so the straw man of individual `malloc` and `free` isn't exactly relevant. Arenas, bump allocators, etc. are what is actually being discussed as an alternative.

(As a side note I think this makes RAII a somewhat peculiar solution to the issue as well. RAII implies objects themselves controlling allocation and deallocation and this is just not very useful at all if you want to make something that behaves properly.)

Re: Zig and Rust

#140
post #111

I tried zig after reading so many HN articles and I am not impressed. You have to remember to free after each allocation. The language is supposed to be simple, yet there exist try, catch, defer, errdefer. Rust enums make defining errors so simple with arbitrary payload. Using data structures in rust is much nicer. I think unless you have some precise needs about memory allocation, and want to control exactly where w…

I think you might actually like Virgil more. It's actually memory safe and has nice enums. It has no way near the adoption of Zig, nor the standard library or batteries included. But it is my fullest expression of what I think a systems programming language should be.

I second that. Several ideas of Virgil like unification of an argument list and tuples or handling of objects are very nice.
Post reply on HN