内部可能存在长循环和声明变量

时间:2011-06-02 01:30:47

标签: c++ optimization loops dynamic overhead

我最近编写了一个动态程序,用于计算两个DNA链序列之间的相似性(修改的编辑距离)(可能很长)。

我的代码就像(自分配以来不是实际代码):

while(!file.eof){
   string line;
   int sizeY, sizeX;

   //get first strand
   getline(db, line)

   //second strand
   getline(db, line)

   double ** ary = new double[sizeY];
   //loop to initialize array

   for(i to sizeY)
   {
      for(i to sizex)
      {
            pair<string,string> p,d;
            p.first = "A";
            p.second = "T";
            d.first = "G";
            d.second = "C";
            //do some comparisons
      }
   }
}

上述代码大约需要40分钟才能完成约2400行的文件。 如果我移动对p,d和嵌套for循环之外的赋值并运行完全相同的文件,它将在大约1分钟内完成。

我在其他帖子中读过,性能几乎相同。我也用-O2编译了它。

为什么上面的代码要慢得多?

4 个答案:

答案 0 :(得分:2)

考虑必须在内循环的每次迭代中发生的各种分配/解除分配。

  1. 在堆栈中分配配对对象
  2. 分配四个空字符串,每个字符串可能在堆上分配1个字节
  3. 四个字符串分配,可能需要4次堆释放和新分配
  4. 破坏涉及4堆解除分配的字符串
  5. 破坏对象
  6. 忽略堆栈分配(应该相对便宜),总共有8个堆分配和另外8个解除分配(或4/4的最佳情况)。如果这是一个调试版本,则检查每个堆操作可能会有额外的开销。

    如果你的sizeX / sizeY常量是2400,那么你总共做了 9200万堆操作。如果你很幸运,每个循环都会分配相同大小的对象,因此每个操作都需要大约相同的时间。如果你运气不好,那么由于堆碎片,一些堆操作可能需要更长的时间才能完成。

    正如您所发现的,显而易见的解决方案是将变量定义和赋值放在循环之外。如果在某个时刻在循环内覆盖它们,则只需重新分配对值。

答案 1 :(得分:0)

通用答案:   看起来你正在使用gcc(也就是说g ++);你总是可以做g ++ -S [stuff]看看G ++对你的代码做了些什么(假设你可以很好地阅读汇编程序)。

具体答案:   我很惊讶差异是40倍,但在你的代码中,每次你完成一个循环,它必须调用create_new_pair两次(而且我认为它必须做一点清理才能“释放”旧的一对,但考虑到它在堆栈上,我想这并不像我想象的那么难,或者至少我没有看到它...从Gcc读取代码比阅读C ++代码要容易得多目前)

答案 2 :(得分:0)

这可能是因为变量是一个对象。由于p和d不是基本类型,除非编译器内联它的构造函数和析构函数(如果使用-O3而不是-O2可能会发生),它将构造并销毁两个std :: pair(因此四个std) :: string)每次迭代。如果它是一个原始变量(如int),即使您没有启用内联优化,编译器也可以优化它。

编辑: 请注意,由于std :: string内部使用堆分配,因此即使内联也不会优化这些分配(但您仍然可以使用内联节省一些开销)。对于带有-O3的std :: int对,在循环内部或外部的性能应该相同。

答案 3 :(得分:-1)

在具有良好编译器的编译语言(至少是平庸的优化)中,在循环内声明的变量永远不会成为“失败者”,并且经常(特别是对于只有适度优化的编译器)将是“赢家” ”

但是,对于解释型语言,它可能会有所不同。每次通过循环时,解释器都需要分配变量位置,这可能很昂贵。

你可能还有一个设计糟糕的编译环境的“失败者”情况,即在堆栈上分配变量的代价很高。

虽然在任何这些情况下,我都会无法解释40:1的差异。我怀疑省略的代码可能包含一些重要的线索。

<啊> [啊,在重新阅读时(可能还有海报的重新编辑)我发现它不仅仅是变量DECLARATION,而是在循环之外被移动的OBJECT创建。]