什么是"页面错误"在'perf stat`中表示关于"后端循环空闲"?

时间:2018-05-14 09:07:21

标签: caching perf

我写了一个简单的矩阵乘法程序来演示通过perf实现缓存和性能测量的效果。用于比较相对效率的两个函数是sqmat_multsqmat_mult_efficient

void sqmat_mult(int x, const int a[x][x], const int b[x][x], int m[x][x])
{
    for (int i = 0; i < x; i++) {
        for (int j = 0; j < x; j++) {
            int sum = 0;
            for (int k = 0; k < x; k++) {
                sum += a[i][k] * b[k][j]; // access of b array is non sequential
            }
            m[i][j] = sum;
        }
    }
}


static void sqmat_transpose(int x, int a[x][x])
{
    for (int i = 0; i < x; i++) {
        for (int j = i+1; j < x; j++) {
            int temp = a[i][j];
            a[i][j] = a[j][i];
            a[j][i] = temp;
        }
    }     
}

void sqmat_mult_efficient(int x, const int a[x][x], int b[x][x], int m[x][x])
{
    sqmat_transpose(x, b);

    for (int i = 0; i < x; i++) {
        for (int j = 0; j < x; j++) {
            int sum = 0;
            for (int k = 0; k < x; k++) {
                sum += a[i][k] * b[j][k];  // access of b array is sequential
            }
            m[i][j] = sum;
        }
    }

    sqmat_transpose(x, b);
}

然而,当我基于这两个函数运行perf stat时,我对&#34; page-faults&#34;感到困惑。统计。 sqmat_mult的输出为:

     428374.070363      task-clock (msec)         #    1.000 CPUs utilized          
               128      context-switches          #    0.000 K/sec                  
               127      cpu-migrations            #    0.000 K/sec                  
            12,334      page-faults               #    0.029 K/sec                  
 8,63,12,33,75,858      cycles                    #    2.015 GHz                      (83.33%)
    2,89,73,31,370      stalled-cycles-frontend   #    0.34% frontend cycles idle     (83.33%)
 7,90,36,10,13,864      stalled-cycles-backend    #   91.57% backend cycles idle      (33.34%)
 2,24,41,64,76,049      instructions              #    0.26  insn per cycle         
                                                  #    3.52  stalled cycles per insn  (50.01%)
    8,84,30,79,219      branches                  #   20.643 M/sec                    (66.67%)
       1,04,85,342      branch-misses             #    0.12% of all branches          (83.34%)

     428.396804101 seconds time elapsed

sqmat_mult_efficient的输出为:

       8534.611199      task-clock (msec)         #    1.000 CPUs utilized          
      39876.726670      task-clock (msec)         #    1.000 CPUs utilized          
                11      context-switches          #    0.000 K/sec                  
                11      cpu-migrations            #    0.000 K/sec                  
            12,334      page-faults               #    0.309 K/sec                  
 1,19,87,36,75,397      cycles                    #    3.006 GHz                      (83.33%)
      49,19,07,231      stalled-cycles-frontend   #    0.41% frontend cycles idle     (83.33%)
   50,40,53,90,525      stalled-cycles-backend    #   42.05% backend cycles idle      (33.35%)
 2,24,10,77,52,356      instructions              #    1.87  insn per cycle         
                                                  #    0.22  stalled cycles per insn  (50.01%)
    8,75,27,87,494      branches                  #  219.496 M/sec                    (66.68%)
         50,48,390      branch-misses             #    0.06% of all branches          (83.33%)

      39.879816492 seconds time elapsed

使用基于转置的乘法,&#34;后端循环空闲&#34;如预期的那样大大减少了。这是由于获取相干内存的等待时间较短。让我感到困惑的是&#34;页面错误&#34;是一样的。起初我认为这可能是一个上下文切换问题,因此我使用调度SCHED_FIFO和优先级1运行程序。上下文切换的数量从大约800减少到11,但是&#34;页面错误&#34;与以前完全一样。

我使用的矩阵的维度为2048 x 2048,因此整个数组不适合4K页面。它也不适合我的整个缓存(在我的情况下为4MiB),因为三个矩阵(3 * 2048 * 2048 * sizeof(int)== 48MiB)的大小远远超过了总的avaibale缓存。假设&#34;页面错误&#34;这里指的是TLB未命中或缓存未命中,我不知道这两种算法如何具有完全相同的数字。据我所知,我看到的很多效率提升都来自L1缓存命中,但这并不意味着RAM缓存传输没有任何作用。

我还做了另一个实验,看看&#34;页面错误&#34;使用数组大小​​进行缩放 - 正如预期的那样。

所以我的问题是 -

  1. 我是否正确地假设&#34;页面错误&#34;请参考TLB未命中或缓存未命中?
  2. 为什么&#34;页面错误&#34;两种算法完全相同吗?

1 个答案:

答案 0 :(得分:1)

  

我是否正确地假设&#34;页面错误&#34;是指TLB未命中还是缓存未命中?

没有。当虚拟内存未映射到物理内存时,会发生页面错误。这有两个典型的 1 原因:

  1. 数据已从物理内存移至磁盘(交换)。发生页面错误时,数据将被移回物理内存,并将恢复虚拟内存的映射。
  2. 懒惰地分配了一个内存区域。只有在第一次分配时,实际物理内存才会分配给相应的虚拟内存。
  3. 现在页面错误可能只会在高速缓存未命中和TLB未命中之后发生 2 ,因为TLB仅存储实际映射,而高速缓存仅包含也在主存储器中的数据

    以下是发生了什么的简化图表:

    decision making process for memory lookup 图片来自维基共享资源的Aravind krishna CC BY-SA 4.0

      

    为什么&#34;页面错误&#34;这两种算法完全相同吗?

    您的所有数据都适合物理内存,因此您的情况下无法进行交换。这将为您留下2),每个虚拟页面都会发生一次。您使用48 MiB内存,使用12288页4 kiB大小。这两种算法都是一样的,几乎就是你测量的。

    1 其他原因可能是内存映射IO或NUMA平衡。

    2 我想你可以构建一个架构,其中TLB包含未映射的虚拟地址信息,或者内存不包括缓存,但我怀疑这样的架构是否存在。