首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何理解棘手的提速

如何理解棘手的提速
EN

Stack Overflow用户
提问于 2013-10-28 16:12:46
回答 3查看 493关注 0票数 13

对不起,可能是太抽象的问题,但对我来说很实用+可能是一些专家有类似的经验,可以解释它。

我有一个大代码,大约10000行大小。

我注意到如果我在某个地方

代码语言:javascript
复制
if ( expression ) continue;

如果表达式为,则始终为假(使用代码和cout的逻辑进行双重检查),但依赖于未知参数(因此编译器在编译过程中不能简单地摆脱这一行),程序的速度提高了25% (计算结果相同)。如果测量回路本身的速度,则加速因子大于3。

为什么会发生这种情况,如果没有这样的技巧,有什么可能利用这种加速的可能性呢?

我使用gcc 4.7.3,-O3优化。

更多信息:

  1. 我试过两种不同的表达方式,两者都有效。
  2. 如果我将行更改为: 如果(表达式){ cout <<“HELLO”<< endl;继续;}; 提速已经过去了。
  3. 如果我将行更改为: 表达; 提速已经过去了。
  4. 围绕这一行的代码如下所示: 对于( int i= a;;){ do {i += d;if ( d*i > d*ilast )中断;//少量计算和条件调用继续;} while ( expression0 );if ( d*i > dir*ilast )中断;if (表达式)继续;//大量计算和条件调用的继续;} for循环看起来很奇怪。这是因为我修改了回路,以抓住这个瓶颈。最初表达式等于expression0,而不是do-循环,我只有这个继续。
  5. 为了理解分支预测,我尝试使用__builtin_expect。使用 //通过分支预测,表达式(= false)应该是真。如果( __builtin_expect(!!(表达式),1) )继续; 提速是25%。 //通过分支预测,表达式(= false)应该是false。如果( __builtin_expect(!!(表达式),0) )继续; 提速已经过去了。
  6. 如果我使用-O2而不是-O3,效果就消失了。该代码比带有虚假条件的快速O3版本稍慢(~3%)。
  7. 同样适用于"-O2 -finline-functions fun开关-循环-f预测-共同-fgcse后-重新加载-ftree-矢量化“。再加上一个选项:"-O2 -finline-functions funswitch-loops fgcse-在重新加载后-ftree-vectorize -fipa克隆“-效果被放大。对于“行”,速度是相同的,没有“行”,代码会慢75%。
  8. 原因就在条件运算符后面。代码看起来如下: 对于( int i= a;;){ //少量计算和条件调用继续;if (表达式)继续;// calculations1 if ( expression2 ){ // calculations2 } //很大的计算量,以及条件调用的继续}; expression2的值几乎总是假的。所以我就这样改变了: 对于( int i= a;;){ //少量的计算和条件调用继续;// if (表达式)继续;//不再需要这个// calculations1 if ( __builtin_expect(!( expression2 ),0)){ //假设expression2 == false // calculations2 } //大量计算和条件调用的继续;} 并且得到了预期的25%的提速。甚至再多一点。行为不再取决于临界线。

如果有人知道材料,这可以解释这种行为而无需猜测,我将非常高兴地阅读和接受他们的答案

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-10-28 20:47:32

找到了。

原因是在下面的条件运算符中。代码看起来如下:

代码语言:javascript
复制
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的值几乎总是假的。所以我就这样改变了:

代码语言:javascript
复制
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。同时,它假设在简单的连续检查中

代码语言:javascript
复制
if ( expression ) continue;

表达式== false,并且很好地跳过calculations2,在任何情况下都必须这样做。如果在下面,如果我们有更复杂的操作(例如cout),则假设表达式是真的,而技巧不起作用。

如果有人知道材料,这可以解释这种行为,无需猜测,我将非常高兴地阅读和接受他们的答案。

票数 3
EN

Stack Overflow用户

发布于 2013-10-28 16:24:13

不可能到达的分支的引入破坏了流程图。通常,编译器知道执行的流程是从循环的顶部直接到退出测试,然后再回到起点。现在图中有一个额外的节点,在那里流可以离开循环。现在,它需要以不同的方式编译循环体,分为两部分。

这几乎总是导致更糟糕的代码。为什么不在这里,我只能给出一个猜测:您没有使用分析信息进行编译。因此,编译器必须做出假设。特别是,它必须对在运行时获取分支的可能性作出假设。

显然,由于它必须做出的假设是不同的,因此产生的代码很有可能在速度上有所不同。

票数 0
EN

Stack Overflow用户

发布于 2013-10-28 21:46:57

我不想这么说,但答案将是非常技术性的,更重要的是,对您的代码非常具体。如此之多,可能除了你自己之外,没有人会花时间去调查你问题的根源。正如其他人所建议的,它很可能依赖于分支预测和其他与管道相关的编译后优化。

如果这是编译器优化问题或编译后优化(CPU)优化,我唯一可以建议的帮助您缩小范围,就是再次编译您的代码,使用-O2 vs -O3,但这一次添加以下附加选项:-fverbose-asm -S。将每个输出导入两个不同的文件,然后运行类似sdiff的命令来比较它们。你应该看到很多不同之处。

不幸的是,如果不对汇编代码有很好的理解,就很难理解它的正面或反面,老实说,没有多少人在堆栈溢出上有耐心(或时间)花在这个问题上的时间超过了几分钟。如果您不熟悉程序集(大概是x86),那么我建议您找一位同事或朋友来帮助您解析程序集输出。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19639796

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档