如何告诉 gcc 不要内联函数?
Posted
技术标签:
【中文标题】如何告诉 gcc 不要内联函数?【英文标题】:How can I tell gcc not to inline a function? 【发布时间】:2010-12-01 05:39:21 【问题描述】:假设我在源文件中有这个小函数
static void foo()
我构建了我的二进制文件的优化版本,但我不想内联这个函数(出于优化目的)。我可以在源代码中添加一个宏来防止内联吗?
【问题讨论】:
感谢您提出这个问题!当一个函数没有出现时,我正在使用 oprofile 进行分析,这里的答案解决了这个问题。 c++: ***.com/questions/3329214/… 【参考方案1】:使用noinline
attribute:
int func(int arg) __attribute__((noinline))
你应该在声明函数供外部使用和编写函数时都使用它。
【讨论】:
【参考方案2】:您需要 gcc
特定的 noinline
属性。
此功能属性可防止 从被考虑的功能 内联。如果函数没有 有副作用,有 内联以外的优化 导致函数调用被优化 离开,虽然函数调用是 居住。为了防止这样的电话 优化了,放
asm ("");
像这样使用它:
void __attribute__ ((noinline)) foo()
...
【讨论】:
在 Arch Linux 上使用 gcc 4.4.3,我得到一个语法错误,上面放置的属性。当它在函数之前时它可以正常工作(例如,attribute ((noinline)) void foo() ) Arduino 还希望将其放在函数之前。 已编辑以修复属性语法。 asm("") 构造实际上是相当跨平台的并且可以完成工作。我是为 x86 Linux 做的,它没有在 PowerPC AIX 上引起构建问题。感谢这个有用的建议! 要求到处更改代码的方法不能被合理地认为是可接受的答案。【参考方案3】:一种可移植的方式是通过指针调用函数:
void (*foo_ptr)() = foo;
foo_ptr();
虽然这会产生不同的分支指令,但这可能不是您的目标。这提出了一个很好的观点:您的目标是什么?
【讨论】:
如果指针是在文件范围内定义的,而不是静态的,它应该可以工作,因为编译器不能假设它在使用时具有初始值。如果它是本地的(如图所示),它几乎肯定会被视为与 foo() 相同。 (“在这十年里”,他补充道,看着日期) 这在现代编译器上不起作用(例如 gcc 11、clang 12):他们会看穿它并内联函数:godbolt.org/z/hh8dcnE3v 添加volatile
使其再次工作。跨度>
【参考方案4】:
如果您收到__attribute__((noinline))
的编译器错误,您可以尝试:
noinline int func(int arg)
....
【讨论】:
【参考方案5】:static __attribute__ ((noinline)) void foo()
这对我有用。
【讨论】:
【参考方案6】:GCC 有一个开关叫做
-fno-inline-small-functions
所以在调用 gcc 时使用它。但副作用是所有其他小函数也是非内联的。
【讨论】:
在编译器级别不起作用。正在使用 gcc 5.2.1 20150902 (Red Hat 5.2.1-2) 要么当前的 GCC 6.4 被破坏,要么这个和更简单的-fno-inline
根本不起作用。 gdb
仍会进入跨步方法。有东西坏了,我怀疑是gdb
。
它将关闭所有的内联优化,而不仅仅是指定的函数。
@ajeh 不内联函数意味着它们被正常调用,不是吗?【参考方案7】:
我使用 gcc 7.2。我特别需要一个非内联函数,因为它必须在库中实例化。我尝试了__attribute__((noinline))
答案以及asm("")
答案。两者都没有解决问题。
最后,我发现在函数内部定义一个静态变量将强制编译器在静态变量块中为其分配空间,并在函数第一次调用时为其发出初始化。
这是一种肮脏的伎俩,但它确实有效。
【讨论】:
您可以在标头中定义您的函数inline void foo(void) ...
,并在库源文件中声明它extern inline void foo(void);
。遵循 C99 语义,编译器可以在需要时内联函数并在您的库中发出目标代码。见Is "inline" without "static" or "extern" ever useful in C99 ?。【参考方案8】:
我知道这个问题是关于 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
适用于下一个函数:
#pragma noinline
static void foo(void)
TI 6.0+ 支持
FUNC_CANNOT_INLINE
pragma (令人讨厌)在 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 还支持一个 pragma,它采用 函数名,返回at least 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 的链接。我已经要求我们的其他开发人员评估它以供我们使用。我们有多种架构。 我很想知道他们说什么,尤其是如果他们不感兴趣的话。而且,当然,我会回答问题(GitHub 问题跟踪器、电子邮件等等……)。以上是关于如何告诉 gcc 不要内联函数?的主要内容,如果未能解决你的问题,请参考以下文章