在尝试估算push_back
和std::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(预发布)
答案 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
现在我将简要介绍调查情况:
快速和慢速有什么区别?在慢速版本中,我们对emplace_back_aux
内的bar()
无效调用,当你的foo被取消注释时,这是神奇的内联。所以我们可以得出结论,这个酒吧非常热,内联在这里很脏。而且很可能所有这些错误都是关于内联的。
现在使用选项-fdump-ipa-inline-details
可以查看内联转储。您将看到不同的时间/尺寸考虑因素。它很难阅读,我不想在这里粘贴所有细节。但是研究这些信息的一般结果是:GCC认为,模块大小的增长(百分比)不值得估计加速。
怎么办?两种可能性:
3.1。使用未使用的foo
代码增加模块大小和整体加速估计,即使用正确的类型(如insert_iterator)调用emplace_back,移动比率更大并达到内联限制(注意这种方式非常不稳定 - 一切都可能爆炸在其他具有改进的内联算法的编译器版本中,您还需要非常幸运地猜测代码可以工作)。
3.2。或者移动内联限制。我通过参数提供给GCC的是&#34;考虑使用较少的加速来内联甚至大功能请#34;
就是这样。 GCC中有很多其他参数以及其他可以用它们做的技巧。