CLFLUSH如何处理尚未缓存的地址?

我们正在尝试使用Intel CLFLUSH指令在用户空间中刷新Linux中进程的缓存内容。

我们创建了一个非常简单的C程序,它首先访问一个大型数组,然后调用CLFLUSH来刷新整个数组的虚拟地址空间。 我们测量CLFLUSH刷新整个arrays所需的延迟。 程序中arrays的大小是一个输入,我们将输入从1MB变为40MB,步长为2MB。

根据我们的理解,CLFLUSH应该刷新缓存中的内容。 所以我们期望看到整个arrays的刷新延迟首先在arrays大小方面线性增加,然后在arrays大小大于20MB(这是我们程序的LLC的大小)之后延迟应该停止增加。

然而,实验结果非常令人惊讶,如图所示。 数组大小超过20MB后,延迟不会停止增加。

我们想知道如果地址不在缓存中,CLFLUSH是否可能在CLFLUSH将地址刷出缓存之前引入地址? 我们还尝试在英特尔软件开发人员手册中进行搜索,但没有找到任何解释,如果地址不在缓存中,CLFLUSH会做什么。

在此处输入图像描述

以下是我们用于绘制图形的数据。 第一列是以KB为单位的数组大小,第二列是以秒为单位刷新整个数组的延迟。

任何建议/建议都不仅仅是值得赞赏的。

[改性]

以前的代码是不必要的。 尽管CLFLUSH具有相似的性能,但它可以更容易地在用户空间中完成。 所以我删除了凌乱的代码以避免混淆。

SCENARIO=Read Only 1024,.00158601000000000000 3072,.00299244000000000000 5120,.00464945000000000000 7168,.00630479000000000000 9216,.00796194000000000000 11264,.00961576000000000000 13312,.01126760000000000000 15360,.01300500000000000000 17408,.01480760000000000000 19456,.01696180000000000000 21504,.01968410000000000000 23552,.02300760000000000000 25600,.02634970000000000000 27648,.02990350000000000000 29696,.03403090000000000000 31744,.03749210000000000000 33792,.04092470000000000000 35840,.04438390000000000000 37888,.04780050000000000000 39936,.05163220000000000000 SCENARIO=Read and Write 1024,.00200558000000000000 3072,.00488687000000000000 5120,.00775943000000000000 7168,.01064760000000000000 9216,.01352920000000000000 11264,.01641430000000000000 13312,.01929260000000000000 15360,.02217750000000000000 17408,.02516330000000000000 19456,.02837180000000000000 21504,.03183180000000000000 23552,.03509240000000000000 25600,.03845220000000000000 27648,.04178440000000000000 29696,.04519920000000000000 31744,.04858340000000000000 33792,.05197220000000000000 35840,.05526950000000000000 37888,.05865630000000000000 39936,.06202170000000000000 

你想看看Skylake的新优化指南,英特尔推出了另一个名为clflush_opt的clflush版本,它的版本很弱,并且在你的场景中表现更好。

请参阅此处的第7.5.7节 – http://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf

一般来说,CLFLUSHOPT吞吐量高于CLFLUSH,因为CLFLUSHOPT就如上所述和7.5.6节中描述的较小的内存流量顺序进行排序。 CLFLUSHOPT的吞吐量也会有所不同。 使用CLFLUSHOPT时,刷新修改的缓存行将比在非修改状态下刷新缓存行的成本更高。 CLFLUSHOPT将为CLFLUSH提供性能优势,适用于任何相关状态的缓存行。 CLFLUSHOPT更适合冲洗大缓冲区(例如大于几KB),适用于CLFLUSH。 在单线程应用中,使用CLFLUSHOPT的冲洗缓冲区可能比使用Skylake微架构的CLFLUSH高出9倍。

该部分还解释了刷新修改数据的速度较慢,这显然来自回写惩罚。

至于延迟增加,您是否正在测量超过地址范围所需的总时间并逐条扫描每条线路? 在这种情况下,即使通过LLC大小,您也会线性地依赖于数组大小。 即使行不存在,clflush也必须由执行引擎和内存单元处理,并查找每行的整个缓存层次结构,即使它不存在。

这并没有解释只读图中的膝盖,但确实解释了为什么它没有稳定。


我没有在本地测试以查看热缓存和冷缓存之间的区别,但我确实遇到了clflush的性能数字:

此AIDA64指令延迟/吞吐量基准测试库列出了单插槽Haswell-E CPU(i7-5820K) ,其clflush吞吐量为每~99.08个周期一个 。 它没有说明是否重复相同的地址,或者是什么。

因此即使不需要做任何工作, clflush也不会接近自由。 它仍然是一个微编码指令,没有经过大量优化,因为它通常不是CPU工作量的一个重要部分。

Skylake正准备改变,支持连接到内存控制器的持久内存:在Skylake(i5-6400T)上 ,测得的吞吐量为:

  • clflush :每~66.42个圈子一个
  • clflushopt :每~56.33个循环一个

当某些行实际上是需要刷新的脏缓存时, clflushopt可能clflushopt ,也许当L3忙于其他核心做同样的事情时。 或者他们可能只是希望在对吞吐量进行更大改进之前,尽快使用弱订购版本来获取软件。 在这种情况下,它快了约15%,这也不错。