【问题标题】:What is the most optimized way to assign a value to a two dimensional array? [duplicate]将值分配给二维数组的最优化方法是什么? [复制]
【发布时间】:2021-05-11 03:58:37
【问题描述】:

我想知道给二维数组赋值的最佳方式是什么?

说,我想将值 12 分配给下面的数组。

for (int i = 0; i < m; i+++)
    for (int j = 0; j < n; j++)
        array[i][j] = 10

如果没有for 循环,有没有更好的方法来做到这一点?

【问题讨论】:

  • 这不是分配 a 值;它正在分配 m*n 值。
  • I want to assign value 12 好吧,然后分配12,而不是10What is the most optimized way“最优化的方法”是用汇编编写(如果优化速度)。那么,您使用什么衡量标准来确定什么是“最优化的方式”以及什么是“更好的方式”?
  • array 是如何声明的?你可以做一个循环而不是 2 个。

标签: c++ arrays c optimization multidimensional-array


【解决方案1】:

您的代码很好,可读且简单,除了拼写错误。只要确保ij 使用与nm 相同的类型即可。

#define N 10
#define M 10
    int array[M][N];
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < N; j++)
            array[i][j] = 0;
    }

以这种方式编写代码的优点是它适用于平面二维数组int array[M][N] 和数组数组int *array[M] 以及指向数组数组int **array 的指针。

如果数组是整数的平面二维数组并且值为0,则有另一种选择:

memset(array, 0, sizeof(array[0][0]) * M * N);

但它只适用于内存中所有位置都具有相同字节的非常特定的值。对于常规二进制补码架构的 0-1 以及 254 个其他不太明显的值(共 40 亿个),情况就是如此。

您还可以初始化一行并将其复制到以下行...

此类优化令人困惑、容易出错且不必要,因为modern optimizing compilers 会为此生成非常高效的代码:

#define M 10
#define N 10
void clear_matrix(int array[M][N]) {
    for (int i = 0; i < M; i++) {
        for (int j = 0; j < N; j++)
            array[i][j] = 0;
    }
}

注意事项:

  • 代码正确性总是胜过优化:快速获得错误结果的目的是什么?
  • 始终在优化之前进行基准测试和分析,并首先关注作为明显目标的瓶颈。
  • 矩阵初始化不太可能成为瓶颈。如果是这样,您的算法可能有问题。
  • 与微优化相比,找到更好的算法并降低复杂性是一项更有成效的工作。

【讨论】:

  • memset(array, 0, …); 不是… array[i][j] = 10 的好替代品。我在某处听说“代码的正确性总是胜过优化。”
  • @EricPostpischil:确实如此!我添加了一个额外的解释。
【解决方案2】:

这个问题几乎不可能回答。简单测试:

#define ROWS 1000
#define COLS 4000

int arr[ROWS][COLS];

void __attribute__((noinline)) set(int val)
{
    for(size_t col = 0; col < COLS; col++)
    {
        arr[0][col] = val;
    }
    for(size_t row = 1; row < ROWS; row++)
    {
        memcpy(arr[row], arr[0], sizeof(arr[0]));
    }
}

void __attribute__((noinline)) set1(int val)
{
    static int arr1[COLS];
    for(size_t col = 0; col < COLS; col++)
    {
        arr1[col] = val;
    }
    for(size_t row = 0; row < ROWS; row++)
    {
        memcpy(arr[row], arr1, sizeof(arr1));
    }
}


void __attribute__((noinline)) set2(int val)
{
    for(size_t row = 0; row < ROWS; row++)
        for(size_t col = 0; col < COLS; col++)
        {
            arr[row][col] = val;
        }
}


int main(void)
{
    clock_t begin, end;
    double time_spent;

    begin = clock();
    for(int x = 0; x < 1000; x++)
    {
        set(x);
    }

    end = clock();
    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;

    printf("%f\n", time_spent);

    begin = clock();

    for(int x = 0; x < 1000; x++)
    {
        set1(x);
    }
    end = clock();
    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf("%f\n", time_spent);


    begin = clock();

    for(int x = 0; x < 1000; x++)
    {
        set2(x);
    }
    end = clock();
    time_spent = (double)(end - begin) / CLOCKS_PER_SEC;
    printf("%f\n", time_spent);

}

Godbolt 显示循环方法最快: https://godbolt.org/z/K9Gcbs

1.162777
1.009047
0.973361

但是通过 ubuntu 20.4 64 位测试(相同的编译器版本,相同的编译器选项)给出的结果正好相反。

0.542433
0.502667
0.936434

因此结论是:始终进行基准测试。不要相信意见和神话。

【讨论】:

  • 每次我在 Godbolt 上刷新,我都会得到不同的排名。我怀疑您的微基准测试设计不佳。
  • 完全同意基准测试是必须的,过早的优化是邪恶的。我建议将 printf 语句延迟到所有测量完成后,并使用单独的较小数组来最大程度地减少缓存和 CPU 负载造成的副作用。
  • @CodyGray 但它们是一致的 - 总是 简单循环比其他循环更快。这就是重点。 Godbolt - 循环总是更快。 Ubuntu - 与 memcpy 方法相比总是更慢。
  • 他们不一致一致,这就是为什么我说“不同的排名”,而不是“不同的绝对时间”。
  • @chqrlie 这只是为了说明问题
猜你喜欢
  • 2015-06-20
  • 1970-01-01
  • 2017-02-20
  • 2021-04-03
  • 1970-01-01
  • 1970-01-01
  • 2014-09-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多