你好,我一直在尝试体验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
是我的理解错了?还是我的程序错了?
1条答案
按热度按时间ozxc1zmp1#
你的假设有点正确。
printf
是libc
程式库的一部分。如果您使用动态链接,操作系统可能会借由只为所有使用程式库的行程载入一次程式库,来最佳化内存使用。但是,我不希望您测量任何显著差异的原因有很多:
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
之类的地址可能不起作用!