【问题标题】:What is the difference in the Code Snippets?代码片段有什么区别?
【发布时间】:2018-11-13 11:44:33
【问题描述】:

我是操作系统的初学者,我正在尝试理解一些代码 sn-ps。你能解释一下这些代码sn-ps之间的区别吗?

int sum_array_rows(int a[M][N])
 {
    int i,j,sum=0;
    for(i=0;i<M;i++)
      for(j=0;j<N;j++)
        sum+=a[i][j];
    return sum;
  }

int sum_array_col(int a[M][N])
 {
    int i,j,sum=0;
    for(i=0;i<N;i++)
      for(j=0;j<M;j++)
        sum+=a[i][j];
    return sum;
  }

不同的部分是双 For 属性 他们中的一个应该比另一个更快吗?如果可以,请您解释一下原因,因为我不明白。

【问题讨论】:

  • 1.您的代码将导致 N 和 M 不相等的溢出错误,较低的代码将 i 视为列但您已将其用作行索引,其次在大多数现代操作系统上不会有性能差异
  • 那么第一个比第二个好?对不起,我不完全理解。 @SiddharthChabra
  • 在第二个中,当 i 从 0 到 N-1 变化时,您使用 sum+=a[i][j],但您的数组是 a[M][N],因此您的代码不正确。
  • 什么?这与操作系统有什么关系?
  • 哦,我想我明白了!非常感谢!! @SiddharthChabra

标签: arrays stride cache-locality


【解决方案1】:

正如其他人所说,如果数组维度不同,第二个代码sn-p会导致溢出错误,因此需要修复这个问题。

但是,由于多维数组的元素在内存中的存储方式以及现代 CPU 的缓存架构,在最内层循环中遍历最后一个数组维度可能更快。

此处要搜索的术语是“缓存位置”和“数组步长”

【讨论】:

    【解决方案2】:

    在第一个例子中:

    i 将获得值 0, 1, 2, ..., M-1

    j 将获得值 0, 1, 2, ..., N-1

    所以sum 计算为

    sum = a[0][0] + a[0][1] + a[0][2] + ... + a[0][N-1] +
          a[1][0] + a[1][1] + a[1][2] + ... + a[1][N-1] +
          a[2][0] + a[2][1] + a[2][2] + ... + a[2][N-1] +
          ...
          ...
          a[M-1][0] + a[M-1][1] + a[M-1][2] + ... + a[M-1][N-1]
    

    在第二个例子中,这已经被切换了

    i 将获得值 0、1、2、...、N-1

    j 将获得值 0, 1, 2, ..., M-1

    现在

    sum = a[0][0] + a[0][1] + a[0][2] + ... + a[0][M-1] +
          a[1][0] + a[1][1] + a[1][2] + ... + a[1][M-1] +
          a[2][0] + a[2][1] + a[2][2] + ... + a[2][M-1] +
          ...
          ...
          a[N-1][0] + a[N-1][1] + a[N-1][2] + ... + a[N-1][M-1]
    

    注意第二个版本是错误的因为参数是int a[M][N],即合法的第一个索引是0..M-1,合法的第二个索引是0..N-1 换句话说,如果N和M不同第二个版本越界访问数组。

    为了使第二个例子正确。这行sum+=a[i][j]; 应该是sum+=a[j][i];,所以sum 现在是:

    sum = a[0][0] + a[1][0] + a[2][0] + ... + a[M-1][0] +
          a[0][1] + a[1][1] + a[2][1] + ... + a[M-1][1] +
          a[0][2] + a[1][2] + a[2][2] + ... + a[M-1][2] +
          ...
          ...
          a[0][N-1] + a[1][N-1] + a[2][N-1] + ... + a[M-1][N-1]
    

    通过该更改,两个版本在功能上相同,即产生相同的结果。它们仅在添加元素的顺序上有所不同。

    由于二维数组的内存布局和缓存系统的工作方式,第一个版本的性能可能比第二个更好。另一方面,编译器可能会优化这两个版本,使其性能相同。

    【讨论】:

    • 我不认为这个编码错误是问题的重点——它询问的是遍历“行然后列”是否比“列然后行”更好或更差。
    • @simon3270 你可能是对的。我错过了。太糟糕了,OP(可能)犯了一个转移焦点的错字。无论如何,我将其添加到答案中
    • 没问题 - 我只是因为错过了 M/N 错误才发现这一点!
    【解决方案3】:

    仅当 MN 的值为 equal 时,这两个代码的工作原理相似,否则两个代码块不同。

    案例一:-看下面的代码块

    int sum_array_rows(int a[M][N]) {
        int i,j,sum=0;
        for(i=0;i<M;i++)
          for(j=0;j<N;j++)
            sum+=a[i][j];
        return sum;
    }
    

    这里a 是一个由M 行和N 列组成的数组,您正在通过sum+=a[i][j] 对每个行列元素进行求和。这是一个很好的代码,因为外循环旋转等于行数,内循环旋转等于列数。

    Case-2 :- 现在看第二个代码块,它会导致溢出。

    int sum_array_rows(int a[M][N]) {
        int i,j,sum=0;
        for(i=0;i<N;i++)
          for(j=0;j<M;j++)
            sum+=a[i][j];
        return sum;
    }
    

    这里a 也是M 行和N 列的数组。您的第一个外部 for 循环从 0 旋转到 N 但您只有 M 行。当你做sum+=a[i][..] 时,如果MN 不相同,就会产生一个大问题。例如M2N5 即它就像int a[2][5] 和外部循环从0 迭代到5,你继续做

    • sum+=a[0][j] 然后

    • sum+=a[1][j] 直到这一切都很好(bcz M=2),但什么时候会发生

    • sum+=a[2][j]sum+=a[3][j] 等然后有一个问题,因为没有 a[2][j]a[3][j] 退出,所以你试图访问导致 未定义行为.

    所以上面两个代码块只有在MN相同时才相同,否则两者不同。

    首先第二个代码块是错误的,但你可以通过 sum+=a[j][i] 而不是 sum+=a[i][j] 来纠正它

    int sum_array_rows(int a[M][N]) {
        int i,j,sum=0;
        for(i=0;i<N;i++)
          for(j=0;j<M;j++)
            sum+=a[j][i];
        return sum;
    }
    

    正如其他人所说,由于 2D 数组的内存布局和缓存系统的工作方式,第一个版本的性能可能比第二个更好。另一方面,编译器可能会优化这两个版本,使其性能相同。

    【讨论】:

      猜你喜欢
      • 2021-01-06
      • 2021-12-17
      • 2013-05-13
      • 1970-01-01
      • 2022-12-12
      • 2013-03-29
      • 1970-01-01
      • 2020-01-04
      相关资源
      最近更新 更多