CLFLUSH如何处理不在缓存中的地址?

时间:2016-03-09 19:05:17

标签: c linux-kernel intel cpu-architecture cpu-cache

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

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

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

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

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

enter image description here

以下是我们用于绘制图形的数据。第一列是以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

2 个答案:

答案 0 :(得分:7)

你想看一下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),comp和CLFLUSH。在单线程中   应用程序,使用CLFLUSHOPT的冲洗缓冲区可能高达9X   比使用CLFLUSH和Skylake微架构更好。

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

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

答案 1 :(得分:6)

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

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

This AIDA64 instruction latency/throughput benchmark repository将单插槽Haswell-E CPU (i7-5820K)列为 clflush吞吐量为每99.08个周期一个。它没有反复说明是否重复相同的地址,或者是什么。

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

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

  • clflush:每~66.42个圈子一个
  • clflushopt:每个~56.33周期一个

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