如何在Linux系统中体验缓存未命中和命中?

drkbr07n  于 2022-11-22  发布在  Linux
关注(0)|答案(1)|浏览(186)

你好,我一直在尝试体验Linux中的缓存未命中和命中。为了做到这一点,我用C编写了一个程序,在那里我测量了CPU周期中执行指令 printf() 所需的时间。第一部分测量了未命中所需的时间,第二部分测量了命中所需的时间。下面是给定的程序:

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sched.h>
#include <sys/types.h> 
#include <unistd.h> 
#include <signal.h>

uint64_t rdtsc() {
  uint64_t a, d;
  asm volatile ("mfence");
  asm volatile ("rdtsc" : "=a" (a), "=d" (d));
  a = (d<<32) | a;
  asm volatile ("mfence");
  return a;
}

int main(int argc, char** argv)
{
    size_t time = rdtsc();
    printf("Hey ");
    size_t delta1 = rdtsc() - time;
    printf("delta: %zu\n", delta1);

    size_t time2 = rdtsc();
    printf("Hey ");
    size_t delta2 = rdtsc() - time2;
    printf("delta: %zu\n", delta2);
    sleep(100);
}

现在我想说明两个进程(两个终端)有commun缓存。所以我认为在两个终端上运行这个程序会导致:

Terminal 1:
miss
hit
Terminal 2:
hit 
hit

但现在我有这样的东西:

Terminal 1:
miss
hit
Terminal 2:
miss 
hit

是我的理解错了?还是我的程序错了?

ozxc1zmp

ozxc1zmp1#

你的假设有点正确。
printflibc程式库的一部分。如果您使用动态链接,操作系统可能会借由只为所有使用程式库的行程载入一次程式库,来最佳化内存使用。
但是,我不希望您测量任何显著差异的原因有很多:
1.与缓存命中和缓存未命中之间的差异相比,printf需要花费大量的时间来完成,并且有很多事情会引入噪声。仅通过一次测量,您不太可能测量到这种微小的差异。
1.第一次测量花费更长时间的实际原因可能是库函数printf的懒惰绑定正由加载器(https://maskray.me/blog/2021-09-19-all-about-procedure-linkage-table)解析,或者对于第一次输出发生了一些其它的魔术(正在建立缓冲器等)。
1.许多libc函数被许多不同的进程使用。如果库是共享的,那么即使你没有使用printf,它也可能被缓存。
我建议在其中一个终端的printf上安装一个Flush+Reload攻击(https://eprint.iacr.org/2013/448.pdf),然后在另一个终端上使用它。这样,你可能会看到时间上的差异。
注意:要找到攻击的printf的实际地址,您需要熟悉动态链接和plt。仅使用void* addr = printf之类的地址可能不起作用!

相关问题