Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> To be honest, I think a lot of the justification here is just a difference in standard library and ease of use.

I really liked this article by Bryan Cantrill from 2018:

https://bcantrill.dtrace.org/2018/09/28/the-relative-perform...

He straight ported some C code to rust and found the rust code outperformed it by ~30% or something. The culprit ended up being that in C, he was using a hash table library he's been copy pasting between projects for years. In rust, he used BTreeMap from the standard library, which turns out to be much better optimized.

This isn't evidence Rust is faster than C. I mean, you could just backport that btreemap to C and get exactly the same performance in C code. At the limit, I think both languages perform basically the same.

But most people aren't going to do that.

If we're comparing normal rust to normal C - whatever that means - then I think rust takes the win here. Even Bryan Cantrill - one of the best C programmers you're likely to ever run into - isn't using a particularly well optimized hash table implementation in his C code. The quality of the standard tools matters.

When we talk about C, we're really talking about an ecosystem of practice. And in that ecosystem, having a better standard library will make the average program better.





The only real question I have with this is did the program have to have any specific performance metric? I could write a small utility in python that would be completely acceptable for use but at the same time be 15x slower than an implementation in another language. So you do you compare code across languages that were not written for performance given one may have some set of functions that happens to favour one language in that particular app? I think to compare you have to at least have the goal of performance for both when testing. If he needed his app to be 30% faster he would have made it so, but it didn't need to be so he didn't. Which doesn't make it great for comparison.

   Edit, I also see that your reply was specifically about the point that the libs by themselves can help the performance with no work, and I do agree with you, as you were to the guy above.

Honestly I'm not quite sure what point you're making.

> If he needed his app to be 30% faster he would have made it so

Would he have? Improving performance by 30% usually isn't so easy. Especially not in a codebase which (according to Cantrill) was pretty well optimized already.

The performance boost came to him as a surprise. As I remember the story, he had already made the C code pretty fast and didn't realise his C hash table implementation could be improved that much. The fact rust gave him a better map implementation out of the box is great, because it means he didn't need to be clever enough to figure those optimizations out himself.

Its not an apples-to-apples comparison. But I don't think comparing the world's fastest C code to the world's fastest rust code is a good comparison either, since most programmers don't write code like that. Its usually incidental, low effort performance differences that make a programming language "fast" in the real world. Like a good btree implementation just shipping with the language.


I did feel my post was a bit unneeded when I added my edit :)

   My point about the  30% was that you mentioned that he got in rust and attributed it to essentially, better algorithms in the rust lib he used.  Once he knew that then its hard to say that rust is 'faster' but the point is valid and I accept that he gained performance by using the rust library.

   My other point was that the speed of his code probably didn't matter at the time. If it was a problem in the past he probably would have taken the time to profile and gain some more speed.  Sure you cant gain speed that can't be had but as you pointed out, it wasn't a language issue, it was an implementation of the library issue.

   He could have arbitrarily used a different program that used a good library and the results reversed.

   I also agree that most devs are not working down at that level of optimisation so the default libraries can help but at the same time it mostly doesnt matter if something takes 30% longer if that overall time is not a problem.   If you are working on something where the speed really matters and you are trying to shave off milliseconds then you have to be that developer that can work C or Rust at that level.

What I think it illustrates more is how much classic languages could gain by having a serious overhaul of their standard library and maybe even a rebrand if that's the expected baseline of a conformant implementation.

>If he needed his app to be 30% faster he would have made it so

That still validates "In short, the maximum possible speed is the same (+/- some nitpicks), but there can be significant differences in typical code" the parent wrote


> He straight ported some C code to rust and found the rust code outperformed it by ~30% or something. The culprit ended up being that in C, he was using a hash table library he's been copy pasting between projects for years. In rust, he used BTreeMap from the standard library, which turns out to be much better optimized.

Are you surprised? Rust is never inherently faster than C. When it appears faster, it boils down to library quality and algorithm choice, not the language.

Also worth noting that hash tables and B-trees have fundamentally different performance characteristics. If BTreeMap won, it is either the hash table implementation, or access patterns that favor B-tree cache locality. Neither says anything about Rust vs C. It is a library benchmark, not a language one.


> library quality and algorithm choice

And especially having performant and actively maintained default choices built in. With C, as described in the post you responded to, you'll typically end up building a personal collection of dusty old libraries that work well enough for most of the time.


I think Rust projects will accumulate their own cruft over time, they are just younger. And the Rust ecosystem's churn (constant breakage, edition migrations, dependency hell in Cargo.lock) creates its own class of problems.

Either way, I would like to reiterate that the comparison is flawed at a more fundamental level because hash tables and B-trees are different data structures with different performance characteristics. O(1) average lookup vs O(log n) with cache-friendly ordered traversal. These are not interchangeable.

If BTreeMap outperformed his hash table, that is either because the hash table implementation was poor, or because the access patterns favored B-tree cache locality. Neither tells you anything about Rust vs C. It is a data structure benchmark.

More importantly, choosing between a hash table and a tree is an architectural decision with real trade-offs. It is not something that should be left to "whatever the standard library defaults to". If you are picking data structures without understanding why, that is on you, not on C's lack of a blessed standard library (BTW one size cannot fit all).


> If BTreeMap outperformed his hash table, that is either because the hash table implementation was poor, or because the access patterns favored B-tree cache locality. Neither tells you anything about Rust vs C. It is a data structure benchmark.

The specific thing it tells you about Rust vs C is that Rust makes using an optimized BTreeMap the default, much-easier thing to do when actually writing code. This is a developer experience feature rather than a raw language performance feature, since you could in principle write an equally-performant BTreeMap in C. But in practice Bryan Cantrill wasn't doing that.

> More importantly, choosing between a hash table and a tree is an architectural decision with real trade-offs. It is not something that should be left to "whatever the standard library defaults to". If you are picking data structures without understanding why, that is on you, not on C's lack of a blessed standard library (BTW one size cannot fit all).

The Rust standard library provides both a hash table and a b-tree map, and it's pretty easy to pull in a library that provides a more specialized map data structure if you need one for something (because in general it's easier to pull in any library for anything in a Rust project set up the default way). Again, a better developer experience that leads to developers making better decisions writing their software, rather than a fundamentally more performant language.


> the Rust ecosystem's churn (constant breakage, edition migrations, dependency hell in Cargo.lock) creates its own class of problems.

What churn? Rust hasn't broken compatibility since 1.0, over a decade ago. These days it feels like rust changes slower than C and C++.

> Either way, I would like to reiterate that the comparison is flawed at a more fundamental level because hash tables and B-trees are different data structures with different performance characteristics. O(1) average lookup vs O(log n) with cache-friendly ordered traversal. These are not interchangeable.

They're mostly interchangeable when used as a map! In rust code, in most cases you can just replace HashMap with BTreeMap. In practice, O(log n) and O(1) are very similar bounds owing to how slowly log(n) grows with respect to n. Cache locality often matters much more than a O(log n) factor in your algorithm.

If you read the actual article, you'll see that Cantrill benchmarked his library using rust's b-tree and hash table implementation. Both maps outperformed his C based hash table implementation.

> Neither tells you anything about Rust vs C.

It tells you rust's standard library has a faster hash map implementation than Bryan Cantrill. If you need a hash table, you're almost certainly better off using rust than rolling your own in C.


One point of clarification: the C version does not have (and never had) a hash table; the C version had a BST (an AVL tree). Moreover, the "Rust hash table implementation" is in fact still B-tree based; the hash table described in the post is a much more nuanced implementation detail. The hash table implementation has really nothing to do with the C/Rust delta -- which is entirely a BST/B-tree delta. As I described in the post, implementing a B-tree in C is arduous -- and implementing a B-tree in C as a library would be absolutely brutal (because a B-tree relies on moving data). As I said in the piece, the memory safety of Rust is very much affecting performance here: it allows for the much more efficient data structure implementation.

I wouldn't consider implementing a B-tree in C any more "arduous" than implementing any other notable container/algorithm in C, nor would making a library be "brutal" as moving data really isn't an issue. Libraries are available if you need them.

Quite frankly, writing the same in Rust seems far, far more "arduous", and you'd only realistically be writing something using BTreeMap because someone else did the work for you.

However, being right there in std makes use much easier than searching around for an equivalent library to pull into your C codebase. That's the benefit.


I don't often do this, but I'm sorry, you don't know what you're talking about. If you bother to try looking for B-tree libraries in C, you will quickly find that they are either (1) the equivalent of undergraduate projects that are not used in production systems or (2) woven pretty deeply into a database implementation. This is because the memory model of C makes a B-tree library nasty: it will either be low performance or a very complicated interface -- and it is because moving data is emphatically an issue.

> I don't often do this

You should have practiced better restraint then, as this was not a productive addition to the discussion.

My experience disagrees with your opinion and I see no value in engaging further.


> constant breakage

Can you mention 3 cases of breakage the language has had in the last, let's say, 5 years? I've had colleagues in different companies responsible for updating company-wide language toolchains tell me that in their experience updating Rust was the easiest of their bunch.

> edition migrations

One can write Rust 2015 code today and have access to pretty much every feature from the latest version. Upgrading editions (at your leisure) can be done most of the time just by using rustfix, but even if done by hand, the idea that they are onerous is overstating their effect.

Last time I checked there were <100 checks in the entire compiler for edition gates, with many checks corresponding to the same feature. Adding support for new features that doesn't affect prior editions and by extension existing code (like adding async await keywords, or support for k# and r# tokens) is precisely the point of editions.

> dependency hell in Cargo.lock

Could you elaborate on what you mean?


> When it appears faster, it boils down to library quality and algorithm choice, not the language.

That's a thin, thin line of argumentation. The distinction between the ecosystem and language may as well not exist.

A lot of improvements of modern languages come down to convenience, and the more convenient something is, the more likely it is to be used. So it is meaningful to say that the average Rust program will perform better than the average C program given that there exist standard, well-performing, generic data structure libraries in Rust.

> It is a library benchmark, not a language one.

If you have infinite time to tune performance, perhaps. It is also meaningful to say that while importing a library may take a minute, writing equivalently performant code in C may take an hour.


Then this is more like comparison between Cargo and whatever-C-using. If a project decided not to use cargo, then Rust would have the same problem.

I acknowledge that C needs a tool as good as cargo, but if we are comparing language, we should restrict to language.


> Rust is never inherently faster than C.

The opposite is true too. Which is the point of the article.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: