2D阵列访问时间比较

时间:2014-03-03 18:47:37

标签: c++ c performance

我有两种构建2D数组的方法:

int arr[NUM_ROWS][NUM_COLS];
//...
tmp = arr[i][j]

和扁平数组

int arr[NUM_ROWS*NUM_COLS];
//...
tmp = arr[i*NuM_COLS+j];

我正在进行图像处理,因此即使访问时间有所改善也是必要的。哪一个更快?我想第一个,因为第二个需要计算,但第一个需要两个寻址,所以我不确定。

5 个答案:

答案 0 :(得分:2)

我认为没有任何性能差异。在这两种情况下,系统将分配相同数量的连续内存。对于计算i*Numcols+j,您可以为1D数组声明执行此操作,或者系统将在2D情况下执行此操作。唯一关心的是易用性。

答案 1 :(得分:1)

在优化标准代码时,您应该相信编译器的功能。

此外,您应该信任具有快速数字乘法指令的现代CPU。

不要费心去使用其中一种!

我 - 几十年前 - 通过使用指针而不是使用2d阵列计算来极大地优化了一些代码 - >但这将a)只有在存储指针的选项时才有用 - 例如在一个循环和b)具有低影响因为我猜现代cpu应该在一个周期内进行2d阵列访问?值得测量!可能与数组大小有关。

在任何情况下,如果适用,使用ptr ++或ptr + = NuM_COLS的指针肯定会更快一点!

答案 2 :(得分:0)

第一种方法几乎总是更快。总的来说(因为总是存在极端情况)处理器和内存架构以及编译器可能内置了优化以帮助2d数组或其他类似的数据结构。例如,GPU针对矩阵(2d数组)数学进行了优化。

所以,一般来说,如果可能的话,我会允许编译器和硬件优化你的内存和地址算法。

...我同意@Paul R,在性能方面还有比数组分配和地址算法更重要的考虑因素。

答案 3 :(得分:0)

由于您的目标是图像处理,因此我认为您的图像对于静态数组来说太大了。关于动态分配数组的正确问题

在C / C ++中,有多种方法可以分配动态2D数组How do I work with dynamic multi-dimensional arrays in C?。为了使这个在C / C ++中工作,我们可以使用malloc和cast(对于C ++,你可以使用new)

方法1:

int** arr1 = (int**)malloc(NUM_ROWS * sizeof(int*));
for(int i=0; i<NUM_ROWS; i++)
    arr[i] = (int*)malloc(NUM_COLS * sizeof(int));

方法2:

int** arr2 = (int**)malloc(NUM_ROWS * sizeof(int*));
int* arrflat = (int*)malloc(NUM_ROWS * NUM_COLS * sizeof(int));
for (int i = 0; i < dimension1_max; i++)
  arr2[i] = arrflat + (i*NUM_COLS);

方法2基本上创建了一个连续的2D数组:即arrflat[NUM_COLS*i+j]arr2[i][j]应具有相同的性能。但是,由于arrflat[NUM_COLS*i+j]不是连续的,因此不应期望方法1中的arr[i][j]arr1具有相同的性能。但是,方法1似乎是最常用于动态数组的方法。

一般来说,我使用arrflat[NUM_COLS*i+j]所以我不必考虑如何分配动态2D数组。

答案 4 :(得分:0)

有两种情况需要考虑:编译时间定义和数组大小的运行时定义。性能差异很大。

静态分配,全局或文件范围,固定大小数组:
编译器知道数组的大小并告诉链接器在数据/内存部分中分配空间。这是最快的方法。

示例:

#define ROWS 5
#define COLUMNS 6
int array[ROWS][COLUMNS];
int buffer[ROWS * COLUMNS];

运行时分配,功能本地范围,固定大小数组:
编译器知道数组的大小,并告诉代码在数组的本地内存(a.k.a.堆栈)中分配空间。通常,这意味着向堆栈寄存器添加值。通常有一两条指令。

示例:

void my_function(void)
{
  unsigned short my_array[ROWS][COLUMNS];
  unsigned short buffer[ROWS * COLUMNS];
}

运行时分配,动态内存,固定大小数组:
同样,编译器已经计算了数组所需的内存量,因为它是以固定大小声明的。编译器发出代码以调用具有所需数量的内存分配函数(通常作为参数传递)。由于函数调用和查找一些动态内存(可能是垃圾收集)所需的开销,有点慢。

示例:

void another_function(void)
{
  unsigned char * array = new char [ROWS * COLS];
  //...
  delete[] array;
}

运行时分配,动态内存,可变大小:
无论数组的大小如何,编译器都必须发出代码来计算要分配的内存量。然后将该数量传递给内存分配函数。由于计算大小所需的代码,比上面慢一点。

示例:

int * create_board(unsigned int rows, unsigned int columns)
{
  int * board = new int [rows * cols];
  return board;
}