c - 为什么这个顺序数组循环比使用 "lookup"数组的循环慢?

标签 c arrays loops

我最近一直在研究缓存局部性,我想了解 CPU 如何访问内存。我写了一个实验来查看按顺序循环数组与使用某种查找表索引数据数组时是否存在性能差异。我很惊讶地发现查找方法稍微快一些。我的代码如下。我在 Windows (MinGW) 上用 GCC 编译。

#include <stdlib.h>
#include <stdio.h>
#include <windows.h>

int main()
{
    DWORD dwElapsed, dwStartTime;

    //random arrangement of keys to lookup
    int lookup_arr[] = {0, 3, 8, 7, 2, 1, 4, 5, 6, 9};

    //data for both loops
    int data_arr1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int data_arr2[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    //first loop, sequential access
    dwStartTime = GetTickCount();
    for (int n = 0; n < 9000000; n++) {
        for (int i = 0; i < 10; i++)
            data_arr1[i]++;
    }
    dwElapsed = GetTickCount() - dwStartTime;
    printf("Normal loop completed: %d\n", dwElapsed);

    //second loop, indexes into data_arr2 using the lookup array
    dwStartTime = GetTickCount();
    for (int n = 0; n < 9000000; n++) {
        for (int i = 0; i < 10; i++)
            data_arr2[lookup_arr[i]]++;
    }
    dwElapsed = GetTickCount() - dwStartTime;
    printf("Lookup loop completed: %d\n", dwElapsed);

    return 0;
}

运行这个,我得到:

Normal loop completed: 375
Lookup loop completed: 297

最佳答案

根据我之前的评论,下面是你如何做这种事情。

  1. 重复测量
  2. 估计误差
  3. 大内存块
  4. 随机索引与线性索引(因此无论哪种方式都有间接性)

结果与“随机索引”在速度上有显着差异。

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <math.h>

#define N 1000000

int main(void) {
  int *rArr;
  int *rInd; // randomized indices
  int *lInd; // linear indices
  int ii;

  rArr = malloc(N * sizeof(int) );
  rInd = malloc(N * sizeof(int) );
  lInd = malloc(N * sizeof(int) );

  for(ii = 0; ii < N; ii++) {
    lInd[ii] = ii;
    rArr[ii] = rand();
    rInd[ii] = rand()%N;
  }

  int loopCount;
  int sum;
  time_t startT, stopT;
  double dt, totalT=0, tt2=0;

  startT = clock();
  for(loopCount = 0; loopCount < 100; loopCount++) {
    for(ii = 0; ii < N; ii++) {
      sum += rArr[lInd[ii]];
    }
    stopT = clock();
    dt = stopT - startT;
    totalT += dt;
    tt2 += dt * dt;
    startT = stopT;
  }
  printf("sum is %d\n", sum);
  printf("total time: %lf += %lf\n", totalT/(double)(CLOCKS_PER_SEC), (tt2 - totalT * totalT / 100.0)/100.0 / (double)(CLOCKS_PER_SEC));

  totalT = 0; tt2 = 0;
  startT = clock();
  for(loopCount = 0; loopCount < 100; loopCount++) {
    for(ii = 0; ii < N; ii++) {
      sum += rArr[rInd[ii]];
    }
    stopT = clock();
    dt = stopT - startT;
    totalT += dt;
    tt2 += dt * dt;
    startT = stopT;
  }
  printf("sum is %d\n", sum);
  printf("total time: %lf += %lf\n", totalT/(double)(CLOCKS_PER_SEC), sqrt((tt2 - totalT * totalT / 100.0)/100.0) / (double)(CLOCKS_PER_SEC));
}

结果 - 顺序访问快了 2 倍以上(在我的机器上):

sum is -1444272372
total time: 0.396539 += 0.000219
sum is 546230204
total time: 0.756407 += 0.001165

通过 -O3 优化,差异更加明显 - 整整快了 3 倍:

sum is -318372465
total time: 0.142444 += 0.013230
sum is 1672130111
total time: 0.455804 += 0.000402

关于c - 为什么这个顺序数组循环比使用 "lookup"数组的循环慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20496271/

相关文章:

在预定义结构上创建动态 vector

在 C 中作为参数传递的复合文字指针

c++ - 如何将二进制数据文件中的信息读入结构数组 C++

c - 编写一个函数来检查其参数(正整数)是否是完全平方数。然后将此函数应用于正整数 vector

ios - 循环中单个像素的渲染速度

c++ - 如何从字符串末尾提取数字

计算谐波级数部分和时累积的计算错误

c - C中的这个指针是什么?

java - 使用两个不同 while 循环中的变量

java - 增强的 for 循环中的空值检查