缓存内存如何工作?

时间:2008-10-20 11:36:52

标签: c caching optimization computer-architecture

今天当我在计算机组织课上时,老师谈到了一些有趣的事情。谈到为什么缓存有效时,他说:

for (i=0; i<M; i++)
   for(j=0; j<N; j++)
      X[i][j] = X[i][j] + K; //X is double(8 bytes)

用第二行改变第一行是不好的。你对此有何看法?为什么会这样?

5 个答案:

答案 0 :(得分:12)

Red Hat和glibc成名的Ulrich Drepper发表了一篇非常好的论文,What Every Programmer Should Know About Memory。一节非常详细地讨论了缓存。例如,在SMP系统中存在缓存效应,其中CPU最终会来回摧毁修改后的缓存行的所有权,从而极大地损害了性能。

答案 1 :(得分:9)

参考地点。因为数据是按行存储的,所以对于每一行,j列都在相邻的存储器地址中。操作系统通常会将整个页面从内存加载到缓存中,相邻的地址引用可能会引用同一页面。如果你通过内部循环中的行索引递增,那么这些行可能会在不同的页面上(因为它们之间由j个双精度分隔),并且缓存可能必须不断地引入并丢弃内存页面数据。这称为颠簸,对性能不利。

在实践中,对于较大的现代缓存,行/列的大小在发挥作用之前需要相当大,但它仍然是很好的做法。

[编辑]以上答案特定于C,可能因其他语言而异。我所知道的唯一不同的是FORTRAN。 FORTRAN按列主要顺序存储事物(上面是行主要部分),更改FORTRAN中语句的顺序是正确的。如果您想/需要效率,了解您的语言如何实现数据存储非常重要。

答案 2 :(得分:7)

这就像是因为像地方这样的缓存。访问的内存数量相同但间隔较远,将会遇到缓存的不同“行”,或者甚至可能完全错过缓存。因此,只要您有选择,就可以组织数据,以便及时在彼此附近发生的访问也可以在空间中进行。这会增加缓存命中的几率,并为您提供更高的性能。

当然有关于此主题的大量信息,例如参见this wikipedia entry on locality of reference。或者,我猜,你自己的课程教科书。 :)

答案 3 :(得分:2)

在C中,n维矩阵是行主要,意味着矩阵的最后一个索引表示内存中的相邻空格。这与其他一些语言(FORTRAN)不同,后者是列专业。在FORTRAN中,迭代这样的2D矩阵更有效:

do jj = 1,N
  do ii = 1,M
    x(ii,jj) = x(ii,jj) + K;
  enddo
enddo

答案 4 :(得分:1)

高速缓存是非常快速且非常昂贵的内存,靠近CPU。 CPU不是每次从RAM中获取一小段数据,而是取出一大块数据并将其存储在缓存中。赌注是,如果您只读取一个字节,那么您读取的下一个字节可能就在它之后。如果是这种情况,那么它可以来自缓存。

通过按原样布置循环,可以按照存储在内存中的顺序读取字节。这意味着它们位于缓存中,并且可以由CPU非常快速地读取。如果你在第1行和第2行交换,那么每次循环时你都会读取每个“N”字节。您正在读取的字节在内存中不再连续,因此它们可能不在缓存中。 CPU必须从(较慢的)RAM中获取它们,因此性能会降低。