5 ms·
I don’t feel like this gave a satisfactory answer the question. Since everything was inlined, the argument passing convention made no difference in the micro be
by celeritascelery 4y ago
I don’t feel like this gave a satisfactory answer the question. Since everything was inlined, the argument passing convention made no difference in the micro benchmarks. But what happens when it does not inline? Then you would actually be testing by-borrow be by-copy instead of how good rust is at optimizing.
- furyofantares 4y agoI feel like they got excited by their C++ code being so much slower and curious about the "weird" C++ result and forgot to figure out the original question.
- fnordpiglet 4y agoTo be fair I got excited too. But I still want to know the answer as well.
- FpUser 4y ago>"C++ code being so much slower" Rust - Windows - By-Copy: 14124, By-Borrow: 8150 C++ - Windows MS Compiler - By-Copy: 12160, By-Ref: 11423 C++ - Windows LLVM 15 - By-Copy: 4397, By-Ref: 4396 Delta: -0.0227428% So it appears that C++ - Windows LLVM 15 beats Rust by large margin.
- jasonhansel 4y agoIn Rust it's considered idiomatic to pass things by-value whenever you can. Usually this is also the most performant option, since it avoids dereferencing in the callee. Of course, if your struct is truly enormous, you may want to break this rule to avoid large copies. But in that case you probably want to Box<T> the struct anyway. Of course, if your struct contains something that can't be copied--like a Vec<T>--you'll have to decide whether to clone the whole struct (and thus the vector in it), pass the struct by-borrow, or find some other solution.
- brundolf 4y agoI don't think I'd agree that idioms come into play here, one way or the other. Safely borrowing things by reference is one of Rust's headline features
- kibwen 4y ago> Safely borrowing things by reference is one of Rust's headline features Sure, but it's worth noting that references in Rust do not exist merely to avoid passing by-value. They also exist to make it easier to deal with Rust's ownership semantics: they let you pass things to a function without also requiring the function to "pass back" those things as returned values. In other words, references let you do `fn foo(x: &Bar)` rather than `fn foo(x: Bar) -> Bar`. This is a unique and interesting consequence of languages with by-default move semantics.
- int_19h 4y agoWhen the compiler can see both the callee and the caller (the most common case), why should it even matter? If you pass by value, but don't actually do anything that mutates the copy, and there are no aliasing concerns, surely the compiler can make it by-ref under the hood? (The other direction is trickier, since by-ref implies the desire to observe aliasing, even though that's usually not expected in practice - but the compiler cannot tell.)
- ncallaway 4y agoI sort of agree and sort of disagree. > Then you would actually be testing by-borrow be by-copy instead of how good rust is at optimizing. I don’t think the question is actually: “what is faster in practice, a by-copy method call or a by-value method call”, I think the question is: “as an implementer, which semantics should I choose when I’m writing my function”. For the second question: “Rust is usually pretty good at aggressively inlining, so… if you’re willing to trust Rust’s compiler, you’re often okay going with by-copy implementations, but you should keep an eye on it”. Whereas, as you note, for the first question it’s not an answer. But, I do think if someone was going to put more work into it I’d be very curious what the answer to the first question is. If I’m choosing to implement with by-copy semantics and trusting the Rust compiler to hopefully inline things for me, I’d like to know the implications in the cases when it doesn’t.
- forrestthewoods 4y agoBlog author here. This feels like the best summary in this comment section. The root question is indeed “what semantics should I use”. And the answer I came up with was “the compiler does a lot of magic so by-copy seems pretty good”. I agree with the previous commenter this is not a satisfying conclusion! My experience with Rust is that it requires a moderate amount of trust in the compiler. Iterator code is another example where the compiler should produce near optimal code. Emphasis on should!
- FpUser 4y agoWhen value size is small (whatever "small" means for particular architecture) I'd say "trust the compiler" suggestion is reasonable. When the size grows there should be no more "trust" unless compiler can decipher if it is safe to use ref instead of value basing on value size (we assume that the function does not mutate the value). Your tests on my PC: Rust - By-Copy: 14124, By-Borrow: 8150 C++ - By-Copy: 12160, By-Ref: 11423 P.S. Just built it using LLVM under CLion IDE and the results are: G:\temp\cpp\rust-cpp-bench\cpp\cmake\cmake-build- release\fts_cmake_cpp_bench.exe Totals: Overlaps: 220384338 By-Copy: 4397 By-Ref: 4396 Delta: -0.0227428% Process finished with exit code 0