【问题标题】:Performance optimization nested loops性能优化嵌套循环
【发布时间】:2018-10-08 03:48:51
【问题描述】:

我正在实现一个相当复杂的代码,并且在其中一个关键部分中,我需要基本上考虑遵循特定规则的所有可能的数字字符串。解释我所做的事情的幼稚实现将是这样一个嵌套循环实现:

std::array<int,3> max = { 3, 4, 6};
for(int i = 0; i <= max.at(0); ++i){
    for(int j = 0; j <= max.at(1); ++j){
       for(int k = 0; k <= max.at(2); ++k){  
           DoSomething(i, j, k);
       }
     }
 }

显然我实际上需要更多的嵌套,并且“最大”规则更复杂,但我认为这个想法很清楚。 我使用递归函数方法实现了这个想法:

std::array<int,3> max = { 3, 4, 6};
std::array<int,3> index = {0, 0, 0};
int total_depth = 3;
recursive_nested_for(0, index, max, total_depth);

在哪里

void recursive_nested_for(int depth, std::array<int,3>& index,
                     std::array<int,3>& max, int total_depth)
{
    if(depth != total_depth){
        for(int i = 0; i <= max.at(depth); ++i){
            index.at(depth) = i;
            recursive_nested_for(depth+1, index, max, total_depth);
        }
    }
    else
        DoSomething(index);  
}

为了尽可能的节省,我在实际代码中声明了所有我使用的全局变量。

由于这部​​分代码需要很长时间,是否可以做任何事情来加快它的速度? 如果有必要,我也愿意写 24 嵌套,至少可以避免开销! 我认为也许像表达式模板这样在编译时实际生成这些嵌套的方法可能更优雅。但有可能吗? 任何建议将不胜感激。 谢谢大家。

【问题讨论】:

  • 三个嵌套循环将导致 O(N^3) 的时间复杂度。你确定你的算法已经尽可能地优化了吗?

标签: c++ templates recursion optimization expression


【解决方案1】:

recursive_nested_for() 是个好主意。它现在写的有点不灵活。但是,您可以将std::vector&lt;int&gt; 用于数组维度和索引,或者将其作为模板来处理任何大小std::array&lt;&gt;。如果编译器知道递归的深度,它可能能够内联所有递归调用,然后它可能与三个嵌套的 for 循环一样高效。

另一种选择是使用单个 for 循环来递增需要递增的索引:

void nested_for(std::array<int,3>& index, std::array<int,3>& max)
{
    while (index.at(2) < max.at(2)) {
      DoSomething(index);

      // Increment indices
      for (int i = 0; i < 3; ++i) {
          if (++index.at(i) >= max.at(i))
             index.at(i) = 0;
          else
             break;
      }
   }
}

但是,您也可以考虑创建一个线性序列来访问迭代器 i、j、k 等的所有可能组合。例如,对于数组维度{3, 4, 6},有 3 * 4 * 6 = 72 种可能的组合。因此,您可以有一个从 0 到 72 的计数器,然后将该计数器“拆分”为您需要的三个迭代器值,如下所示:

for (int c = 0; c < 72; c++) {
    int k = c % 6;
    int j = (c / 6) % 4;
    int i = c / 6 / 4;
    DoSomething(i, j, k);
}

您可以将其推广到任意多个维度。当然,你拥有的维度越多,拆分线性迭代器的成本就越高。但是,如果您的数组尺寸是 2 的幂,那么这样做可能非常便宜。此外,您可能根本不需要拆分它;例如,如果您正在计算多维数组的所有元素的总和,您不需要关心实际索引i、j、k 等等,您只想访问所有元素一次。如果数组在内存中是线性布局的,那么你只需要一个线性迭代器。

当然,如果您有 24 个嵌套的 for 循环,您会注意到所有维度大小的乘积将变成一个非常大的数字。如果它不适合 32 位整数,那么您的代码将会非常慢。如果它不再适合 64 位整数,它将永远不会完成。

【讨论】:

  • 感谢您的回答,我可以解决!好吧,这段代码应该运行几天甚至几周,然后才能给我需要的几个数字,所以我知道我基本上是在与风车作斗争,但任何改进都可能值得!
  • 我很高兴您找到有用的答案!考虑投票和/或将其标记为已接受的答案,请参阅:stackoverflow.com/help/someone-answers
猜你喜欢
  • 1970-01-01
  • 2021-01-21
  • 1970-01-01
  • 1970-01-01
  • 2012-01-27
  • 2015-05-29
  • 2020-05-18
  • 1970-01-01
相关资源
最近更新 更多