我怎样才能告诉gcc不要内联函数?


126

说我在源文件中有这个小功能

static void foo() {}

并且我构建了二进制文件的优化版本,但我不想内联此函数(出于优化目的)。我可以在源代码中添加宏以防止内联吗?


谢谢这个问题!当函数未显示时,我正在使用oprofile进行配置,此处的答案已解决此问题。
西蒙·欧格斯特

Answers:


149

您需要gcc-specific noinline属性。

此函数属性可防止考虑对函数进行内联。如果该函数没有副作用,则可以进行优化,除了内联之外,还可以使函数调用被优化,尽管函数调用是实时的。为了避免优化此类通话,请放置 asm ("");

像这样使用它:

void __attribute__ ((noinline)) foo() 
{
  ...
}

32
在Arch Linux上使用gcc 4.4.3时,出现上述属性放置语法错误。它在函数之前正确运行(例如,属性((noinline))void foo(){})
mrkj 2010年

2
Arduino还希望将其放在函数之前。
Peter N Lewis

2
编辑以修复属性语法。
Quuxplusone

1
asm(“”)构造实际上是跨平台的,可以完成工作。我是在x86 Linux上完成的,它没有在PowerPC AIX上引起构建问题。感谢您的有用建议!
2014年

1
到处都需要更改代码的方法不能合理地视为可接受的答案。
ajeh

31

GCC有一个名为

-fno-inline-small-functions

因此,在调用gcc时使用它。但是副作用是所有其他小的函数也未内联。


在编译器级别上不起作用。使用gcc 5.2.1 20150902(Red Hat 5.2.1-2)
John Greene

当前的GCC 6.4损坏了,或者-fno-inline根本不起作用。gdb仍然在跨步时输入方法。东西坏了,我怀疑是这样gdb
ajeh

它将关闭所有功能的内联优化,而不仅限于指定功能。
where23 '19

@ajeh不内联函数意味着它们通常被调用,不是吗?
Melebius

21

一种可移植的方法是通过指针调用该函数:

void (*foo_ptr)() = foo;
foo_ptr();

尽管这会产生不同的分支指令,但这可能不是您的目标。这带来了一个好处:您的目标什么?


2
如果指针是在文件范围内定义的,并且不是静态的,则它应该起作用,因为编译器无法在使用时假定其初始值。如果它是本地的(如图所示),几乎可以肯定与foo()一样。(“在这十年中,他补充说,看日期)
greggo

16

我知道问题是关于GCC的,但是我认为了解一些有关编译器和其他编译器的信息可能会很有用。

GCC的 noinline function属性在其他编译器中也很流行。它至少受以下方面的支持:

  • 叮当声(勾选__has_attribute(noinline)
  • 英特尔C / C ++编译器(它们的文档很糟糕,但是我确定它可以在16.0+上运行)
  • Oracle Solaris Studio至少恢复到12.2
  • ARM C / C ++编译器至少回到4.1
  • IBM XL C / C ++至少恢复到10.1
  • TI 8.0+(或带有+ gcc的7.3+,将定义__TI_GNU_ATTRIBUTE_SUPPORT__

此外,MSVC还支持 __declspec(noinline) Visual Studio 7.1。英特尔可能也支持它(他们试图与GCC和MSVC兼容),但是我没有费心去验证这一点。语法基本相同:

__declspec(noinline)
static void foo(void) { }

PGI 10.2+(可能更旧)支持noinline适用于下一个功能的编译指示:

#pragma noinline
static void foo(void) { }

TI 6.0+支持一种 FUNC_CANNOT_INLINE 杂用功能(烦人)在C和C ++中的工作方式不同。在C ++中,它类似于PGI:

#pragma FUNC_CANNOT_INLINE;
static void foo(void) { }

但是,在C中,函数名称是必需的:

#pragma FUNC_CANNOT_INLINE(foo);
static void foo(void) { }

Cray 6.4+(可能更早)采用类似的方法,要求函数名称:

#pragma _CRI inline_never foo
static void foo(void) { }

Oracle Developer Studio还支持使用函数名称的编译指示,至少可以追溯到Forte Developer 6,但是请注意,即使在最新版本中,它也必须位于声明之后

static void foo(void);
#pragma no_inline(foo)

根据您的献身精神,您可以创建一个可在任何地方使用的宏,但是您需要将函数名称以及声明作为参数。

如果OTOH,您对大多数人都可以使用的东西还可以,那么您可以摆脱一些美学上令人愉悦并且不需要重复的事情。这就是我为Hedley采取的方法,其中HEDLEY_NEVER_INLINE的当前版本 如下所示:

#if \
  HEDLEY_GNUC_HAS_ATTRIBUTE(noinline,4,0,0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
  HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
  HEDLEY_TI_VERSION_CHECK(8,0,0) || \
  (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
#  define HEDLEY_NEVER_INLINE __attribute__((__noinline__))
#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0)
#  define HEDLEY_NEVER_INLINE __declspec(noinline)
#elif HEDLEY_PGI_VERSION_CHECK(10,2,0)
#  define HEDLEY_NEVER_INLINE _Pragma("noinline")
#elif HEDLEY_TI_VERSION_CHECK(6,0,0)
#  define HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;")
#else
#  define HEDLEY_NEVER_INLINE HEDLEY_INLINE
#endif

如果您不想使用Hedley(这是一个公共域/ CC0标头),则可以毫不费力地转换版本检查宏,但超出我的意愿。


感谢您到项目@nemequ的链接。我已经请其他开发人员对其进行评估以供我们使用。我们有多种架构。
荒木大辅(Daisuke Aramaki)

我很想知道他们在说什么,特别是如果他们不感兴趣。而且,当然,我可以回答问题(GitHub问题跟踪器,电子邮件等等)。
nemequ

14

如果您遇到的编译器错误__attribute__((noinline)),可以尝试:

noinline int func(int arg)
{
    ....
}

10
static __attribute__ ((noinline))  void foo()
{

}

这对我有用。


8

使用noinline 属性

int func(int arg) __attribute__((noinline))
{
}

在声明供外部使用的函数和编写函数时,都应该使用它。


2

我使用gcc 7.2。我特别需要一个非内联函数,因为它必须在库中实例化。我尝试了__attribute__((noinline))答案以及asm("")答案。没有人解决问题。

最终,我发现在函数内部定义静态变量将迫使编译器在静态变量块中为其分配空间,并在首次调用该函数时对其进行初始化。

这是一个肮脏的把戏,但它可以工作。


您可以inline void foo(void) { ... }在标头中定义函数,然后extern inline void foo(void);在库源文件中声明它。按照C99语义,允许编译器在需要时内联函数并在库中发出目标代码。请参阅在C99中没有“静态”或“外部”的“内联”是否有用?
diapir '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.