【问题标题】:Optimize a basic nested loop in C by writing the loop通过编写循环优化 C 中的基本嵌套循环
【发布时间】:2020-06-14 04:44:33
【问题描述】:

当前循环是:

#define N 3000
...
int i, j;
int a[N][N], b[N][N], c[N]; 
// Fill in b and c with random values

for (i = 0; i < n; ++i) {
  for (j = 0; j < n; ++j) {
    a[i][j] = b[i][j] / c[i];
  }
}

我的优化版本同时展开了外循环和内循环:

for (int i = 0; i < N; i += 2) {
    for (int j = 0; j < N; j += 2) {
      a[i][j] = b[i][j] / c[i];
      a[i][j + 1] = b[i][j + 1] / c[i];
      a[i + 1][j] = b[i + 1][j] / c[i + 1];
      a[i + 1][j + 1] = b[i + 1][j + 1] / c[i + 1];
    }
  }

但是,我的导师说第二个循环没有优化得很好。 c(i) 的指示应该从 j 上的循环中取出。通过更改索引的顺序来优化循环。这样一来,您就可以在内循环中对内存进行一次扫描,而不是进行曲折式的搜索。

我仍然不确定他的意思,因为更改索引的顺序仍然会使循环以之字形遍历。这种情况的正确解决方案应该是什么?

【问题讨论】:

  • 这不清楚:“循环是通过改变索引的顺序来优化的。”是对原始代码(问题中显示的第一个代码)还是“我的优化版本”(问题中显示的第二个代码)的评论?看起来代码确实是这样,通常最好在j 上展开循环,而i 上的循环并不需要太多关注。
  • 展开“i”部分会导致更多的缓存未命中。由于内存布局,最好展开“j”部分。
  • @technosaurus 你的意思是将 j 切换为外循环?
  • 不,那会更糟。省略 i+1 展开。它会在每个循环中导致缓存未命中。对于多维数组,内循环应该在最右边的索引之上。展开超过缓存行大小很少显示改进(通常约为 64 字节),但展开至少 16 字节的数据访问有时可以帮助编译器使用 SIMD 指令。

标签: c loops optimization nested-loops


【解决方案1】:

int ci = c[i]; 放在外循环中,内循环除以ci. 请注意,任何合理的编译器都会为您执行此操作。

【讨论】:

  • 另外,如果这是 FORTRAN,倒转循环顺序是合适的。
  • 不应假设编译器会为您执行此操作。问题中的代码是一个简化的示例。在实践中,像这样的循环经常出现在单独翻译单元的例程中。在这样的例程中,编译器在编译时无法知道它传递的c是否与a的任何行相同,因此它无法知道某些a[i][j] = …是否改变了c[i],等等将c[i] 视为循环不变量的优化将是一个错误。 (这可以通过在运行时插入代码来测试地址来克服,程序员或编译器都可以这样做。)
  • 是的,但我正在寻找通过改变索引顺序来改善循环,就像我的导师说的那样。
【解决方案2】:

我不确定您的讲师在寻找什么,但您可以在此处使用称为 Duff's Device 的相当知名的 C 技术来帮助加快展开循环:

  init_arrays();

  precomputed_n = (N + 7) / 8;

  for(i = 0 ; i < N ; ++i)
    {
    to = a[i];
    from = b[i];
    ci = c[i];

    n = precomputed_n;

    switch(N % 8)
      case 0: do { *to++ = *from++ / ci;
      case 7:      *to++ = *from++ / ci;
      case 6:      *to++ = *from++ / ci;
      case 5:      *to++ = *from++ / ci;
      case 4:      *to++ = *from++ / ci;
      case 3:      *to++ = *from++ / ci;
      case 2:      *to++ = *from++ / ci;
      case 1:      *to++ = *from++ / ci;
                 } while (--n > 0);
    }

Duff 的设备是展开循环的便捷方式,它结合了 while 循环和 switch

Try it online!

【讨论】:

    猜你喜欢
    • 2013-05-30
    • 2021-01-21
    • 2015-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-28
    相关资源
    最近更新 更多