潜在的长循环和内部声明变量
Potentially long loop and declaring variables inside
我最近写了一个动态程序,可以计算两个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编译了它。
为什么上面的代码慢得多?
考虑内部循环的每次迭代中必须发生的各种分配/解除分配。
- 在堆栈上分配一对对象
- 分配四个空字符串,每个字符串可能在堆上分配一个1字节
- 四个字符串分配,可能需要4个堆释放和新的分配
- 破坏涉及4个堆释放的字符串
- 对对象的破坏
忽略堆栈分配(应该相对便宜),即总共8个堆分配和另外8个释放(或者4/4的最佳情况)。如果这是一个调试构建,那么在检查每个堆操作时可能会有额外的开销。
如果您的sizeX/sizeY常量为2400,那么您总共要执行92000000次堆操作。如果幸运的话,每一个操作都需要大约相同的时间,因为每个循环都分配了相同大小的对象。如果运气不好,那么由于堆碎片,一些堆操作可能需要更长的时间才能完成。
正如您所发现的,显而易见的解决方案是将变量定义和赋值放在循环之外。您只需要在循环中某个时刻覆盖对值时重新分配对值。
一般答案:看起来你在使用gcc(也就是说g++);你总是可以做g++-S[stuff]来看看g++对你的代码做了什么(假设你能很好地阅读汇编)。
具体答案:我很惊讶差异是40倍,但在你的代码中,每次你完成一个循环,它必须调用create_new_pair两次(我本以为它必须做一点清理才能"释放"旧对,但考虑到它在堆栈上,我想这并不像我想象的那么难,或者至少我没有看到它……从Gcc读取代码过去比现在读C++代码容易得多)
这可能是因为变量是一个对象。由于p和d不是基元类型,除非编译器内联它的构造函数和析构函数(如果使用-O3而不是-O2,则可能发生这种情况),否则它将在每次迭代中构造和析构两个std::对(以及四个std::字符串)。如果它是一个基元变量(如int),即使您没有启用内联优化,编译器也可以对此进行优化。
编辑:请注意,由于std::string在内部使用堆分配,所以即使是内联也不会优化这些分配(但内联仍会节省一些开销)。对于std::对int和-O3,循环内外的性能应该相同。
在一种编译过的语言中,如果有一个好的编译器(至少可以进行平庸的优化),在循环中声明变量永远不会是"失败者",而且通常(尤其是对于只有适度优化的编译器)会是"胜利者"。
不过,对于解释语言来说,情况可能会有所不同。每次通过循环,解释器都需要分配变量位置,这可能会很昂贵。
在编译环境设计不佳的情况下,在堆栈上分配变量的成本很高,也可能出现"失败者"的情况。
尽管在任何一种情况下,我都无法解释40:1的差异。我怀疑省略的代码可能包含一些重要的线索。
[啊,在重读(可能还有海报的重新编辑)时,我发现这不仅仅是变量DECLARATION,而是OBJECT的创建被移出了循环。]
- C++:在函数内部声明数组时,表达式必须具有常数值
- 正在为循环内部声明向量
- (C++)返回函数内部声明的对象的地址
- 稍后在程序中使用if-else构造内部声明的变量会导致未声明的标识符错误
- C++在if-else内部声明派生类对象,并在外部使用它
- 在类内部声明结构
- 在条件中使用do-while循环内部声明的变量
- 在结构定义内部声明结构的堆栈
- 使用外部类内部声明的数据类型
- 在C++中的类的函数内部声明变量
- C++-在函数内部声明数组
- QByteArray的外部与内部声明
- C++ 结构内部声明混淆
- 在类外部使用在类内部声明的类型定义枚举
- 在函数内部声明函数的用法
- 模板成员函数的参数推导不适用于函数内部声明的类
- 函数内部声明的静态变量的值是多少
- 潜在的长循环和内部声明变量
- 在类内部声明模板化函数(基于容器类型),在模板类外部定义模板化函数(基于容器类型)
- 在函数内部声明函数时出现语法错误