了解GCC内联

时间:2018-09-23 20:47:54

标签: c++ g++ openmp inline

我一直在尝试重构一些中等大小的低级代码,并且我不能说我对编译器优化器内联代码的方式太满意了。

我不太了解gcc如何内联代码,但是对于我的一种特殊情况,通过使用以下选项,我得到的运行时速度相当于gcc 8.2.1中的手写代码:

-std=c++17 -Winline 
-Ofast -march=native -DNDEBUG 
-finline-limit=100000 --param large-function-insns=10000 --param large-stack-frame-growth=1000 
--param inline-unit-growth=1000 --param early-inlining-insns=150 --param max-early-inliner-iterations=1000
-fopenmp -fPIC

没有内联选项,我的程序要慢3倍。我本来希望有一个更简单的选项来告诉编译器“相信我,当我说内联时,您必须内联它”。有这样的编译器选项吗?

注意:

  • 有关代码的一些细节:有3个嵌套的for循环,第三个是SIMD,每次迭代都在计算复杂的固定大小的线性代数。线性代数本身不是SIMD(因为上面的循环是SIMD)。大多数抽象处理多维数组和密集线性代数(需要表达式模板)。
  • 我要内联的所有函数都在编译单元中定义。我没有递归函数或虚函数,也没有抛出异常。我的函数是constexpr而不是内联,bu constexpr暗含隐式内联。没有第三方库调用(所有调用都是数学函数,例如std :: sqrt)。除了SIMD,没有并行性。
  • 在重构开始时,当内联函数仍然很少时,绝对没有问题。但是随着我添加越来越多的内联函数来对代码进行抽象,编译器开始努力进行内联(似乎还有其他东西,例如SROA)。
  • 我有很多内嵌的小功能。我并没有因此而定义函数,但是我确实需要定义很多函数才能通用。
  • 我正在研究一个实际的性能测试用例。这不是一个微基准,所以我有信心正在测量自己真正想要测量的东西。
  • 如果未内联热循环中的函数,则我确实会测量x2的性能损失(肯定是由于它阻止了很多进一步的优化,尤其是矢量化和SROA的事实)
  • 我开始使用intel编译器进行此工作,而intel编译器使用模板代码会非常缓慢且容易出错。保持手写性能非常复杂,因此我改用gcc。

现在我注意到一些奇怪的行为:

  • 在某些情况下,不使用-fPIC会使gcc发出-Winline警告,说它没有内联。我不了解-fPIC与内联之间的关系。
  • 我不知道需要为gcc指定早期内联传递。我本以为--param early-inlining-insns=150应该只用于优化编译时间,而不是gcc生成的代码。但是事实是,如果值是50,我会得到 silent 不好的内联(gcc没有警告),如果值是1000,我也会得到不好的内联( gcc这次警告我)。发生了什么事?
  • 我有点不愿意使用__attribute__((always_inline)),因为对于每个小功能都很难做到这一点,但是在我看来,即使具有此属性,gcc有时也不会内联该功能。 gcc是否真的总是使用此属性来内联函数?

如何强制gcc内联我所有的inline函数?我什至从概念上都无法理解,为什么手工制作看起来如此简单,但编译器却难以内联。内联是否存在可伸缩性优化问题?

0 个答案:

没有答案