HiveBrain v1.2.0
Get Started
← Back to all entries
gotchacppModerate

Why does iteration over an inclusive range generate longer assembly in Rust than in C++?

Submitted by: @import:stackoverflow-api··
0
Viewed 0 times
iterationwhyinclusivethanlongerovergeneratedoesassemblyrust

Problem

These two loops are supposed to be equivalent in C++ and Rust:
#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
        ret


while 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:
        ret


Godbolt: 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:
#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.