我今天和朋友说话了......他告诉我一件我不知道的事情 这是真的,所以我决定在这里问一下。
他有这个巨大的矩阵,他有这样的项目:
1 2 3 4 .. 1000
1001 ...... 2000
2001 ...... 3000
....
无论如何..他说在C中遍历它更有效率1 2 3 4 ..因为在C中,数组被逐行存储在内存中。他在代码中对此进行了一次测试,以逐列和逐行遍历这个巨大的结构之一,时间也不同。一个比另一个更有效率。
但我在想这怎么会有所作为......
我的意思是在连续的内存数组中访问*(i + 1)和*(i + 1000)需要相同的时间。正确?
泰德
答案 0 :(得分:8)
他是locality of reference(特别是空间位置)。将预取/缓存附近的地址以加快访问速度。因此,如果您点击了内存地址p
,则会预取并缓存内存地址p + 1
和其他附近地址,以加快对它们的访问速度,而访问p + 1000
则可能需要再次访问记忆巴士。
(顺便说一下,您描述的布局是row-major order。有些语言(例如Fortran)使用column-major order。)
答案 1 :(得分:1)
处理器假设如果你检索到一块内存很快就会需要其他附近的数据,那么预取一些额外的数据到缓存中。缓存命中非常快,因此利用此功能的代码将比不执行此操作的代码更快。这就是为什么遍历附近的数据成员比跳转更快的原因。 (所以,是的,他是对的)
答案 2 :(得分:1)
原因是你的大多数硬件是围绕很多关于如何可能使用的假设而构建的,然后他们利用这些知识来获得更好的性能。在这种情况下,相关原则是“空间位置”,“如果你最近需要地址N,那么你可能很快就会需要地址N + 1的内容”。
因此,您的RAM旨在以突发方式传输内存。当你要求一个数据字时,它会开始加载一个更大的连续内存块,以便在你可能稍后要求它时就可以了。
CPU本身也是如此。它的缓存按照相同的原则排列。它存储整个缓存行(通常为16或32个字节,如果内存服务),而不是单个字节或单词。这样,当您请求单个字节时,整个缓存行将被读入缓存,以便在您需要时也可以使用附近的数据。
如果逐列遍历矩阵,那么每个内存访问都是远离前一个的地址,因此没有空间局部性,因此CPU缓存和RAM都无法预测它们应该是什么数据预取,最后等待数据一直传送给你。
答案 3 :(得分:1)
请参阅Row-major order。
在C语言中,成员逐行存储。缓存意味着当访问*i
时,*(i+1)
可能位于缓存中,因此速度更快。
提及C语言的重要性是因为并非所有语言都是这样的。众所周知,Fortran使用列主要订单。因此,您有时会找到一个名为 C-contiguous 的行主矩阵,而一个主要的矩阵称为 Fortran-contiguous 。
答案 4 :(得分:0)
Ulrich Drepper有一篇很好的论文,涵盖了与现代计算机内存系统相关的缓存行为。它位于http://www.akkadia.org/drepper/cpumemory.pdf。