首页 > 技术文章 > CSAPP:局部性原理

ZhaoxiCheung 2017-04-16 18:19 原文

  一个编写良好的计算机程序常常具有良好的局部性(locality)。局部性通常有两种不同的形式:时间局部性(temporal locality)和空间局部性(spatial locality)。在一个具有良好时间局部性的程序中,被引用过一次的内存位置很可能在不远的将来再被多次引用。在一个具有良好空间局部性的程序中,如果一个内存位置被引用了一次,那么程序很可能在不远的将来引用附近的一个内存位置。

      评价程序局部性的一些简单原则有如下几条:

    • 重复引用相同变量的程序有良好的时间局部性。
    • 对于具有步长为m的引用模式的程序,步长越小,空间局部性越好。具有步长为n的引用模式的程序有很好的空间局部性。在内存中以大步长跳来跳去的程序空间局部性会很差。
    • 对于取值令来说,循环有好的时间局部性和空间局部性。循环体越小,循环迭代次数越多,局部性越好。 

      对以上原则的具体实例分析:

       从中,我们可以看出,变量sum在每次循环迭代中被引用一次,因此,对于sum来说,有好的时间局部性。另一方面,因为sum是标量,对于sum来说,没有空间局部性。但是,向量v的元素是被顺序读取的,按照它们存储在内存中的顺序(假设数组是从地址0开始)。因此,对于向量v,函数有很好的空间局部性,但是时间局部性很差,因为每个向量元素只被访问一次。从整个循环体来看,循环体中的每个变量,这个函数要么有好的空间局部性,要么有好的时间局部性,所以我们可以断定sumvec函数有良好的局部性。

      我们称像sumvec这样顺序访问一个向量每个元素的函数,具有步长为1的引用模式。有时我们称步长为1的引用模式为顺序引用模式。一个连续向量中,每隔k个元素进行访问,就称为步长为k的引用模式。一般而言,随着步长的增加,空间局部性下降。

  对于引用多维数组的程序来说,步长也是一个很重要的问题。

      二者的区别在于我们交换了 i 和 j 的循环,但是就是这么一个小的改动,能对其局部性有很大的影响。

 例题分析:

void clear1(point *p,int n)
{
    int i,j;

    for (i = 0;i < n;i++)
    {
        for (j = 0;j < 3;j++)
            p[i].vel[j] = 0;
        for (j = 0;j < 3;j++)
            p[i].acc[j] = 0;
    }
}

void clear2(point *p,int n)
{
    int i,j;
    for (i = 0;i < n;i++)
    {
        for (j = 0;j < 3;j++)
        {
            p[i].vel[j] = 0;
            p[i].acc[j] = 0;
        }
    }
}

void clear3(point *p,int n)
{
    int i,j;
    for (j = 0;j < 3;j++)
    {
        for (i = 0;i < n;i++)
            p[i].vel[j] = 0;
        for (i = 0;i < n;i++)
            p[i].acc[j] = 0;
    }
}

  单从空间局部性(不考虑复杂度等问题)对这三个函数排序,容易得出函数clear1以步长为1的引用模式访问数组,因此明显地具有最好的空间局部性。函数clear2依次扫描N个结构中的每一个,但是在每个结构中,它以步长不为1的模式跳到下列相对于结构起始位置的偏移处:0、12、4、16、8、20.所以clear2的空间局部性比clear1的要差。函数clear3不仅在每个结构中跳来跳去,而且还从结构跳到结构,所以lcear3的空间局部性比clear2和clear1都要差。

 

推荐阅读