【问题标题】:Is there a way to check whether the processor cache has been flushed recently?有没有办法检查处理器缓存最近是否被刷新?
【发布时间】:2011-08-27 19:54:50
【问题描述】:

在 i386 linux 上。如果可能的话,最好在 c/(c/posix std libs)/proc 中。如果没有,是否有任何组件或第三方库可以做到这一点?

编辑:我正在尝试开发测试内核模块是否清除缓存行或整个处理器(使用 wbinvd())。程序以 root 身份运行,但如果可能,我更愿意留在用户空间。

【问题讨论】:

  • 你的意思是处理器缓存吗?最近到底是多少时间?
  • 不到一秒(我知道那不是很准确)。基本上我想测试一个相当快的函数是否刷新了缓存。
  • 问题没有意义:(a)通常有多个缓存,(b)缓存行一直被驱逐,(c)“刷新”缓存(即驱逐所有缓存行)是不是通常会发生的事情,(d) 一般来说,CPU 不知道任何缓存中发生了什么
  • 这可能不常见,但在某些地方会进行冲洗,不是吗?
  • @Roman: 是的,WBINVD 刷新了所有的数据缓存(不清楚它是否也刷新了 L1 指令缓存,并且它的实现是依赖于 CPU 的),但是使用它非常不寻常指令(自我修改代码是唯一想到的例子),即便如此,缓存将立即开始再次填充,并且 CPU 无法直接了解任何缓存的当前状态。您应该解释您真正想要达到的目标是什么,即您提出问题的动机。

标签: linux cpu flush hardware-interface cpu-cache


【解决方案1】:

缓存相关系统会尽最大努力向您隐藏此类内容。我认为您将不得不间接地观察它,或者通过使用性能计数寄存器来检测缓存未命中,或者通过使用高分辨率计时器仔细测量读取内存位置的时间。

这个程序在我的 x86_64 盒子上运行,以演示 clflush 的效果。它计算使用rdtsc 读取全局变量所需的时间。作为直接与 CPU 时钟相关联的单条指令,直接使用 rdtsc 非常适合。

这是输出:

用了 81 个刻度 用了 81 个刻度 同花顺:花费了 387 个滴答声 用了 72 个滴答声

您会看到 3 次试验:第一次确保 i 在缓存中(确实如此,因为它只是作为 BSS 的一部分被归零),第二次是读取应该在缓存中的 i。然后clflushi 踢出缓存(连同它的邻居)并显示重新读取它需要更长的时间。最终读取验证它已回到缓存中。结果非常可重复,并且差异足够大,可以很容易地看到缓存未命中。如果您愿意校准 rdtsc() 的开销,您可以使差异更加明显。

如果您无法读取要测试的内存地址(尽管 /dev/mem 中的 mmap 应该可以用于这些目的),如果您知道缓存线大小和缓存。然后,您可以使用可访问的内存位置来探测您感兴趣的集合中的活动。

源码:

#include <stdio.h>
#include <stdint.h>

inline void
clflush(volatile void *p)
{
    asm volatile ("clflush (%0)" :: "r"(p));
}

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

volatile int i;

inline void
test()
{
    uint64_t start, end;
    volatile int j;

    start = rdtsc();
    j = i;
    end = rdtsc();
    printf("took %lu ticks\n", end - start);
}

int
main(int ac, char **av)
{
    test();
    test();
    printf("flush: ");
    clflush(&i);
    test();
    test();
    return 0;
}

【讨论】:

  • 谢谢 这起初看起来不错,但结果太不准确了。可能是由于我的代码中的问题。
  • @Ben 是否必须满足任何特殊要求(编译、系统设置等)才能使此代码正常工作?我在“flush:”行中收到与缓存访问相同数量级的滴答声。我正在使用带有 gcc-9 和 clang 11 的 macOS 10.15.5(均经过测试)。
【解决方案2】:

我不知道任何通用命令来获取缓存状态,但有一些方法:

  1. 我想这是最简单的:如果您有内核模块,只需反汇编它并查找缓存失效/刷新命令(atm。我只想到了 3 个:WBINDVD、CLFLUSH、INVD)。
  2. 你刚才说它是针对i386的,但我猜你的意思不是80386。问题是有很多不同的扩展和功能。例如。最新的英特尔系列包含一些用于缓存系统的性能/分析寄存器,您可以使用它们来评估缓存未命中/命中/传输次数等。
  3. 类似于 2,很大程度上取决于您获得的系统。但是,当您有一个多处理器配置时,您可以观察第一个缓存一致性协议 (MESI) 和第二个。

您提到了 WBINVD - afaik 将始终刷新完成,即所有缓存行

【讨论】:

  • 我有源代码,我正在尝试编写一个测试来确认在某些条件下会发生什么。 Atom 双核和单核。
  • 如果你有源代码,你就不能直接看一下是否发出过 WBINVD 指令吗?
  • +1 用于 pc 创意 -- 性能计数器可以提供问题所要求的信息。
  • jalf: 1) 我正在尝试进行半自动化测试,而不是代码审查(代码可能会更改,但希望测试仍然有效)。 2) 一些地方基于策略调用了WBINVD。我正在尝试测试不同的条件以查看它是否被调用。
【解决方案3】:

这可能不是您特定问题的答案,但您是否尝试过使用缓存分析器,例如 Cachegrind?它只能用于分析用户空间代码,但您仍然可以使用它,例如如果函数的代码不依赖于任何内核特定的接口,则将其移动到用户空间。

它实际上可能比试图向处理器询问可能存在或可能不存在的信息更有效,并且可能会受到您仅仅询问它的影响 - 是的,Heisenberg 早于他的时间:-)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-09
    • 1970-01-01
    • 1970-01-01
    • 2020-01-07
    • 2016-09-22
    • 2021-12-31
    相关资源
    最近更新 更多