12 ms·
The relative performance of C and Rust
- dpc_pw 8y agoTL;DR: Using dtrace to analyze the performance of AVL Trees in C vs BTreeMaps&HashMaps in Rust. Spoilers: Rust wins every time, and you should rewrite your software in Rust anyway ;).
- dxhdr 8y agoI thought the tl;dr was: Implementing B-trees as an embeddable library in C is hard, therefore Rust is faster.
- kenhwang 8y agoRust makes hard problems easier, but easy problems harder. BTrees are a great example of that sentiment.
- TheDong 8y agoLet's look at some problems in C which should be easy: 1. Storing an array and its length together and always checking it correctly such that you don't have a buffer overrun. 2. Freeing memory, not leaking memory. 3. Using a union correctly, e.g. always accessing the right variant. 4. Printing unicode characters. 5. Macros which dont' conflict with their scope (are hygenic) All of those problems seem like they should be easy, are incredibly easy in rust, and are not fully solved problems in C. I don't think rust makes easy problems harder just because its type-system and macro system and such are all so good compared to C. It turns out having proper sum types instead of C's garbage unions, and having proper string support, and so on make easy problems easy too!
- paulddraper 8y agoThe problem that Rust makes hard: linked lists.
- Animats 8y agoDoubly linked lists. Singly linked lists and one-way trees work fine with the single ownership model. As I've said before, the two places where Rust has problems with expressive power are backlinks and partially initialized arrays. Those are routine cases which are forced to unsafe code.
- Rusky 8y agoPlease, these are not interesting or useful talking points. You always get the same responses: Partially initialized arrays are a solved problem- anything more would just be baking `Vec` into the language. And backlinks are nowhere near "routine."
- nixpulvis 8y agoBacklinks ARE a very interesting point about Rust. Just because you don't find yourself using them all that often doesn't mean it's not interesting to the design and expresivity of the language. The issue is a direct consequence of Rust's strict aliasing rules, which I feel any programmer would benefit from groking.
- acjohnson55 8y agoThey're routine in CS classes. Curiously, I've never needed to use them in real life.
- namibj 8y agoklib's kbtree header/macro library seems to provide rather nice B-trees in C. https://github.com/attractivechaos/klib/blob/master/kbtree.h https://github.com/attractivechaos/klib/blob/master/kbtree.h
- kibwen 8y agoI think that TL;DR misses lots of nuance; I'd suggest a better TL;DR would be "the features and idioms of different languages can lead to different preferred solutions to the same problems, which means that we should be careful not to unnecessarily restrict our perception of new languages in terms of the familiar idioms of old ones." Or maybe: "performance benchmarks are hard, and even carefully-crafted comparisons can miss the point if they paper over important practical implementation concerns; an apples-to-apples comparison can unintentionally mislead if it precludes the consideration that perhaps what you actually wanted was an orange after all". Importantly, the article explains why Rust is faster on this specific benchmark, but goes out of its way to note that benchmarks should always be interpreted with appropriate caution, and that nobody should come away with the notion that this implies any sort of strict dominance of Rust over C.
- igouy 8y agoSo it's called "The relative performance of C and Rust" to put the emphasis on the data structures? :)
- kibwen 8y agoAs suggested in the introduction, one would think it was called "the relative performance of C and Rust" because this article is a corollary to a previous article and conceived as a response to an inquiry regarding that previous article--specifically, to answer the question "in your given benchmark, what explains the relative performance of C and Rust?" :P The basic answer: "data structures"; the insightful answer: "data structures that were originally chosen to play to the different strengths and weaknesses of their respective implementation languages, giving us food for thought as to how language design can shape the solutions that users of that language will tend to arrive at, and the performance implications thereof".
- igouy 8y agoObviously Why Rust BTreeMaps beat C AVL Trees is way too-long and misleading a title ;-)
- igouy 8y agoMight there be a C library that could be used? https://old.reddit.com/r/programming/comments/9jsqb3/the_relative_performance_of_c_and_rust/e6umut4/?st=jmo70uic&sh=d3f0d1cf https://old.reddit.com/r/programming/comments/9jsqb3/the_rel...
- nostrademons 8y agoNote that the data structure is different: AVL trees vs. BTrees. The interesting part for me was that because of modern cache hierarchies, BTrees appear to be superior than AVL trees even for in-memory data structures, while before I'd thought of them as a special-purpose on-disk data structure for databases.
- rdtsc 8y agoMakes total sense. The insight is that the memory acts like the disk used to and the caches act like the memory. BTrees worked well for spinning disk because they'd nicely load a whole array (node) of BTree child pointers in one disk read. But the same works for caches, they'd load the the whole array of into the cache when the first one is accessed. A silly idea I had, wonder if it would be possibly to auto-adjust the B factor of a BTree (the number of nodes) at runtime based the cache line size.
- fulafel 8y agoAuto adjusting would be interesting, even better (though harder) would be to do it experimentally rather than based on cache line size. Cache line size only determines the minimum block size that doesn't waste cache capacity, after all.
- klodolph 8y agoAVL trees were more competitive when memory was more shallow. These days an L1 cache hit is dirt cheap and a RAM fetch is ~200x as expensive, following a pointer creates a data dependency. B-trees give you more L1 cache hits and fewer pointers to follow. Back in the 1990s the cache hits and RAM fetches were much closer to parity and there weren't as many pipeline stages to fill so data dependencies weren't as important. Go back even farther and you have completely uniform memory that could be accessed in a fixed number of cycles no matter what the address. There are a lot of good books from these eras, and there are a lot of good books that teach with the uniform memory model. Most algorithms classes don't talk about the hierarchy at all (even though it distorts the true asymptotic runtime of many algorithms).
- saagarjha 8y agoThis comparison is disingenuous. They’re using a completely different data structure and getting a slight difference in performance, and are chalking this up to language differences. Sure, it might be harder to implement at B-Tree in C than it is in Rust. But then you can just use C++? Don’t make an apples to oranges comparison.
- dmpk2k 8y agoSuggestion: actually read the article. It's one of the few high-quality technical articles that pass through HN... if you don't get hung up on the title.
- saagarjha 8y agoPlease don’t do this here; I did read the article and this is my response to it. If you have a meaningful comment to make on my analysis, please reply with that instead.
- jerf 8y agoOK, read the article starting with the phrase "But that would be overlooking something important". Normally, "read the article" is a bit not-HN-friendly, but when you call someone out as disingenuous because they didn't say things that they in fact do clearly call out and have a multi-paragraph discussion of, you are earning a "please read the article" response. They're not being disingenuous about what they are doing if they clearly explain what that is and then have a (IMHO) balanced pro-and-con discussion about it.
- saagarjha 8y agoOk, sure, I’ll accept that calling the article itself disingenuous was a bit harsh. But it does seem like it sent the message that Rust was faster than C, when it is clearly not a fair comparison (as the author mentions themself). I’m actually relatively happy with that part, but it comes at the end and it’s clearly spawned discussion here that proves my point; look at the other comments: the other top-level one says that I should rewrite everything in Rust because it performed better here.
- ridiculous_fish 8y agoWhat is a scenario where you would consider C and also Rust? If Rust is in the mix, wouldn't you opt for C++ before C?
- lewisinc 8y agoC++ inheritance doesn't map well to Rust's trait system for one. I think this is an ongoing issue within the `bindgen` community, but I don't know much beyond that. Suffice to say, some concepts in C++ do not map well to Rust and so FFI can be hairy in some instances. On instance of this becoming a complication would be in the GTK+ world with the Rust bindings to GObject. There's some really good articles about their adventures in that realm you could find.
- deleted 8y ago[deleted]
- tomjakubowski 8y agoWriting an FFI for some C++ so that C code may call it takes almost as much work as doing the same for Rust. That's less true the closer your preferred dialect of C++ is to C, I suppose. Meanwhile Rust, in this user's opinion, offers a superior toolset for modeling data and building behavior, namely: sum types and traits. So if you're looking to extend or integrate with a C codebase, barring some exotic target platform and all else equal, I don't know why you'd opt for C++ over Rust.
- jcelerier 8y ago> Writing an FFI for some C++ so that C code may call it takes almost as much work as doing the same for Rust where "as much work" means putting `extern "C"` in front of your functions and having a few `static_assert(is_pod_v<type_that_i_pass_in_argument_to_my_c_api>)` or only using opaque typedefs, ie it frankly amounts to nothing
- flurrything 8y ago
- vnorilo 8y agoThis was a well-crafted benchmark, but what I found especially interesting and insightful was the "apples to oranges" part: C and Rust encourage distinct approaches, different data structures. I feel it is actually more interesting than implementing the same algorithm and comparing optimizer pipelines.
- cryptonector 8y agoI agree. Many of us like it about C that we can build intrusive data structures easily enough. The price is more complex memory management and locking protocols, but in some cases the price is too steep in actual run-time cost. The apples to oranges comparison is not telling about whether Rust produces better code than C, but it shows how Rust's design choices force themselves on the coder and yield better results in the end. When you stay away from unsafe code, Rust makes one write code that is much easier to reason about, especially in a threaded environment. Performance is nice, but the nicer thing is correctness -- being able to get good performance along with correctness, now that is really nice.
- TheDong 8y agoI think this is more a result of rust having good generics. If c were built with good generics and memory management (not that garbage preprocessor stuff), I expect similarly expressive and good data-structures would be built. C++ is an affirmation of this because it happens to have good libraries of data-structures which are not really very different from rust's. I don't think this is as much "rust's design forces the coder to do this" and more "c makes it hard to do as nice of a design"
- vnorilo 8y agoRight. In C, correctness is by convention, not enforced. C++ allows "userland" enforcement via RAII. There are, however, pieces of compiler magic partially outside the type system (const, &, &&) - you can't specify such behavior within C++. Rust does more of this magic in ownership. If you just want a particular instruction soup, all of them will get you there. It's a question of ergonomics, convenience and the kinds of compiler guarantees that we want.
- abenedic 8y agoAs a meta comment, I really like how active Stepha(e?)n is on internet forums. And the energy around Rust is pretty intense. I feel like in there, people are unwilling to try to embrace some of the nuance in the arguments that some people make against Rust. A person should understand that no language(programming or otherwise) is perfect. Therefore there is some criticism of any language that is valid. Therefore some criticism should be taken seriously as there may be a real point to the criticism. There are many posts like this, where there are comparisons of Rust to C. Realistically they are all a little biased towards Rust as Rust is a C++ replacement really. A more proper comparison would be Zig or D as a better C against C. Just understand that a person defending C is not always an idiot, and maybe they have point. Consider the excessive memory use of any working Rust compiler. That will probably not be remedied anytime soon and is a legitimate complaint. The ideal of how something could be is not how something is. The reality is that C works pretty well most of the time, Rust works well most of the time. They both fail at some things.
- vvanders 8y agoI don't think I've seen anyone claiming that Rust is a perfect language without flaws. Heck I'd hold up all of the huge strides that have been made in errors, usability and constant feedback as a counter example of that. You see a lot of people holding up Rust precisely because we've been there for the last 5/10/15/20 years. The day I don't have to write a makefile or build yet another CMakeLists.txt is the day I rejoice. What Rust offers is another option in the native, non-GC'd language space. A space that has very few languages and even fewer yet that are shipped at scale. Rusts inclusion w/ FF means that the have to address the robustness, security, performance and usability of the language to a degree that you don't commonly see. Having just blown 4+ hours today dealing with the linker on a mixed C/C++ project I don't really miss a lot of the baggage that comes with native development these days. Rust gives you the option of dropping down to that level while still preserving a set of sane, opinionated defaults that are pretty well thought out.
- kxyvr 8y agoSince you made a mention of CMake, does Rust really provide an alternative for building multilanguage projects? When I went to look for examples on how this is managed, it seemed horrendous. For example, some projects manage this by writing a program to download, unzip, and build the source: https://github.com/elrnv/ipopt-rs/blob/master/ipopt-sys/build.rs https://github.com/elrnv/ipopt-rs/blob/master/ipopt-sys/buil... Others just assume that the libraries are there, but they still require a program to compile them: https://github.com/cmr/openblas-src/blob/master/build.rs https://github.com/cmr/openblas-src/blob/master/build.rs As long as all of the dependencies are in Rust, things appear nice. At the same time, unless I'm missing something, it seems like crates manages multilanguage projects poorly. Though I have some major gripes with CMake, I've managed multilanguage projects mostly well. Is there a sane way outside of CMake to manage a multilanguage project with Rust?
- nixpulvis 8y agoFantastic read. Learned a few things about profiling I'm sure I'll use. Just one tiny gripe, don't use a blue and green next to each other in graphs if avoidable. I'm not even blue green colorblind, and I found them hard to distinguish. Thanks for the great read.
- solipsism 8y agoThanks for looking out for us colorblind people. But, funnily enough, there is no "blue green colorblind", and to this person with red-green colorblindness, the colors in the charts are incredibly easy to distinguish. Much easier that the typical colors chosen.
- Twirrim 8y agoThere is such a thing as blue-green colour blindness, though it's a little more unusual (I as understand it). My father is one of those lucky people. His father was completely colour blind. My father can see the two colours just fine, but if they're adjacent to each other and about the same kind of tone, they start to blur into grey. One particular example I remember growing up. He hated the board game "Game of Life" because there's all these greens (ground) and blues (rivers, lakes, ocean etc) right next to each other all over the board. It was not a pleasant experience to be looking at it.
- nixpulvis 8y agohttps://www.color-blindness.com/2007/05/18/mixing-up-blue-and-green/ https://www.color-blindness.com/2007/05/18/mixing-up-blue-an... (read the part titled "Mixing up blue and green") seems to say that people with color-blindness in general may have a harder time with blues and greens. This further supports my experience given that I'm not color blind and have trouble sometimes with these colors. Green and Blue are just really similar perceptively. All this said, you may be correct that there's no such thing as blue-green color-blind, and maybe that's not the best word to describe what I'm trying to say.
- mijoharas 8y agoI am not colourblind. I did find those colours difficult to distinguish next to one another.
- stewbrew 8y agoIsn't that to some extent also a benchmark of gcc vs llvm?
- stochastic_monk 8y agoThis is a benchmark comparing different data structures more than different programming languages. Of course the Rust program has fewer cache misses; this is because it’s a b-tree, not an AVL tree.
- kiriakasis 8y agoIt is a benchmark comparing what type of data structure are easier to use in the two languages. Of course it is entirely subjective (the author argues for how that is due to a static vs dynamic ownership model) and there is a single data point, which is the author rewriting the code. (two is you consider when he/she swaps one of the data structure)
- saagarjha 8y ago> It is a benchmark comparing what type of data structure are easier to use in the two languages. Then it's not a performance benchmark, it's a style and usability benchmark.
- quotemstr 8y agoThis is not really a good comparison: the author acknowledges that the C and Rust programs are using different data structures, and while he tries to vary the problem a bit to explore that difference, it's still not a fairly matched comparison, and the headline leaves the reader with the incorrect impression that we're comparing similar things. I'm not sure that comparing "Rust" and "C" performance is even meaningful. The latter is a multi-implementation language, and in most implementations, can be written in a way that gets the compiler to generate the same machine code that the Rust compiler does. (Frequently, they both just go through LLVM.)
- p0nce 8y agoAnd in this particular benchmark, they didn't choose the same compiler backend too. Namely GCC -O2 vs LLVM -O3. This has a large impact, often larger than the frontend language, when measuring programs in native languages.
- kiriakasis 8y agoThe fact that the two benchmarks are using different data structure is one of the main points of the a article. The author (in my opinion) argues that each data structure would be an hell to use in the other language. when comparing languages you need to include a measure of how idiomatic your programs are.
- FartyMcFarter 8y agoDid I read the article correctly that the C code was compiled with -O2 and not -O3? Why?
- wffurr 8y ago-O3 isn't always strictly faster than -O2. There are different tradeoffs made about memory usage and binary size which interact with CPU caches in interesting ways. I can't speak for the author but at my work we found -O2 to give better results on our OpenGL ES rendering engine.
- bowyakka 8y agocargo --release doe not compile at -O3 either it's at -O2
- steveklabnik 8y agoI believe that’s wrong. cargo build —release —verbose would tell you for sure; I’m not at a computer right now.
- toolslive 8y agoCould the lumpiness be caused by the overflow and subsequent split of the root of the BTree (which cascades all the way down)?
- UncleEntity 8y ago> The C version was compiled with GCC 7.3.0 with -O2 level optimizations; the Rust version was compiled with 1.29.0 with --release. Isn't rust based on LLVM? It would make more sense, for a true apples to apples comparison, to use clang instead of GCC. Not that I'm pro-C or anti-rust or anything but the beginning of the article goes on about previous tests not being fair.
- kiriakasis 8y agoIt also insist on how this data is unfair and only relevant to the system (comprehensive of OS, compilers, programmer skill/talent/interest/errors and so on) At the end the author never states that one is faster (yes, the title is misleading, but it is then clearly clarified in the text) the main part is the discussion about intrusive data structures.