Is zig lang faster than rust?

You can get something like a 10% speed improvement for even existing programs if you run your CPU without the Meltdown and Spectre mitigations.

1 Like

Regarding my previous comment on the untagged unions, I want to be absolutely clear that I have nothing against using untagged unions in this way, and I would also support using them in this way in unsafe Rust. What I had a beef with was that it was presented as being just as safe as what Rust offers.

If you want to argue that your language is faster because it has a looser definition of safety that makes it more socially acceptable to use dangerous features, that's fine, but then you should say that.

13 Likes

I don't really understand this comparison. That would be allowing known exploits for the sake of performance. Using unsafe would only allow the risk of some exploit or other problem.

A 35% performance improvement is pretty significant. In many domains it seems like that would be worth the added risk, especially if you can encapsulate the unsafe well and vet it with multiple reviewers.

But maybe Andrew was right and

Nope. Not even close.

Same as Spectre/Meltdown mitigations. Said mitigations are not fixing known places where you may exploit Spectre/Meltdown to do something. Nope. They are changing code to make sure certain code patterns are not used even if most of them are not exploitable (it's not even know if any if these are actually exploitable).

Similar to range checks in Rust array accesses.

2 Likes

I did not understand any of that post.

Are you suggesting array bounds checks in Rust are a useless waste of time?

The trick is to make sure it's well-encapsulated. If it means that all the code in the project ended up being unsafe because that was required to interact with the storage tech, I'd hassle them about it.

But if the internals are unsafe while it provides a useful and fast interface that's safe, then great! That's the sine qua non of Rust. And it's completely doable -- see, for example, https://bevyengine.org/ which has such an ECS.

9 Likes

Most of the time — yes. But every now and then they help to prevent serious issues.

Similarly to Meltdown/Spectre mitigations: most of these are inserted in places which couldn't be exploited in principle, but one out of thousand may happen to prevent serious attack.

In both cases we pay for a lot of useless checks to ensure one rare one would catch problems.

@jbe:

Do you remember a particular example?

Rust's memory safety has been pushing the boundaries of LLVM with respect to opportunities for optimization related to noalias that are just off the table with C/C++. Once LLVM takes advantage of them, things get faster. Here's a years-old reddit thread about the topic. From this presentation, it sounds like LLVM still has yet to take full advantage of Rust's noalias promises.

1 Like

Rust can outperform zig in real world applications.
Languages are only as fast as the libraries you use to create your functions.
Rust community pledges to optimize performance and keep code as robust as possible.
Maybe when using no external libraries zig would be faster, but it won't catch bad practices at compile time.
The reason why I love rust, is the speed, safety and this new paradigm rust introduces. It's not like learning other languages where you just learn the syntax and certain schematics, it's like learning a whole new way to code like functional or procedural languages.

I want to add good content to this forum. I read the "this week" and "help me" and all the other posts and try to learn and respond and be part of the team. A couple of times I might have actually gave some good advice. More than once I have had posts blocked for slightly rude comments. For some reason, my sarcastic comment above seems to daily get liked and has earned me a "Good Reply" badge. I conclude that my past slightly rude comments were blocked because they just were not funny enough.
Thank you for your time, now back the the benchmarks.

Zig and Rust are my two favorite languages, and I've experimented with writing the same program in both languages a number of times. I've not seen a significant difference in speed. Here's my take, based on real world experience.

In Zig's favor:

  • Binary size is almost always smaller, likely due to lazy compilation
  • Much less friction with C interop
  • Simpler language
  • Allocations are always explicit
  • Can be totally freestanding (no libc dependency) and still use the Zig standard library
  • Best compile time reflection anywhere

In Rust's favor:

  • Much more mature
  • More comprehensive tooling
  • Safer by far (although I think Zig is orders of magnitude better than C)
  • Better compiler messages
  • Better documentation everywhere
  • Easier and safer concurrency
  • Traits are extremely useful (Zig has no equivalent)

What is surprising is that I can often copy paste an entire function from one language to the other and just make some minor syntax changes to get it working. I've found over time that for most language constructs there is an equivalent feature in both languages. For instance, Rust has enum types with associated data, while Zig has tagged unions. Both have optionals instead of NULL. Error handling is similar, including the ability to bubble up errors to the caller. In short, I see a lot of convergent evolution, or maybe Rust had a much greater influence on Zig than Andrew Kelley likes to admit? I don't know.

I feel like both languages have their place. A lot of people describe Rust as a good C++ replacement, and I think within reason that's a good analogy, while I would sooner reach for Zig to write a kernel or to interact directly with the kernel (there's a generic syscall interface in the standard library), or basically anything sufficiently low level. That's not to say those things are out of scope for Rust because obviously Rust can be used quite effectively in those situations. There's a distinct advantage to being able to include anything from the standard library though, without linking to libc and without pulling in any other functions from std than the ones being called. It's much less friction to get a tiny self contained binary with a lot of functionality. On the flip side, it's much more difficult to write library code due to lazy compilation because any code which isn't actually called does not get compiled. The only way to write a library in Zig and to be sure that it performs exactly as expected is to have 100% test coverage. Anything less than 100% test coverage and the compiler won't even check all code branches. And that can bite you, hard.

I recommend Rust to newer programmers and especially younger ones. I recommend Zig to hardcore C programmers. I'm working with my daughter in Rust (she's a first year computer science major) because I feel like it's going to help her future career. I wouldn't try to teach her Zig because it's very early days for that project and difficult to say if adoption will ever reach critical mass. It might be a skill that just never becomes an advantage.

4 Likes

Allocations are also explicit in Rust.

I'm curious what kind of friction with C interop can be eliminated that Rust can't take care of. Depending on a C library is a pain because getting C code to compile is generally a pain, but that's not the fault of Rust.

Well I'm not sure what you are expecting there. It's a non-const function call so it is expected to be able to do anything, and opening a file is a much more expensive operation than a mere allocation anyway.

Alright, so you basically want an effect system. That's been discussed numerous times before, and pretty much every time the conclusion was that it sounds good in theory, but in practice, it doesn't add much value, yet it causes endless annoyance.

And I'm saying that it's true of Rust, too. It's just that you have to have the right expectations syntactically. Function calls can do a wide variety of expensive and dangerous things; allocations aren't special. Meanwhile, the mere construction of a struct instance or an enum variant, or the declaration of a variable doesn't allocate (or perform any other side effect) implicitly, and in practice this is what matters, not that all allocating functions have an additional argument. Hence, I don't see why the Zig way is any more explicit (let alone in general an advantage).

1 Like

Does zig not allow you to call C functions? I'm pretty sure you can do that fairly easily, and C functions can certainly allocate.

You raise some valid points. I'll give one example of the Zig way being an advantage, with the caveat that Rust is making some progress here. When the first PR landed in the Linux kernel to add Rust as a second language one of the main blockers was that the allocator could potentially panic in an OOM situation. If it were Zig in this case, you would just swap that allocator for one with more desireable behavior.

That said, this is now a solved problem with the latest work on bringing Rust into the Linux kernel.

Personally, I'm comfortable in both languages and don't feel like one strategy is inherently better. I find that Zig aligns well with my own feelings about wanting things like allocations and control flow to be very explicit, but when I'm working in Zig I miss other things that Rust provides, like Traits as I mentioned before.

First off I'm not claiming that Rust is high friction in C interop, so let's keep that clear. Zig is just an even lower level of friction. Whereas in Rust we tend to wrap the interface in safe abstractions, in Zig it's pretty common to just call C functions directly. Additionally, Zig's string type is literally just an array of u8, and as such is extremely close to C strings. Most if not all of the Zig standard library functions which operate on strings have a counterpart which will give the result with null termination, which can then be passed via a pointer directly to C. In Rust the String type must be converted, the possibility of the conversion failure accounted for, etc. It works fine in practice and I've done it myself, but there's less to be done in Zig in this regard.

Then there's the Zig build system, which can build C code directly, use mixed C and Zig object files, etc. You can build C code with a build.rs file of course, but you'll be shelling out to do it.

I say all of this not because I'm promoting Zig, mind you, but because you asked. I still love and use Rust quite heavily and feel confident saying that it's going to be an important language for decades to come. And for good reason!

4 Likes

Nobody is saying there's no difference between passing a parameter and using global state. The difference is in how these things are made explicit in a syntactical fashion. In rust, all allocations happen behind a function call. That's explicit. It's not the same degree of explicitness, but it is explicit. Functions can do lots of stateful things, regardless of whether you pass in an allocator, so why bother separating out the allocator?

As far as I can tell, it is not true that "in Zig any functions that need to allocate memory accept an allocator parameter," as this is only true of the standard library, and only because they've chosen to design it that way. You could do the same thing in Rust if you wanted, so how is that a way to meaningfully distinguish between the languages? Their standard libraries are designed differently.

6 Likes

Moderator note: I've cleaned up a few comments that got into back-and-forth that doesn't really add much to the discussion. Please avoid saying things that can be seen as personal attacks (like accusing someone of lying). If someone says something that you don't think is true, don't just say they're wrong, try to prove it.

8 Likes

I very much agree. The fact that Zig's std functions which allocate take an allocator is just a form of contract specific to its std lib, it doesn't mean any other Zig library is bound to such contract. One can simply create a seemingly simple function signature which allocates behind the developer's back, and the Zig compiler would just simply accept it:

pub fn add_two(x: i32) i32 {
    // allocate some value and add to a global container
    return x + 2;
}
3 Likes