对不起,可能是太抽象的问题,但对我来说很实用+可能是一些专家有类似的经验,可以解释它。
我有一个大代码,大约10000行大小。
我注意到如果我在某个地方
if ( expression ) continue;如果表达式为,则始终为假(使用代码和cout的逻辑进行双重检查),但依赖于未知参数(因此编译器在编译过程中不能简单地摆脱这一行),程序的速度提高了25% (计算结果相同)。如果测量回路本身的速度,则加速因子大于3。
为什么会发生这种情况,如果没有这样的技巧,有什么可能利用这种加速的可能性呢?
我使用gcc 4.7.3,-O3优化。
更多信息:
如果有人知道材料,这可以解释这种行为而无需猜测,我将非常高兴地阅读和接受他们的答案。
发布于 2013-10-28 20:47:32
找到了。
原因是在下面的条件运算符中。代码看起来如下:
for ( int i = a; ; ) {
// small amount of calculations, and conditional calls of continue;
if ( expression ) continue;
// calculations1
if ( expression2 ) {
// calculations2
}
// very big amount calculations, and conditional calls of continue;
}expression2的值几乎总是假的。所以我就这样改变了:
for ( int i = a; ; ) {
// small amount of calculations, and conditional calls of continue;
// if ( expression ) continue; // don't need this anymore
// calculations1
if ( __builtin_expect( !!(expression2), 0 ) ) { // suppose expression2 == false
// calculations2
}
// very big amount calculations, and conditional calls of continue;
}并且得到了预期的25%的提速。甚至再多一点。行为不再取决于临界线。
我不知道该如何解释,也找不到足够的资料来预测分支。
但是我想重点是应该跳过calculations2,但是编译器不知道这一点,并且假设expression2 ==默认为true。同时,它假设在简单的连续检查中
if ( expression ) continue;表达式== false,并且很好地跳过calculations2,在任何情况下都必须这样做。如果在下面,如果我们有更复杂的操作(例如cout),则假设表达式是真的,而技巧不起作用。
如果有人知道材料,这可以解释这种行为,无需猜测,我将非常高兴地阅读和接受他们的答案。
发布于 2013-10-28 16:24:13
不可能到达的分支的引入破坏了流程图。通常,编译器知道执行的流程是从循环的顶部直接到退出测试,然后再回到起点。现在图中有一个额外的节点,在那里流可以离开循环。现在,它需要以不同的方式编译循环体,分为两部分。
这几乎总是导致更糟糕的代码。为什么不在这里,我只能给出一个猜测:您没有使用分析信息进行编译。因此,编译器必须做出假设。特别是,它必须对在运行时获取分支的可能性作出假设。
显然,由于它必须做出的假设是不同的,因此产生的代码很有可能在速度上有所不同。
发布于 2013-10-28 21:46:57
我不想这么说,但答案将是非常技术性的,更重要的是,对您的代码非常具体。如此之多,可能除了你自己之外,没有人会花时间去调查你问题的根源。正如其他人所建议的,它很可能依赖于分支预测和其他与管道相关的编译后优化。
如果这是编译器优化问题或编译后优化(CPU)优化,我唯一可以建议的帮助您缩小范围,就是再次编译您的代码,使用-O2 vs -O3,但这一次添加以下附加选项:-fverbose-asm -S。将每个输出导入两个不同的文件,然后运行类似sdiff的命令来比较它们。你应该看到很多不同之处。
不幸的是,如果不对汇编代码有很好的理解,就很难理解它的正面或反面,老实说,没有多少人在堆栈溢出上有耐心(或时间)花在这个问题上的时间超过了几分钟。如果您不熟悉程序集(大概是x86),那么我建议您找一位同事或朋友来帮助您解析程序集输出。
https://stackoverflow.com/questions/19639796
复制相似问题