我尝试了所有可能得出的推理,但我并不真正了解这种情节。
它基本上显示了从不同大小的数组以不同的步幅进行读取和写入的性能。
我知道对于4字节这样的小步长,我读取了缓存中的所有单元,因此,我具有良好的性能。但是,当我拥有2 MB阵列和4k步幅时会发生什么?还是4M和4k大步前进?为什么性能这么差?最后,为什么当我有1MB阵列并且跨度为1/8的大小性能不错时,为什么当1/4的大小性能最差,然后再减半大小时,性能却 super 好?
请帮助我,这件事使我发疯。
在此链接中,代码为:https://dl.dropboxusercontent.com/u/18373264/membench/membench.c
最佳答案
您的代码在给定的时间间隔(而不是恒定的访问次数)中循环,您没有比较相同的工作量,并且并非所有的缓存大小/步幅都具有相同的重复次数(因此它们获得不同的缓存机会)。
还要注意,第二个循环可能会被优化(内部for
),因为您没有在任何地方使用temp
。
编辑:
此处的另一个影响是TLB利用率:
在4k页面系统上,当步幅仍小于4k时,随着步幅的增长,您将享受越来越少的每页利用率(最终在4k步幅上达到每页一次访问),这意味着访问时间随着您的每次访问都必须访问第二级TLB(甚至可能至少部分地序列化您的访问)。
由于您通过步幅大小对迭代计数进行归一化,因此一般而言,您将在最内层循环中访问(size / stride)
,但在外部访问* stride
。但是,您访问的唯一页面的数量不同-对于2M阵列,2k步幅,您在内部循环中将具有1024次访问,但是只有512个唯一页面,因此对TLB L2的访问为512 * 2k。在4k跨度上,仍将有512个唯一页面,但有512 * 4k TLB L2访问。
对于1M阵列情况,您总共将拥有256个唯一页面,因此2k跨度将具有256 * 2k TLB L2访问,而4k将再次具有两次。
这就解释了为什么当您接近4k时,每行的性能会逐渐下降,以及为什么数组大小每增加一倍,同一步幅的时间就会增加一倍。较低的阵列大小可能仍会部分享受L1 TLB,因此您看不到相同的效果(尽管我不确定为什么有512k的原因)。
现在,一旦您开始将步伐提高到4k以上,您就会突然再次受益,因为您实际上跳过了整个页面。对于相同的阵列大小,跨度为8K的访问将只能访问其他页面,而将总TLB访问的一半作为4k进行访问,等等。
关于performance - 内存基准测试图: understanding cache behaviour,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20434193/