Repository: rust
Issue: Strange cycles perfomance with generators and very slow `for` in `debug` build [on nightly]
Labels: I-slow,C-enhancement,T-compiler,A-coroutines

Having following simple benchmark code:
<details><summary> Very large code </summary>

```rust
#![feature(generators, generator_trait)]
#![feature(trait_alias)]
#![allow(while_true)]
#![allow(clippy::print_literal)]

use std::marker::PhantomData;
use std::ops::{Add, Generator, GeneratorState::*};
use std::pin::Pin;

trait FibValue<T> = Add<Output = T> + From<u8> + Copy;

struct Fibs<T: FibValue<T>, G: Generator<Yield = T, Return = !> + Unpin>(
    Pin<Box<G>>,
    PhantomData<T>,
);

impl<T: FibValue<T>, G: Generator<Yield = T, Return = !> + Unpin> Iterator for Fibs<T, G> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        if let Yielded(element) = self.0.as_mut().resume() {
            element.into()
        } else {
            None
        }
    }
}

fn create_fibs_for_gen<T: FibValue<T>>() -> Fibs<T, impl Generator<Yield = T, Return = !> + Unpin> {
    Fibs(
        Box::pin(|| {
            let mut previous: T = 0.into();
            let mut current: T = 1.into();

            yield previous;
            yield current;

            for _ in 0usize.. {
                let element = current + previous;

                previous = current;
                current = element;

                yield element
            }

            unreachable!()
        }),
        PhantomData,
    )
}

fn create_fibs_loop_gen<T: FibValue<T>>() -> Fibs<T, impl Generator<Yield = T, Return = !> + Unpin>
{
    Fibs(
        Box::pin(|| {
            let mut previous: T = 0.into();
            let mut current: T = 1.into();

            yield previous;
            yield current;

            loop {
                let element = current + previous;

                previous = current;
                current = element;

                yield element
            }
        }),
        PhantomData,
    )
}

fn create_fibs_while_gen<T: FibValue<T>>() -> Fibs<T, impl Generator<Yield = T, Return = !> + Unpin>
{
    Fibs(
        Box::pin(|| {
            let mut previous: T = 0.into();
            let mut current: T = 1.into();

            yield previous;
            yield current;

            while true {
                let element = current + previous;

                previous = current;
                current = element;

                yield element
            }

            unreachable!()
        }),
        PhantomData,
    )
}

fn generate_fibs_loop<T: FibValue<T>>(result: &mut Vec<T>) {
    let fib_count = result.capacity();
    let mut previous: T = 0.into();
    let mut current: T = 1.into();
    let mut num = 2;

    if fib_count > 0 {
        result.push(previous);
        if fib_count > 1 {
            result.push(current);
        }
    }

    loop {
        if fib_count <= num {
            break;
        } else {
            num += 1;
        }

        let element = current + previous;

        previous = current;
        current = element;

        result.push(element);
    }
}

fn generate_fibs_for<T: FibValue<T>>(result: &mut Vec<T>) {
    let fib_count = result.capacity();
    let mut previous: T = 0.into();
    let mut current: T = 1.into();

    if fib_count > 0 {
        result.push(previous);
        if fib_count > 1 {
            result.push(current);
        }
    }

    for _ in 2..fib_count {
        let element = current + previous;

        previous = current;
        current = element;

        result.push(element);
    }
}

fn generate_fibs_while<T: FibValue<T>>(result: &mut Vec<T>) {
    let fib_count = result.capacity();
    let mut previous: T = 0.into();
    let mut current: T = 1.into();
    let mut num = 2;

    if fib_count > 0 {
        result.push(previous);
        if fib_count > 1 {
            result.push(current);
        }
    }

    while num < fib_count {
        num += 1;

        let element = current + previous;

        previous = current;
        current = element;

        result.push(element);
    }
}

fn main() {
    let mut fibs = Vec::with_capacity(10000);
    let mut start_time = std::time::Instant::now();
    generate_fibs_loop::<f64>(&mut fibs);
    println!(
        "{} => {:?}, total count: {}, test value: {}",
        "fib via `loop`!",
        std::time::Instant::now() - start_time,
        fibs.len(),
        fibs[500].log(std::f64::consts::PI)
    );

    fibs = Vec::with_capacity(10000);
    start_time = std::time::Instant::now();
    generate_fibs_for::<f64>(&mut fibs);
    println!(
        "{} => {:?}, total count: {}, test value: {}",
        "fib via `for`!",
        std::time::Instant::now() - start_time,
        fibs.len(),
        fibs[500].log(std::f64::consts::PI)
    );

    fibs = Vec::with_capacity(10000);
    start_time = std::time::Instant::now();
    generate_fibs_while::<f64>(&mut fibs);
    println!(
        "{} => {:?}, total count: {}, test value: {}",
        "fib via `while`!",
        std::time::Instant::now() - start_time,
        fibs.len(),
        fibs[500].log(std::f64::consts::PI)
    );
    
    start_time = std::time::Instant::now();
    fibs = create_fibs_loop_gen::<f64>()
        .take(10000)
        .collect::<Vec<f64>>();
    println!(
        "{} => {:?}, total count: {}, test value: {}",
        "fib via `loop` generator!",
        std::time::Instant::now() - start_time,
        fibs.len(),
        fibs[500].log(std::f64::consts::PI)
    );

    start_time = std::time::Instant::now();
    fibs = create_fibs_for_gen::<f64>()
        .take(10000)
        .collect::<Vec<f64>>();
    println!(
        "{} => {:?}, total count: {}, test value: {}",
        "fib via `for` generator!",
        std::time::Instant::now() - start_time,
        fibs.len(),
        fibs[500].log(std::f64::consts::PI),
    );

    start_time = std::time::Instant::now();
    fibs = create_fibs_while_gen::<f64>()
        .take(10000)
        .collect::<Vec<f64>>();
    println!(
        "{} => {:?}, total count: {}, test value: {}",
        "fib via `while` generator!",
        std::time::Instant::now() - start_time,
        fibs.len(),
        fibs[500].log(std::f64::consts::PI),
    );
}
```

</details>

[**If increase benchmark number for every case, median time difference will become less significant**]

But for 1 iteration I got very strange results (with many recompilations and reruns, results are approx. the same): 
- Debug on my machine:
```
fib via `loop`! => 283.689µs, total count: 10000, test value: 209.48277540220764
fib via `for`! => 1.037341ms, total count: 10000, test value: 209.48277540220764
fib via `while`! => 363.379µs, total count: 10000, test value: 209.48277540220764
fib via `loop` generator! => 911.265µs, total count: 10000, test value: 209.48277540220764
fib via `for` generator! => 1.331597ms, total count: 10000, test value: 209.48277540220764
fib via `while` generator! => 897.928µs, total count: 10000, test value: 209.48277540220764
```
- Debug on playground:
```
fib via `loop`! => 518.857µs, total count: 10000, test value: 209.48277540220764
fib via `for`! => 1.706352ms, total count: 10000, test value: 209.48277540220764
fib via `while`! => 621.504µs, total count: 10000, test value: 209.48277540220764
fib via `loop` generator! => 1.53625ms, total count: 10000, test value: 209.48277540220764
fib via `for` generator! => 2.30295ms, total count: 10000, test value: 209.48277540220764
fib via `while` generator! => 1.276106ms, total count: 10000, test value: 209.48277540220764
```
Cycle `for` is **up to 3 times slower**
- Release on my machine:
```
fib via `loop`! => 67.293µs, total count: 10000, test value: 209.48277540220764
fib via `for`! => 60.185µs, total count: 10000, test value: 209.48277540220764
fib via `while`! => 17.95µs, total count: 10000, test value: 209.48277540220764
fib via `loop` generator! => 55.928µs, total count: 10000, test value: 209.48277540220764
fib via `for` generator! => 116.994µs, total count: 10000, test value: 209.48277540220764
fib via `while` generator! => 48.887µs, total count: 10000, test value: 209.48277540220764
```
- Release on playground:
```
fib via `loop`! => 60.817µs, total count: 10000, test value: 209.48277540220764
fib via `for`! => 67.948µs, total count: 10000, test value: 209.48277540220764
fib via `while`! => 28.903µs, total count: 10000, test value: 209.48277540220764
fib via `loop` generator! => 46.099µs, total count: 10000, test value: 209.48277540220764
fib via `for` generator! => 158.023µs, total count: 10000, test value: 209.48277540220764
fib via `while` generator! => 108.421µs, total count: 10000, test value: 209.48277540220764
```
`for` and `loop` are slower for some reasons. Also I know that generators aren't stable yet but they already work **very fast** and also are very convenient to use for some purposes. However this behaviour looks strange: `for` in generators is **up to 3 times** less performant than `loop` and `while true` (which's antipattern).

Is this my fault or something wrong with the cycles compilation? I'm very scared... Thanks!

[Playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=b4c2be76169f0f6dc03871055ada362b)