未使用的功能改变了性能

时间:2015-03-19 20:42:24

标签: c++ performance gcc

在尝试估算push_backstd::inserter之间的性能差异时,我遇到了一个非常奇怪的性能问题。

让我们考虑以下代码:

#include <vector>
using container = std::vector<int>;
const int size  = 1000000;
const int count = 1000;

#ifdef MYOWNFLAG
void foo(std::insert_iterator<container> ist)
{
    for(int i=0; i<size; ++i)
        *ist++ = i;
}
#endif

void bar(container& cnt)
{
    for(int i=0; i<size; ++i)
        cnt.push_back(i);
}
int main()
{
    container cnt;
    for (int i=0; i<count; ++i)
    {
        cnt.clear();
        bar(cnt);
    }
    return 0;
}

在这种情况下,无论是否定义了MYOWNFLAG,都不会调用函数foo。但是,此标志的值会对性能产生影响:

$ g++ -g -pipe -march=native -pedantic -std=c++11 -W -Wall -Wextra -Werror -O3 -o bin/inserter src/inserter.cc && time ./bin/inserter
./bin/inserter  4,73s user 0,00s system 100% cpu 4,728 total

$ g++ -g -pipe -march=native -pedantic -std=c++11 -W -Wall -Wextra -Werror -O3 -o bin/inserter src/inserter.cc -DMYOWNFLAG && time ./bin/inserter
./bin/inserter  2,09s user 0,00s system 99% cpu 2,094 total

请注意,如果我将foo的protopyte更改为使用std::back_insert_iterator,我会获得类似的效果,就好像我没有设置标记一样。

编译器的优化是怎么回事?

修改

我使用gcc 4.9.2 20150304(预发布)

Repoduced

  • 由stefan在ideone上转载
  • 由我在gcc 4.9.2
  • 的另一台机器上转载
  • 我没有在gcc 4.6.3和flag -std = c ++ 0x
  • 的另一台机器上复制

1 个答案:

答案 0 :(得分:10)

首先,我将向您展示如何在没有垃圾功能的情况下实现此目的的神奇技巧。然后我将向您展示为什么垃圾功能有效。所以诀窍:

原来无效(请注意我的机器快两倍):

g++ -g -pipe -march=native -pedantic -std=c++11 -W -Wall -Wextra -Werror -O3 -o bin/inserter src/inserter.cc --param inline-unit-growth=200 && time ./bin/inserter
real    0m2.197s
user    0m2.200s
sys     0m0.000s

现在进入技巧(你的定义仍处于非活动状态):

g++ -g -pipe -march=native -pedantic -std=c++11 -W -Wall -Wextra -Werror -O3 -o bin/inserter src/inserter.cc --param inline-min-speedup=2 && time ./bin/inserter
real    0m1.114s
user    0m1.100s
sys 0m0.010s

注意:区别在于看起来很奇怪的--param inline-min-speedup=2

现在我将简要介绍调查情况:

  1. 快速和慢速有什么区别?在慢速版本中,我们对emplace_back_aux内的bar()无效调用,当你的foo被取消注释时,这是神奇的内联。所以我们可以得出结论,这个酒吧非常热,内联在这里很脏。而且很可能所有这些错误都是关于内联的。

  2. 现在使用选项-fdump-ipa-inline-details可以查看内联转储。您将看到不同的时间/尺寸考虑因素。它很难阅读,我不想在这里粘贴所有细节。但是研究这些信息的一般结果是:GCC认为,模块大小的增长(百分比)不值得估计加速。

  3. 怎么办?两种可能性:

    3.1。使用未使用的foo代码增加模块大小和整体加速估计,即使用正确的类型(如insert_iterator)调用emplace_back,移动比率更大并达到内联限制(注意这种方式非常不稳定 - 一切都可能爆炸在其他具有改进的内联算法的编译器版本中,您还需要非常幸运地猜测代码可以工作)。

    3.2。或者移动内联限制。我通过参数提供给GCC的是&#34;考虑使用较少的加速来内联甚至大功能请#34;

  4. 就是这样。 GCC中有很多其他参数以及其他可以用它们做的技巧。