5 ms·
I would be really interested to know where the overhead in the simple rust version comes from. At first I thought it might be the println() being less efficient
by orf 8y ago
I would be really interested to know where the overhead in the simple rust version comes from. At first I thought it might be the println() being less efficient for whatever reason, but even without that line this[1] takes 300ms, compared to 184ms for this[2] (with the printf!).
1. https://raw.githubusercontent.com/atilaneves/pythagoras/master/simple.rs https://raw.githubusercontent.com/atilaneves/pythagoras/mast...
2. https://raw.githubusercontent.com/atilaneves/pythagoras/master/simple.cpp https://raw.githubusercontent.com/atilaneves/pythagoras/mast...
- cjhanks 8y agoPerhaps the terminal is linebuffered. (edit: I forgot that `!` was a character in Rust, so I mis-read your comment, oh well).
- trishume 8y agoI looked at the disassembly of both of those in https://godbolt.org/ https://godbolt.org/ and it looks like for some reason Rust puts the loop variables x,y and z on the stack and the loads them off the stack. This causes a bunch of L1 hits instead of register hits in the tight loop, causing the 2x slowdown. I have no idea why LLVM misses optimizing this in Rust's case. Likely it is fed much trickier IR. It also misses the optimization that it does in the C case of lifting the xx and zz multiplications to the appropriate loop level.
- the_duke 8y agoSomeone found a single character change providing a 2x speedup: https://www.reddit.com/r/rust/comments/ab7hsi/comparing_pythagorean_triples_in_c_d_and_rust/ https://www.reddit.com/r/rust/comments/ab7hsi/comparing_pyth...
- orf 8y agoAhh yeah, using `..(z + 1)` makes it run faster than the C++ version.
- kzrdude 8y agoIt's code quality problems with the inclusive-end range iterator.
- bluejekyll 8y agoTo quote that reddit post, which I think has some good interesting details about why: “I believe the problem with ..= is that x..(y + 1) is not identical with x..=y. They are nearly equivalent on most values for x and y, except when y == i32::max(). At that point, x..=y should still be able to function, while x..(y + 1) is allowed to do nothing.”
- SlowRobotAhead 8y agoI don't know anything about Rust, but is it part of it's "safety" that it has operators to allow rollover of type max size and others that do not? As a C programmer, that sort of feels like cheating ;)
- steveklabnik 8y agoSafety means "memory safety". Integer overflow cannot cause memory unsafety directly, and so it's safe. (safe) Rust does not claim to make your code correct, it claims that it will not have undefined behavior. We do care about helping you make your code correct; it will check for overflow and panic at runtime in debug builds. But it's a secondary concern. Memory safety is hard enough!
- ekidd 8y agoI think you've seen this comment about integer overflow optimizations? https://www.reddit.com/r/rust/comments/ab7hsi/comparing_pythagorean_triples_in_c_d_and_rust/ecy5pv7/?st=jqchvb9f&sh=0b2616c6 https://www.reddit.com/r/rust/comments/ab7hsi/comparing_pyth... But I also note that the original version of your benchmark was calling `println!`, which means that you were essentially benchmarking high-level, thread-safe I/O. EDIT: Not in this case, see the thread below. Some Rust benchmarking tips: 1. Always run `cargo build --release`. Debug builds are slow. 2. Be sure to use `stdin.lock()` or `stdout.lock()` when reading or writing standard I/O. If you don't, Rust has to lock standard I/O for you on each call. This means using `writeln!` instead of `println!`.
- burntsushi 8y ago> But I also note that the original version of your benchmark was calling `println!`, which means that you were essentially benchmarking high-level, thread-safe I/O. I don't think so. The program is only doing a thousand prints, which is far less than the total number of loop iterations. Try changing to locked stdout and writeln!---I doubt you'll notice a difference.
- lambda 8y agoYeah, I tried the locking version and didn't see a noticeable improvement.
- ekidd 8y agoThank you for checking this, and determining it didn't actually apply in this case. We do a lot of I/O intensive Rust at work, and I have a list of things to double-check when benchmarking. The most common slowdowns: debug builds, unlocked stdio, line iterators, and anything else which allocates in an inner loop. If I can hit 250 MB/s throughput per core on my laptop, with some light data processing, I'm usually quite happy.
- fanf2 8y agoI think the problem is flushing, not locking. Rust println!() flushes after each call, whereas more C-like io libraries flush once on program exit. Big difference in the number of system calls.