【发布时间】:2019-07-07 12:47:56
【问题描述】:
我注意到一些我以前从未见过的非常奇怪的东西。此伪代码中描述了基本设置:
TARGET_LOOP_TIME = X
loop forever:
before = now()
payload()
payload_time = now() - before
sleep(TARGET_LOOP_TIME - payload_time)
这种设置相当普遍,例如将循环保持在 60 FPS。有趣的部分是:payload_time 取决于睡眠持续时间! 如果TARGET_LOOP_TIME 很高,程序将因此睡眠很多,payload_time 与程序相比要高得多根本不睡觉。
为了衡量这一点,我编写了这个程序:
use std::time::{Duration, Instant};
const ITERS: usize = 100;
fn main() {
// A dummy variable to prevent the compiler from removing the dummy prime
// code.
let mut x = 0;
// Iterate over different target loop times
for loop_time in (1..30).map(|n| Duration::from_millis(n)) {
let mut payload_duration = Duration::from_millis(0);
for _ in 0..ITERS {
let before = Instant::now();
x += count_primes(3_500);
let elapsed = before.elapsed();
payload_duration += elapsed;
// Sleep the remaining time
if loop_time > elapsed {
std::thread::sleep(loop_time - elapsed);
}
}
let avg_duration = payload_duration / ITERS as u32;
println!("loop_time {:.2?} \t=> {:.2?}", loop_time, avg_duration);
}
println!("{}", x);
}
/// Dummy function.
fn count_primes(up_to: u64) -> u64 {
(2..up_to)
.filter(|n| (2..n / 2).all(|d| n % d != 0))
.count() as u64
}
我迭代不同的目标循环时间来测试(1 毫秒到 30 毫秒)并多次迭代 ITERS。我用cargo run --release 编译了这个。在我的机器(Ubuntu)上,程序输出:
loop_time 1.00ms => 3.37ms
loop_time 2.00ms => 3.38ms
loop_time 3.00ms => 3.17ms
loop_time 4.00ms => 3.25ms
loop_time 5.00ms => 3.38ms
loop_time 6.00ms => 4.05ms
loop_time 7.00ms => 4.09ms
loop_time 8.00ms => 4.48ms
loop_time 9.00ms => 4.43ms
loop_time 10.00ms => 4.22ms
loop_time 11.00ms => 4.59ms
loop_time 12.00ms => 5.53ms
loop_time 13.00ms => 5.82ms
loop_time 14.00ms => 6.18ms
loop_time 15.00ms => 6.32ms
loop_time 16.00ms => 6.96ms
loop_time 17.00ms => 8.00ms
loop_time 18.00ms => 7.97ms
loop_time 19.00ms => 8.28ms
loop_time 20.00ms => 8.75ms
loop_time 21.00ms => 9.70ms
loop_time 22.00ms => 9.57ms
loop_time 23.00ms => 10.48ms
loop_time 24.00ms => 10.29ms
loop_time 25.00ms => 10.31ms
loop_time 26.00ms => 10.82ms
loop_time 27.00ms => 10.84ms
loop_time 28.00ms => 10.82ms
loop_time 29.00ms => 10.91ms
我绘制了这些数字的图(sleep_time 是 max(0, loop_time - avg_duration)):
当程序完全不休眠时,有效载荷大约需要 3.3 毫秒(如前三个测量结果所示)。一旦循环在有效载荷之后开始休眠,有效载荷持续时间就会增加!事实上,它会增加到大约 10.5 毫秒。睡眠时间更长并不会增加有效载荷时间。
为什么?为什么这段代码的执行时间取决于我之后(或之前)所做的事情?这对我来说没有意义!看起来 CPU 说“无论如何我都要睡觉了,所以让我们慢慢来”。我考虑了缓存效果,尤其是指令缓存,但是从主存加载指令数据不需要 7ms!这里发生了其他事情!
有没有办法解决这个问题? IE。让有效载荷尽可能快地执行而不考虑睡眠时间?
【问题讨论】:
-
编译器不能优化
count_primes吗?它似乎是一个纯函数,每次迭代都使用相同的参数调用。还应该可以在编译时计算一次 x。 -
@Jens 说得好。它似乎没有优化,但是,是的,我应该修复它。
-
请注意:这似乎是由于 CPU 频率缩放造成的,并且并非在所有机器上都会发生。然而,这仍然不是一个完整的答案,我希望有人可以提供更多信息。
-
嗯?
payload_duration += elapsed;
标签: performance rust