5 ms·
Most insightful part for me was this one: > Many variations [he's talking about Rust] that seemed like they ought to run the same speed or faster turned out sl
by zvrba 11y ago
Most insightful part for me was this one:
> Many variations [he's talking about Rust] that seemed like they ought to run the same speed or faster turned out slower, often much slower. By contrast, in C++ it was surprisingly difficult to discover a way to express the same operations differently and get a different run time.
This is an insidious pitfall that may earn Rust the "slow" label, akin to what happened to Common Lisp.
- infinity0 11y agoYeah, I wish he had gone through this in more detail, too.
- humanrebar 11y ago> This is an insidious pitfall that may earn Rust the "slow" label, akin to what happened to Common Lisp. Could it just be that the language is still young and all the edge cases in the optimizer haven't been implemented yet?
- steveklabnik 11y agoNot just that, but there's a lot of optimizations that would be nice, but we haven't implemented yet. (Though some of these will end up being more to help with compile times than runtime performance, though)
- imh 11y agoI thought Rust left the optimization to LLVM. Is that not the case?
- dikaiosune 11y agoAs I understand it, there are a number of potential optimizations based on Rust's implicit understanding of the lifetime of a memory region. LLVM doesn't know about those because they can't be represented in the IR. Last I heard, any kind of optimization work based on the extra information rustc has is waiting for the compiler refactor that's currently underway.
- pcwalton 11y agoNote that there aren't any optimizations that C++ can do that Rust can't in this area.
- santaclaus 11y agoCan Rust's aliasing rules lead to extra avenues of optimization compared C++ (along the lines of C vs. Fortran)?
- pcwalton 11y agoQuite possibly yes.
- pcwalton 11y agoLLVM is great, and it's the reason why Rust is able to match C++ in performance on this workload, but it's not a magic-optimizer-of-everything: it's tuned to C and C++. We have added a couple of Rust-specific optimizations to it, but not a whole lot. I suspect what the author was seeing was random performance differentials between iterators and loops, which often boils down to little missed optimizations in LLVM. If you find them, please file them in the Rust bug tracker--we've fixed many of them and will continue to do so in the future! Note that when MIR lands, progress on which is well underway, we'll have the ability to do optimizations at the Rust IR level in addition to the LLVM level. This should make it easier to do these kinds of things.
- elcritch 11y agoFigured this might be the case. LLVM is great but C++ compilers have had a long time to mature. It's impressive that the filter and map methods produced such good performance. The lazy evaluation scheme must be well setup, shout out to the Rust team! Are the lazy iterators are combined and effectively stripped out at compile time?
- pcwalton 11y agoYes.
- hellofunk 11y agoThis is a problem in Haskell as well, from everything I've read.
- koloron 11y agoCan you share some links?
- contravariant 11y agoOne particular example that I recall was that if you calculate something like foldl (+) 0 [1..1000000] (i.e. calculate the sum of the first 1000000 natural numbers) Then the obvious way to do it is to simply keep a running total, but haskell doesn't do that, it constructs the expression: (...((1+2)+3)+4)+5)+...)+1000000) And tries to evaluate that, which tends to cause stack overflows, and is just generally slow. To fix it you have to use the following expression instead: foldl' (+) 0 [1..1000000] This problem is described in more detail here: https://wiki.haskell.org/Foldr_Foldl_Foldl' https://wiki.haskell.org/Foldr_Foldl_Foldl'
- hellofunk 11y agoAdding just a single tick mark to make a big difference in performance is one of many areas in Haskell that make performance-minded development a pain. With these high-level languages, you really have to understand the implementation of the language to get anything fast written.
- hellofunk 11y agoAnother discussion today: https://news.ycombinator.com/item?id=11060257 https://news.ycombinator.com/item?id=11060257 for example: https://news.ycombinator.com/item?id=11060566 https://news.ycombinator.com/item?id=11060566
- _yosefk 11y agoI dunno, it's very easy for me to express things differently in C++ and get a different runtime. It's easy enough for a bunch of C++ programmers writing needlessly slow code whom I've known. It must be equally easy for the author. The reason he doesn't ought to be vast experience with C++, so that he doesn't try obviously dumb things (as in things that malloc a ton of stuff needlessly, etc.), whereas his Rust experience is necessarily smaller. I think you need quite a bit of experience to declare that, well, experience just doesn't help because the performance model of this thing is weird!
- CyberDildonics 11y agoI would never call software good to go until I have profiled it to weed out all the places where a slow technique meets an important path. In my experience profiling and optimization is usually sort of like a victory lap - low hanging fruit for substantial differences.
- pcwalton 11y ago> This is an insidious pitfall that may earn Rust the "slow" label, akin to what happened to Common Lisp. This doesn't match my experience at all with Rust, for what it's worth. Not having copy constructors makes up for any difference in that regard: in C++ it's way too easy to accidentally deep copy a vector and the entire object graph underneath it--all it takes is "auto a = b" as opposed to "auto& a = b"--with very bad performance consequences. Rust, on the other hand, doesn't let you deep copy unless you explicitly ask it to.
- Manishearth 11y agoWhen I went back to C++ after learning Rust and used a vector again, I realized this and it hit me like a brick. I used to program in C++ before, so it wasn't like I didn't know this, but it was something rather unremarkable to me. Then I go back to C++ and throw vectors around willy-nilly and stuff works. Huh, I thought, this feels strange, I wonder how C++ accomplishes this without a garbage collector or ownership. Then it hit me -- it deep copies All The Things. Again, I already knew this, but I hadn't ever thought about it much. But the change in perspective made it stick out like a sore thumb.
- deleted 11y ago[deleted]
- yongjik 11y agoI got so worried about accidentally deep-copying stuff that I ended up with an ugly C++ hack: C(const C &) = delete; C &operator=(const C &) = delete; C(C &&) noexcept = default; C &operator=(C &&) noexcept = default; struct MakeCopy { const C &src; MakeCopy(const C &src) : src(src) { } }; MakeCopy make_copy() const { return *this; } C(MakeCopy src) : field1(src.src.field1), (blah blah) { } Then, when you really want to copy stuff, you have to say "a = b.make_copy();". This isn't really clean, because it's too easy to add a field to C and then forget to add it to the "manual copy constructor", but so far I found it good enough for me.
- dikaiosune 11y agoIn Rust, if you want a deep copy you (IIRC) implement the Clone trait, which allows you to explicitly clone everything. Many collections in the standard library already implement this, so you get it by default :). Edit: I should point out that deep copies can only be explicit in Rust -- there's no implicit deep copy AFAIK.
- bsder 11y agoI laughed at this. I STILL write "++foo;" rather than "foo++;" as a default because C++ used to create copies at odd places.