gotchacppModerate
Why does iteration over an inclusive range generate longer assembly in Rust than in C++?
Viewed 0 times
iterationwhyinclusivethanlongerovergeneratedoesassemblyrust
Problem
These two loops are supposed to be equivalent in C++ and Rust:
let mut sum: u64 = 0;
for j in 0u64..=num {
sum += 1;
}
return sum;
}
`
However the C++ version generates a very terse assembly:
while Rust's version is very long with two checks in the loop instead of one:
Godbolt: https://godbolt.org/z/xYW94qxjK
What is Rust intrinsically trying to prevent that C++ is carefree about?
#include
std::uint64_t sum1(std::uint64_t n) {
std::uint64_t sum = 0;
for (std::uint64_t j = 0; j
pub fn sum1(num: u64) -> u64 {let mut sum: u64 = 0;
for j in 0u64..=num {
sum += 1;
}
return sum;
}
`
However the C++ version generates a very terse assembly:
sum1(unsigned long): # @sum1(unsigned long)
xor eax, eax
.LBB0_1: # =>This Inner Loop Header: Depth=1
add rax, 1
cmp rax, rdi
jbe .LBB0_1
retwhile Rust's version is very long with two checks in the loop instead of one:
example::sum1:
xor eax, eax
xor ecx, ecx
.LBB0_1:
mov rdx, rcx
cmp rcx, rdi
adc rcx, 0
add rax, 1
cmp rdx, rdi
jae .LBB0_3
cmp rcx, rdi
jbe .LBB0_1
.LBB0_3:
retGodbolt: https://godbolt.org/z/xYW94qxjK
What is Rust intrinsically trying to prevent that C++ is carefree about?
Solution
Overflow in the iterator state.
The C++ version will loop forever when given a large enough input:
The above program (slightly modified to avoid getting bogged down in debug versus release mode differences with respect to the summation) will terminate after exactly 18 446 744 073 709 551 616 iterations and print 0; the Rust version carefully maintains iterator state so that overflow does not happen in the iterator.
The C++ version will loop forever when given a large enough input:
#include
std::uint64_t sum1(std::uint64_t n) {
std::uint64_t sum = 0;
for (std::uint64_t j = 0; j
This is because when the loop counter j finally reaches 0xffffffff'ffffffff, incrementing it wraps around to 0, which means the loop invariant j use std::num::Wrapping;
fn sum1(num: u64) -> u64 {
let mut sum = Wrapping(0);
for _ in 0..=num {
sum += Wrapping(1);
}
return sum.0;
}
fn main() {
println!("{}", sum1(0xffffffff_ffffffff));
}
The above program (slightly modified to avoid getting bogged down in debug versus release mode differences with respect to the summation) will terminate after exactly 18 446 744 073 709 551 616 iterations and print 0; the Rust version carefully maintains iterator state so that overflow does not happen in the iterator.
Context
Stack Overflow Q#70672533, score: 47
Revisions (0)
No revisions yet.