假设我在源文件中有这个小功能
static void foo() {}
我构建了我的二进制文件的优化版本,但我不想内联这个函数(出于优化目的)。我可以在源代码中添加一个宏来防止内联吗?
您需要gcc
-specificnoinline
属性。
此函数属性可防止将函数考虑进行内联。如果函数没有副作用,那么除了内联之外还有其他优化会导致函数调用被优化掉,尽管函数调用是实时的。为了防止这样的调用被优化掉,把
asm ("");
像这样使用它:
void __attribute__ ((noinline)) foo()
{
...
}
GCC 有一个开关叫做
-fno-inline-small-functions
所以在调用 gcc 时使用它。但副作用是所有其他小函数也是非内联的。
我知道这个问题是关于 GCC 的,但我认为了解有关其他编译器的编译器的一些信息可能会很有用。
GCC 的
noinline
function 属性在其他编译器中也很受欢迎。它至少得到以下支持:
__has_attribute(noinline)
)__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 标头),您可以毫不费力地转换版本检查宏,但比我愿意投入的更多 ☺。
一种可移植的方法是通过指针调用函数:
void (*foo_ptr)() = foo;
foo_ptr();
尽管这会产生不同的分支指令,但这可能不是您的目标。这提出了一个很好的观点:你的目标是什么?
如果您遇到编译器错误__attribute__((noinline))
,您可以尝试:
noinline int func(int arg)
{
....
}
static __attribute__ ((noinline)) void foo()
{
}
这对我有用。
我使用 gcc 7.2。我特别需要一个非内联函数,因为它必须在库中实例化。我尝试了__attribute__((noinline))
答案,以及asm("")
答案。没有一个人解决了这个问题。
最后,我认为在函数内部定义一个静态变量将强制编译器在静态变量块中为其分配空间,并在第一次调用函数时为其发出初始化。
这是一种肮脏的把戏,但它确实有效。