Repository: rust
Issue: Loop unrolled on `x86_64` and `ppc64el` but not on `aarch64`, leading to poor performance
Labels: A-LLVM,I-slow,T-compiler,C-bug

I tried this code:

```rust
#[inline(never)] // so that we can easily view the assembly
fn fibonacci_vec(length: usize) -> Vec<u64> {
    let mut fib = vec![0; length];
    {
        // Unlike a `Vec`, a slice is not resizable
        let fib = fib.as_mut_slice();
        if length > 1 {
            fib[1] = 1;
        }
        if length > 2 {
            // The compiler now knows that `fib` is fixed-size
            // and we are iterating exactly up to its length
            for i in 2..fib.len() {
                fib[i] = fib[i-1] + fib[i-2]; // no more bounds checks!
            }
        }
    }

    fib
}

pub fn main() {
    // read the length at runtime so that the compiler can't just precompute Fibonacci
    let arg = std::env::args().nth(1).expect("Please specify length");
    let length: usize = arg.parse().expect("That's not a number!");
    // actually call the function we care about
    let fibonacci = fibonacci_vec(length);
    // and print the result so that the compiler doesn't remove it as dead code
    println!("{:?}", fibonacci.last());
}
```

I expected loop unrolling to work uniformly on all 64-bit little-endian targets. 

But on `aarch64` the loop with the line ` fib[i] = fib[i-1] + fib[i-2]; ` is not getting unrolled, leading to significantly worse performance.

### Assembly

I've inspected the assembly attributed to the ` fib[i] = fib[i-1] + fib[i-2];` line with https://github.com/pacak/cargo-show-asm to confirm. You can reproduce the results using:

```bash
cargo install cargo-show-asm
git clone https://github.com/Shnatsel/bounds-check-cookbook
cd bounds-check-cookbook
cargo asm --rust --bin fibvec_clever_indexing fibonacci_vec
```

Results on rustc 1.66 stable:

 - **x86_64:** https://gist.github.com/Shnatsel/5ae855298d8f35e9a9a513b044d8e294
 - **ppc64el:** https://gist.github.com/Shnatsel/862cbed11b3d88fcea31db1633433b05
 - **aarch64 (not unrolled):** https://gist.github.com/Shnatsel/9fcd5b220b8852531e3b0298c29912f4

### Benchmarks

I've benchmarked each implementation against a [naive version with bounds checks](https://github.com/Shnatsel/bounds-check-cookbook/blob/4dc0630fff4fca865f6f34ffef2cfc8b70db1d8e/src/bin/comparison_naive.rs). To reproduce:

```bash
cargo install hyperfine
git clone https://github.com/Shnatsel/bounds-check-cookbook
cd bounds-check-cookbook
cargo build --release
hyperfine 'target/release/fibvec_naive_indexing 1000000000' 'target/release/fibvec_clever_indexing 1000000000'
```

#### x86_64

measured on a Zen+ desktop CPU

```
$ hyperfine 'target/release/fibvec_naive_indexing 1000000000' 'target/release/fibvec_clever_indexing 1000000000'

Benchmark 1: target/release/fibvec_naive_indexing 1000000000
  Time (mean ± σ):      3.612 s ±  0.040 s    [User: 1.435 s, System: 2.132 s]
  Range (min … max):    3.546 s …  3.693 s    10 runs
 
Benchmark 2: target/release/fibvec_clever_indexing 1000000000
  Time (mean ± σ):      3.133 s ±  0.019 s    [User: 0.995 s, System: 2.103 s]
  Range (min … max):    3.106 s …  3.163 s    10 runs
 
Summary
  'target/release/fibvec_clever_indexing 1000000000' ran
    1.15 ± 0.01 times faster than 'target/release/fibvec_naive_indexing 1000000000'
```

#### ppc64el

measured on a POWER9 system from IntegriCloud

massive improvement, correlated with unrolling the loop more than on x86_64

```
$ hyperfine 'target/release/fibvec_naive_indexing 1000000000' 'target/release/fibvec_clever_indexing 1000000000'
Benchmark 1: target/release/fibvec_naive_indexing 1000000000
  Time (mean ± σ):      2.853 s ±  0.052 s    [User: 2.149 s, System: 0.694 s]
  Range (min … max):    2.810 s …  2.980 s    10 runs
 
Benchmark 2: target/release/fibvec_clever_indexing 1000000000
  Time (mean ± σ):      1.604 s ±  0.017 s    [User: 0.919 s, System: 0.681 s]
  Range (min … max):    1.586 s …  1.636 s    10 runs
 
Summary
  'target/release/fibvec_clever_indexing 1000000000' ran
    1.78 ± 0.04 times faster than 'target/release/fibvec_naive_indexing 1000000000'
```

#### Aarch64

Measured on an Ampere Altra VM from Google Cloud 

very small improvement due to lack of loop unrolling

```
$ hyperfine 'target/release/fibvec_naive_indexing 1000000000' 'target/release/fibvec_clever_indexing 1000000000' 
Benchmark 1: target/release/fibvec_naive_indexing 1000000000
  Time (mean ± σ):      3.320 s ±  0.024 s    [User: 1.131 s, System: 2.179 s]
  Range (min … max):    3.263 s …  3.346 s    10 runs
 
Benchmark 2: target/release/fibvec_clever_indexing 1000000000
  Time (mean ± σ):      3.226 s ±  0.019 s    [User: 1.092 s, System: 2.127 s]
  Range (min … max):    3.209 s …  3.263 s    10 runs
 
Summary
  'target/release/fibvec_clever_indexing 1000000000' ran
    1.03 ± 0.01 times faster than 'target/release/fibvec_naive_indexing 1000000000'
```

### Meta

Tested on rustc 1.66 stable installed from Rustup. All code built on the target natively, no cross-compilation. 

The exact targets used are  `aarch64-unknown-linux-gnu`, `x86_64-unknown-linux-gnu` and `powerpc64le-unknown-linux-gnu`.