【问题标题】:Why is what timers show so counter-intuitive?为什么计时器显示的内容如此违反直觉?
【发布时间】:2020-03-21 12:01:02
【问题描述】:

我正在解析一些文本。我需要支持 unicode 文本,这就是我使用 String::chars 迭代器的原因:

playground

use std::time::Instant;

fn main() {
    let text = "a".repeat(10000);
    let mut timer1 = 0;
    let mut timer2 = 0;

    let start1 = Instant::now();
    for pos in 1..10000 {
        let start2 = Instant::now();
        let ch = text.chars().nth(pos).unwrap();
        timer2 += start2.elapsed().as_millis();
    }
    timer1 += start1.elapsed().as_millis();

    println!("timer1: {} timer2: {}", timer1, timer2);
}

示例输出:

timer1: 4276 timer2: 133

为什么timer2timer1 少得令人难以置信,而我认为它们应该彼此非常接近?

附:我已经知道.nth 很慢,不应该使用。

【问题讨论】:

  • 每次循环迭代都将在不到一毫秒的时间内运行(在执行.chars().enumerate() 时)。 as_millis 返回一个整数,因此任何小于一毫秒的都被舍入为 0。尝试改用 as_nanos...
  • @kazemakase 天哪。你是对的。我有很多其他事情要考虑,所以我看不出这个简单的原因......谢谢!

标签: performance rust


【解决方案1】:

您遇到了分辨率问题。循环内部的执行时间(平均)不到一毫秒,因此start2.elapsed().as_millis() 通常计算为 0。要解决此问题,您可以在循环内执行一些需要更长时间的操作,或者将分辨率从毫秒更改为更小的东西,比如微秒或纳秒。

切换到微秒会产生更一致的时间

use std::time::{Instant};

fn main() {
    let text = "a".repeat(10000);
    let mut timer1 = 0;
    let mut timer2 = 0;

    let start1 = Instant::now();
    for pos in 1..10000 {
        let start2 = Instant::now();
        let ch = text.chars().nth(pos).unwrap();
        timer2+=start2.elapsed().as_micros();
    }
    timer1+=start1.elapsed().as_micros();

    println!("timer1: {} timer2: {}", timer1, timer2);
}

输出

timer1: 3511812 timer2: 3499669

这个问题被标记为性能,所以我想指出,使用std::Instant 是衡量性能的一种非常繁琐的方法。更好的方法包括criterion.rsflamegraphcargo-bench

【讨论】:

  • 有一刻我认为这个答案是一种不费吹灰之力的偷窃,但你知道吗,你附加的那些链接对像我这样的 Rust 初学者非常有用!谢谢!
  • 我向和我说同样的话的评论者道歉,但我在写这篇文章时没有看到他们的评论。如果他们发布了答案,我会收到通知并且知道。
  • 我发表了评论,因为我没有时间说出正确的答案。由于其他人花了那个时间,所以他们得到赞扬和赞成是公平的。在这里,有我的:)
猜你喜欢
  • 1970-01-01
  • 2017-07-22
  • 1970-01-01
  • 1970-01-01
  • 2015-04-12
  • 1970-01-01
  • 2018-12-16
  • 1970-01-01
  • 2020-12-26
相关资源
最近更新 更多