在C中获取多维数组列的最有效方法

时间:2010-07-14 08:34:09

标签: c performance matrix

我正在尝试用C创建一个矩阵数据结构。我有一个结构,并且在这个结构中有货物部分(数据)的二维空指针数组(大小是在堆上动态定义的)。

给定列索引,我想在一维数组中获取此列的值。使用for或while循环很容易实现。但是如果此矩阵中的行数为N,那么获取列向量需要花费O(N)时间。我可以通过memcpy等内存操作更有效地完成这项工作吗?否则我怎样才能提高性能(我的数据非常结构化,我需要将它存储在某种矩阵中)。

4 个答案:

答案 0 :(得分:4)

如果列中的行数为N,则无法在比O(N)更短的时间内复制,读取或以其他方式操作整列。这是一个坚定的下限;必须考虑每个元素,并且有N个。

所以不,你不能比O(N)更快。

请注意,对于已知大小的2D数组,编译器将x[3][5]翻译为x+((3*num_cols)+5)*size_of_element。因此,提高阵列速度的一种方法是删除其动态大小。

另一个重要的一点是顺序访问内存并不总是最快 - 所以只需将数组旋转90度就不一定能给你带来最好的效果。查看阻止作为优化技术。底线:最佳内存布局取决于您的访问模式和硬件参数,如缓存行长度和缓存大小。

答案 1 :(得分:1)

正如Borealid所说,你无法改善O(N)。但是,如果重新排序数据,则可以加快复制操作,以便行是列,列是行。这将允许您使用memcpy复制数据。

答案 2 :(得分:1)

我的解决方案:

  1. 不要使用多维数组。它们在C99之前是不灵活的(不能改变所有尺寸)并且排除了如下的高效操作。相反,只需使用一维数组并自己进行元素索引算法。

  2. 现在,您可以设置指向列的第一个元素src)的指针src = &matrix[row*ncols+col];,并使用以下代码复制列:for (i=0; i<nrows; i++, src+=ncols) dest[i] = *src;

    < / LI>

答案 3 :(得分:1)

如果要复制矩阵中的数据,无论是行还是列,都不能在O(N)时间内完成,除了硬件功能可能有用的小N.

但是,如果你的矩阵是不可变的,你可以使用烟雾和镜子来产生一个单独的列向量的假象。

下面的代码直接输入到答案文本框,甚至没有编译过。使用风险自负!

您的矩阵类型被定义为结构:

typedef struct 
{
    unsigned int refCount;  // how many Matrixes are referencing this data ref
    size_t lineWidth;       // number of doubles between element at row = n, col = 0 and row = n +1, col = 0 
    double* data;           // the actual data
} DataRef;

typedef struct
{
    size_t rows;            // num rows in matrix
    size_t cols;            // num cols in matrix
    size_t dataOffset;      // offset in doubles from the start of data of element at row = 0, col = 0
    DataRef* data;
} Matrix;

创建一个全新的矩阵(我省略了所有错误处理以使其更简单)。

Matrix* matrix_create(size_t rows, size_t cols, const double* values)
{
    Matrix* ret = calloc(1, sizeof *ret);
    ret->rows = rows;
    ret->cols = cols;
    ret->dataOffset = 0;
    ret->data = calloc(1, sizeof *dataRef);
    ret->data->lineWidth = cols;
    ret->data->data = allocateAndCopy(rows * cols, values); // mallocs a new block of doubles big enough for the values
    ret->data->refCount = 1;
    return ret;
}

访问元素(同样没有错误处理,例如边界错误)

double matrix_elementAt(Matrix* matrix, size_t row, size_t col)
{
    size_t offset = matrix->dataOffset + row * matrix->data->lineWidth + col;
    return *(matrix->data->data + offset);
}

从另一个矩阵的矩形区域创建一个新矩阵(同样需要进行错误处理)

Matrix* matrix_createFromRegion(Matrix* old, size_t startRow, size_t startCol, size_t rows, size_t cols)
{
    Matrix* ret = calloc(1, sizeof *ret);
    ret->rows = rows;
    ret->cols = cols;
    ret->dataOffset = old->dataOffset + startRow * old->dataLineWidth + startCol;
    ret->data = old->data;
    ret->data->refCount++;
    return ret;
}

从另一个矩阵中的列创建新矩阵:

Matrix* vector = matrix_createFromRegion(aMatrix, 0, colYouWant, matrix_numRows(aMatrix), 1);

释放矩阵

void matrix_free(Matrix* aMatrix)
{
    if (aMatrix->data->refCount == 1)
    {
        free(aMatrix->data->data);
        free(aMatrix->data);
    }
    else
    {
        aMatrix->data->refCount--;
    }
    free(aMatrix);
}

如果你想要可变矩阵,每次修改元素时,检查refCount,如果它大于1,则在修改之前复制DataRef(减去旧dataRef上的refCount),否则修改dataRef。

现在上面使用了大量的mallocs,因此效率可能低于小矩阵的朴素实现。但是,您可以维护一个未使用的DataRef结构和Matrix结构的列表,而不是在完成后释放它们,将它们放在空闲列表中。分配新的结构时,从空闲列表中获取结构,除非它们是空的。这样,获得表示现有矩阵列的矩阵通常会花费不变的时间。

相关问题