12

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

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

我已经阅读了有关该cacheflush(char *s, int a, int b)功能的信息,但我不确定它是否合适以及传递什么参数。

4

4 回答 4

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

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

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

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

笔记:

在这个(非常非常低的)级别,“Linux”!=“Unix”

于 2012-06-30T22:03:54.433 回答
5

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

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");
}
于 2017-04-29T10:52:47.200 回答
4

如果您正在编写用户模式(不是内核模式)程序,并且它是单线程的,那么您真的没有理由一开始就费心刷新缓存。您的用户模式程序可能会忘记它甚至存在;它只是为了加快程序的执行速度,而操作系统通过处理器的 MMU 对其进行管理。

我能想到的只有几个原因,您可能实际上想要从用户模式应用程序中刷新缓存:

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

无论如何,假设您使用的是 Linux...

#include <asm/cachectl.h>

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

这假设您有一个刚刚写入的内存块,并且您想确保它已从缓存中刷新回主内存。该块从 addr 开始,长度为 nbytes,位于两个缓存之一(或两者)中:

   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 倍!),写入任何旧垃圾进入它,然后冲洗整个块。

另请参阅: 如何在 C++ 中执行缓存操作?

于 2012-06-30T22:22:59.113 回答
2

好的,对不起我的第一个答案。我后来在您的问题下方阅读了您的后续评论,所以我现在意识到您想要刷新指令缓存以将您的程序(或其中的一部分)从缓存中引导出来,这样当您测试它的性能时,您也可以测试它的初始加载时间从主存到指令缓存。您是否还需要将代码将使用的任何数据刷新到主内存,以便数据和代码都是新加载的?

首先,我想提一下主内存本身也是一种缓存形式,您的硬盘(磁盘上的程序或磁盘上的交换空间)是程序指令可能到达的最低、最慢的地方从。也就是说,当您第一次运行例程时,如果由于靠近已执行的其他代码而尚未将其从磁盘加载到主内存中,则必须首先加载其 CPU 指令从磁盘。这比将其从主内存加载到缓存中需要一个数量级或更长的时间。然后,一旦它被加载到主存中,从主存加载到高速缓存所需的时间比从高速缓存加载到 CPU 的指令获取器所需的时间要长一个数量级。因此,如果您想测试代码的冷启动性能,您必须确定冷启动的含义……将其从磁盘中拉出,或从主内存中拉出。我不知道有任何命令可以将指令/数据从主内存中“刷新”到交换空间,因此将其刷新到主内存几乎可以做到(据我所知),但请记住即使您确实刷新了指令缓存,您的测试结果仍可能与第一次运行(当它可能将其从磁盘中拉出)到后续运行不同。

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

如果我需要这样做(在我看来这是非常奇怪的事情),我可能会首先找到我的函数在内存中的长度和大致位置。由于我使用的是 Linux,我会发出命令“objdump -d {myprogram} > myprogram.dump.txt”,然后我会在编辑器中打开 myprogram.dump.txt 并搜索我想要刷新的函数出,并通过使用十六进制计算器从它们的起始地址中减去它们的结束地址来计算它们的长度。我会写下每个的大小。稍后我将在我的代码中添加 cacheflush() 调用,将我想要刷新的每个函数的地址指定为“addr”,并将找到的长度指定为“nbytes”和 ICACHE。为了安全起见,我可能会稍微捏造一点,并在尺寸上增加约 10%,以防万一我对代码进行了一些调整而忘记调整 nbytes。对于我想要清除的每个函数,我都会像这样调用 cacheflush() 。然后,如果我还需要刷新数据,如果它使用全局/静态数据,我也可以刷新这些数据(DCACHE),但如果它是堆栈或堆数据,那么我可以(或应该)做任何事情来刷新超出缓存。试图这样做将是一种愚蠢的做法,因为它会创造一种在正常执行中永远不会或很少存在的条件。假设您使用的是 Linux ... 我可以(或应该)将其从缓存中清除,这实际上并不现实。试图这样做将是一种愚蠢的做法,因为它会创造一种在正常执行中永远不会或很少存在的条件。假设您使用的是 Linux ... 我可以(或应该)将其从缓存中清除,这实际上并不现实。试图这样做将是一种愚蠢的做法,因为它会创造一种在正常执行中永远不会或很少存在的条件。假设您使用的是 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).

顺便说一句,这是课堂作业吗?

于 2012-06-30T22:51:33.873 回答