如何防止这种“重播缓存”行为?

如何防止这种“重播缓存”行为?

我有这个 C89 小程序——它的作用、工作原理以及未定义的行为并不重要,只是它仅在一个线程上执行一些长内存和数学运算:

x, c, * b, m, t, i, a;

g (n) {

  for (b = malloc(0); c < n; b[c - 1] = x++, t = 1) {
    char s[9];
    for (i = m = 0; i < sprintf(s, "%d", x); m += a, t *= a) a = s[i++] - 48;
    b = m * t ? x % m + x % t ? b : realloc(b, 4 * ++c) : b;
  }
  return b[c - 1];
}


main (j) {
  printf("%d\n", g(--j));
}

像这样编译它:gcc -std=c89 tt.c -o tt -O3

然后,如果我使用 shell 脚本循环运行它,以了解其运行时间:

#!/bin/bash
echo "using input $1"
for _ in `seq 1 10`; do
  ( time ./tt $(seq 1 $1) ) 3>&1 1>/dev/null 2>&3 \
    | grep real \
    | cut -f2
  # sleep 5
done

我看到这样的输出:

$ ./tt.sh 50
using input 50
0m0.016s
0m0.008s
0m0.008s
0m0.007s
0m0.007s
0m0.007s
0m0.008s
0m0.008s
0m0.007s
0m0.007s

或者像这样:

$ ./tt.sh 34
using input 34
0m0.007s
0m0.004s
0m0.004s
0m0.004s
0m0.005s
0m0.004s
0m0.003s
0m0.003s
0m0.003s
0m0.004s

第一次调用后,程序的运行时会出现初始加速real,然后所有后续调用都以此假加速运行。

如果我取消# sleep 5shell 脚本中该行的注释,我们会看到以下结果:

using input 50
0m0.008s
0m0.020s
0m0.018s
0m0.012s
0m0.009s
0m0.006s
0m0.013s
0m0.012s
0m0.009s
0m0.012s
using input 34
0m0.006s
0m0.007s
0m0.004s
0m0.007s
0m0.008s
0m0.003s
0m0.004s
0m0.004s
0m0.005s
0m0.007s

这些时间显得更加符合预期和准确,并且其中的差异必须归因于该时刻处理器的随机状态(即,它们是小的自然变化)。

如果我想获得程序的平均运行时间,我应该对这些数字进行平均,但是sleep 5在每次调用之间,虽然这是我能找到阻止这种行为的唯一方法,但 10 次测试总共需要 50 秒,而不是几次秒进行 20 次测试。

我之前在单线程程序中见过这种“重播缓存”行为,这些程序一遍又一遍地执行长时间操作(紧密循环),并且我知道在 99.9% 的情况下这是可取的。

假设这不是硬件级别的某些 Intel Magic™ 的结果,这是 Linux 内核或 Bash 故意做的事情吗?我怎样才能阻止它?

我希望我的程序具有可重现的运行时,包括从“冷启动”加载库和分页,而不需要sleep 5ing,因为受缓存影响的时间并不代表每次都是冷启动。

答案1

正如我的评论中所说:

可能不会有帮助,但在每次调用之前尝试“echo 3 > /proc/sys/vm/drop_caches”tt

提问者的回答:

是的,回声 1 | sudo tee /proc/sys/vm/drop_caches 正是我想要的(回显 3 相反给出了一个奇怪的结果)。你应该把它作为答案

价值记录

To free pagecache:
    echo 1 > /proc/sys/vm/drop_caches
To free reclaimable slab objects (includes dentries and inodes):
    echo 2 > /proc/sys/vm/drop_caches
To free slab objects and pagecache:
    echo 3 > /proc/sys/vm/drop_caches

相关内容