如何从C程序中刷新Linux中的CPU缓存?

时间:2012-06-30 21:54:27

标签: c linux unix caching flush

我正在写一个C程序,我需要冲洗我的记忆。我想知道是否有任何UNIX系统命令来刷新CPU缓存。

这是我的项目的要求,它涉及计算我的逻辑所花费的时间。

我已经阅读了cacheflush(char *s, int a, int b)函数,但我不确定它是否合适以及参数传递的内容。

4 个答案:

答案 0 :(得分:9)

  1. 我认为你的意思是“CPU缓存”,而不是内存缓存

  2. 上面的链接很好:建议“通过CPU写入大量数据”是 Windows特定

  3. 以下是同一主题的另一个变体:

  4. 这是一篇关于Linux和CPU缓存的文章:

  5. 注:

    在这个(非常非常低)的水平上,“Linux”!=“Unix”

答案 1 :(得分:4)

如果你正在编写一个用户模式(而不是内核模式)程序,并且它是单线程的,那么你真的没有理由在第一时间打扰你的缓存。您的用户模式程序可能会忘记它甚至存在;它只是为了加快程序的执行速度,操作系统通过处理器的MMU来管理它。

我能想到的只有几个原因你可能真的想从用户模式应用程序中刷新缓存:

  1. 您的应用程序旨在在对称多处理器系统上运行,或与外部硬件进行数据交易)
  2. 您只是在测试缓存以进行某种性能测试(在这种情况下,您应该真的应该编写测试以在内核模式下运行,也许作为驱动程序)。
  3. 无论如何,假设你正在使用Linux ......

    #include <asm/cachectl.h>
    
    int cacheflush(char *addr, int nbytes, int cache);
    

    这假设您有一个刚刚写入的内存块,并且您希望确保将其从缓存中刷回主内存。该块从addr开始,并且它是nbytes long,并且它位于两个缓存之一(或两者)中:

       ICACHE Flush the instruction cache.
       DCACHE Write back to memory and invalidate the affected valid cache lines.
       BCACHE Same as (ICACHE|DCACHE).
    

    通常你只需要刷新DCACHE,因为当你将数据写入“内存”(即缓存)时,它通常是数据,而不是指令。

    如果你想为了一些奇怪的测试原因而刷新“所有缓存”,你可以使用malloc()一块大块,你知道它比CPU的缓存大(射击,使它大8倍!),写任何旧的垃圾进入它,只是冲洗整个块。

    另请参阅:How to perform cache operations in C++?

答案 2 :(得分:2)

好的,抱歉我的第一个答案。我后来在你的问题下面阅读了你的后续评论,所以我现在意识到你要刷新INSTRUCTION CACHE以从缓存中启动你的程序(或部分程序),这样当你测试它的性能时,你也会测试它的初始加载时间从主存储器进入指令缓存。您是否还需要将代码将使用的任何数据刷新到主内存中,以便数据和代码都是新负载?

在其他任何事情之前,我想提一下主内存本身也是一种缓存形式,你的硬盘(磁盘上的程序或磁盘上的交换空间)是程序指令中最低,最慢的位置可能来自。也就是说,当你第一次运行一个例程时,如果它还没有因为接近已经执行过的其他代码而从磁盘加载到主内存中,那么首先必须加载它的CPU指令从磁盘。这比从主存储器加载到缓存中需要一个数量级或更长的时间。然后,一旦将其加载到主存储器中,从主存储器加载到高速缓存中所需的时间比从高速缓存加载到CPU的指令提取器所需的时间长一些。因此,如果您想测试代码的冷启动性能,您必须确定冷启动意味着什么......将其从磁盘中拉出,或将其从主内存中拉出来。我不知道有任何命令将指令/数据从主内存“刷新”到交换空间,因此将其刷新到主内存大约就像你能做的那样(我知道),但要记住您的测试结果可能仍然不同于第一次运行(当它可能将其从磁盘中拉出)到后续运行时,即使您确实刷新了指令缓存。

现在,如何刷新指令缓存以确保将自己的代码刷新到主内存?

如果我需要这样做(在我看来很奇怪的事情),我可能会先找到长度&amp;我的函数在内存中的大致位置。由于我使用的是Linux,我会发出命令“objdump -d {myprogram}&gt; myprogram.dump.txt”,然后在编辑器中打开myprogram.dump.txt并搜索我想要的函数刷新,并通过使用十六进制计算器从他们的起始地址减去他们的结束地址来计算他们有多长时间。我写下每个的大小。稍后我会在我的代码中添加cacheflush()调用,为每个函数提供我想要清除的地址作为'addr'和我发现'nbytes'的长度,以及ICACHE。为了安全起见,我可能会捏造一点点&amp;添加大约10%的大小,以防我对代码进行一些调整,忘记调整nbytes。我会为每个要清除的函数调用cacheflush()。然后,如果我还需要清除数据,如果它正在使用全局/静态数据,我也可以刷新它们(DCACHE),但如果它是堆栈或堆数据,那么我可以(或应该)做什么来实现冲洗那个超出缓存。试图这样做是一种愚蠢的运动,因为它会创造一种在正常执行中永远或很少存在的条件。 假设你正在使用Linux ......

#include <asm/cachectl.h>

int cacheflush(char *addr, int nbytes, int cache);

...where cache is one of:
   ICACHE Flush the instruction cache.
   DCACHE Write back to memory and invalidate the affected valid cache lines.
   BCACHE Same as (ICACHE|DCACHE).
顺便说一下,这是一堂课的作业吗?

答案 3 :(得分:2)

这就是英特尔建议刷新缓存的方式:

mem_flush(const void *p, unsigned int allocation_size){
    const size_t cache_line = 64;
    const char *cp = (const char *)p;
    size_t i = 0;

    if (p == NULL || allocation_size <= 0)
            return;

    for (i = 0; i < allocation_size; i += cache_line) {
            asm volatile("clflush (%0)\n\t"
                         : 
                         : "r"(&cp[i])
                         : "memory");
    }

    asm volatile("sfence\n\t"
                 :
                 :
                 : "memory");
}