Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/135.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
刷新缓存以防止基准测试波动 我正在运行某人的C++代码来对数据集进行基准测试。我遇到的问题是,我经常会得到第一次运行的时间,如果我再次运行相同的代码,这些时间会发生很大的变化(即28秒到10秒)。我假设这是由于CPU的自动缓存造成的。有没有一种方法可以刷新缓存,或者以某种方式防止这些波动?_C++_Caching_Benchmarking_Timing - Fatal编程技术网

刷新缓存以防止基准测试波动 我正在运行某人的C++代码来对数据集进行基准测试。我遇到的问题是,我经常会得到第一次运行的时间,如果我再次运行相同的代码,这些时间会发生很大的变化(即28秒到10秒)。我假设这是由于CPU的自动缓存造成的。有没有一种方法可以刷新缓存,或者以某种方式防止这些波动?

刷新缓存以防止基准测试波动 我正在运行某人的C++代码来对数据集进行基准测试。我遇到的问题是,我经常会得到第一次运行的时间,如果我再次运行相同的代码,这些时间会发生很大的变化(即28秒到10秒)。我假设这是由于CPU的自动缓存造成的。有没有一种方法可以刷新缓存,或者以某种方式防止这些波动?,c++,caching,benchmarking,timing,C++,Caching,Benchmarking,Timing,没有一种方法“适用于所有地方”。大多数处理器都有特殊指令来刷新缓存,但它们通常是特权指令,因此必须从操作系统内核内部完成,而不是从用户模式代码中完成。当然,每个处理器架构的指令完全不同 当前所有x86处理器都有一条clflush指令,用于刷新一条缓存线,但要做到这一点,必须有要刷新的数据(或代码)的地址。这对于小而简单的数据结构来说是很好的,但是如果你有一棵到处都是的二叉树,这就不太好了。当然,根本不是便携式的 在大多数环境中,读取和写入大量替代数据,例如: // Global variable

没有一种方法“适用于所有地方”。大多数处理器都有特殊指令来刷新缓存,但它们通常是特权指令,因此必须从操作系统内核内部完成,而不是从用户模式代码中完成。当然,每个处理器架构的指令完全不同

当前所有x86处理器都有一条
clflush
指令,用于刷新一条缓存线,但要做到这一点,必须有要刷新的数据(或代码)的地址。这对于小而简单的数据结构来说是很好的,但是如果你有一棵到处都是的二叉树,这就不太好了。当然,根本不是便携式的

在大多数环境中,读取和写入大量替代数据,例如:

// Global variables.
const size_t bigger_than_cachesize = 10 * 1024 * 1024;
long *p = new long[bigger_than_cachesize];
...
// When you want to "flush" cache. 
for(int i = 0; i < bigger_than_cachesize; i++)
{
   p[i] = rand();
}
//全局变量。
常量大小大于缓存大小=10*1024*1024;
long*p=新长[大于缓存大小];
...
//当您想要“刷新”缓存时。
for(int i=0;i<大于缓存大小;i++)
{
p[i]=rand();
}
使用
rand
比填充常量/已知值要慢得多。但是编译器无法优化调用,这意味着(几乎)可以保证代码将保持不变

上述方法不会刷新指令缓存——这要困难得多,基本上,您必须运行一些(足够大的)其他代码才能可靠地完成这项工作。然而,指令缓存对整体基准性能的影响往往较小(指令缓存对于现代处理器的性能非常重要,这不是我要说的,但从某种意义上说,基准测试的代码通常足够小,可以放入缓存中,并且基准测试在同一代码上运行多次,所以第一次迭代时速度较慢)

其他想法

模拟“非缓存”行为的另一种方法是为每个基准测试过程分配一个新区域——换句话说,在基准测试结束之前不释放内存,或者使用包含数据的数组并输出结果,这样每次运行都有自己的数据集来处理


此外,通常会实际测量基准测试的“热运行”性能,而不是缓存为空的第一次“冷运行”。这当然取决于您实际试图实现的目标…

以下是我的基本方法:

  • 如果您可以动态地(或静态地)确定LLC大小,或者如果您不知道,则在感兴趣的平台1上分配一个大小为LLC大小2倍的内存区域
  • memset
    将内存区域设置为非零值:
    1
    就可以了
  • 将指针“下沉”到某个地方,使编译器无法优化上面或下面的内容(写入
    volatile
    全局几乎100%的时间都有效)
  • 从区域中的随机索引中读取,直到平均触及每个缓存线10次左右(将读取的值累积为一个总和,以与(3)类似的方式接收)
  • 下面是一些关于为什么这通常是有效的以及为什么少做可能不起作用的说明——这些细节是以x86为中心的,但类似的关注点将应用于许多其他体系结构

    • 在开始主只读刷新循环之前,您绝对希望写入分配的内存(步骤2),因为否则您可能只是重复读取操作系统返回的同一个小内存,以满足内存分配
    • 您希望使用比LLC大小大得多的区域,因为外部缓存级别通常是物理寻址的,但您只能分配和访问虚拟地址。如果您只分配LLC大小的区域,通常无法完全覆盖每个缓存集的所有方式:某些缓存集将被过度表示(因此将被完全刷新),而其他集合的表示不足,因此甚至不能通过访问该内存区域刷新所有现有值。2倍的过度分配使得几乎所有集合都有足够的表示
    • 您希望避免优化器做一些聪明的事情,例如注意内存永远不会逃逸函数,并消除所有读写操作
    • 您希望在内存区域周围随机迭代,而不仅仅是线性地跨越内存区域:一些设计,如最近Intel上的LLC,会在“流”时检测到模式存在,并且从LRU切换到MRU,因为LRU对于这种负载来说可能是最糟糕的替换策略。其效果是,无论您在内存中流多少次,在您的努力可以保留在缓存中之前的一些“旧”行。随机访问内存会破坏这种行为
    • 您希望访问多于LLC的内存量,原因如下:(a)与分配多于LLC大小(虚拟访问与物理缓存)的原因相同,(b)因为随机访问需要更多的访问,然后才有可能命中每个集合足够的次数(c)缓存通常只是伪LRU,因此需要的访问次数超过在精确LRU下预期的访问次数才能清除每一行
    即使这样也不是万无一失的。上面没有考虑到的其他硬件优化或缓存行为可能会导致此方法失败。操作系统提供的页面分配可能会让您非常不走运,无法访问所有页面(使用2MB页面可以在很大程度上缓解此问题).我强烈建议测试您的冲洗技术是否足够:一种方法是测量n