gcc__attribute__((always_inline))与__attribute__((noinline))解析

原创 Linux二进制 2024-11-07 08:21

引言

在 C 语言中,inline 关键字是一种编译器提示,用于建议编译器将函数调用替换为函数体本身。这种优化技术可以减少函数调用的开销,特别是对于那些频繁调用但实现简单的函数来说更为有效。除了标准的 inline 关键字外,一些编译器还提供了额外的控制机制,如always_inline 和 noinline,以提供更精细的控制。

我们接下来主要讲一下跟内联函数相关的两个属性:always_inline 和 noinline 。这两个属性的用途是告诉编译器:编译时,对我们指定的函数内联展开或不展开。

内联关键字概述

1. inline

定义:当一个函数声明或定义前加上 inline 关键字时,它指示编译器尝试将该函数的调用展开而不是进行常规的函数调用。这可以减少程序运行时的性能损失。

标准:C99 及以后的标准。

用途:建议编译器将函数内联化。

使用场景:适用于短小且经常被调用的函数。

语法:

inline int func(int x, int y) {
return x + y;
}

2. always_inline

定义:这是一个非标准的扩展,通常由特定的编译器(如 GCC)支持。它强制编译器将指定的函数内联,即使这样做可能会导致代码膨胀。

标准:编译器特定扩展(GCC 等)。用途:强制编译器将函数内联化,即使会导致代码膨胀。

使用场景:当开发者确定某个函数必须被内联以达到最佳性能时使用。

语法(GCC):

inline __attribute__((always_inline)) int func(int x, int y) {
return x * y;
}

3. noinline

定义:同样是一个编译器特定的扩展,它告诉编译器不要将标记的函数内联,即使它们被声明为inline。

标准:编译器特定扩展(GCC等)。用途:禁止编译器将函数内联化,即使函数被声明为 inline 。

使用场景:当需要确保某些函数不会被内联,例如为了调试目的或者避免代码体积过大时。

语法(GCC):

__attribute__((noinline)) int func(int x, int y) {
return x - y;
}
 

拓展:关键字归属说明

  • inline:这是 C 语言标准的一部分,从 C99 开始引入。inline 关键字用于建议编译器将函数调用内联化,以减少函数调用的开销。

  • always_inline 和 noinline:这两个关键字是编译器特定的扩展,主要由 GCCGNU Compiler Collection)和其他一些编译器支持。它们提供了更细粒度的控制,超越了标准 C 语言的 inline 关键字。

内联函数使用 inline 关键字声明即可,有时还会结合 static 和 extern 修饰符一起使用。使用 inline 声明一个内联函数,类似于使用 register 关键字声明一个变量。这两种关键字都是向编译器提出建议,而不是强制命令。使用 register 修饰变量时,编译器被建议将该变量存储在寄存器中,以提高程序的运行效率。然而,编译器是否会遵循这一建议,取决于寄存器资源的可用性和变量的使用频率。

 

思考:内联函数为什么常使用 static 修饰?

 

在 Linux 内核中,大量的内联函数定义在头文件中,并且常常使用 static 修饰。关于这一点,网上有很多讨论,但核心原因可以从 C 语言和 C++ 的角度来理解。Linux 内核作者 Linus Torvalds 也对此有过解释:

 

static inline” 意味着“如果我们需要这个函数,但不内联它,那么就在这个编译单元中生成一个静态版本。”而 “extern inline” 则意味着“我实际上有一个外部定义的函数,但如果需要内联它,这里提供了一个内联版本。”

 

我的理解如下

  1. 为什么内联函数要定义在头文件中?内联函数通常定义在头文件中,因为它们可以像宏一样使用。任何需要使用这些内联函数的源文件,只需包含相应的头文件,即可直接使用这些函数,而不需要重复定义。这样可以简化代码管理和维护。

  2. 为什么内联函数要用 static 修饰?尽管我们使用 inline 关键字定义了内联函数,但编译器并不一定会将其内联展开。如果多个源文件都包含了同一个内联函数的定义,编译时可能会出现重定义错误。通过使用 static 修饰,可以将函数的作用域限制在各自的编译单元内,从而避免重定义错误。

同样,当一个函数使用 inline 关键字修饰时,编译器在编译时并不一定会将其内联展开。编译器会根据多种因素来决定是否内联展开,这些因素包括函数体的大小、函数体内是否存在循环结构、是否有指针操作、是否有递归调用以及函数的调用频率等。GCC 编译器通常不会在默认情况下对内联函数进行展开,默认的 GCC 编译的优化选项是 -O0 的,这样是不会内联的,有些版本甚至无法编译通过,只有当编译优化级别设置为 -O2 或更高时,编译器才会考虑是否进行内联展开。

当我们使用 noinline 和 always_inline 属性对一个内联函数进行声明后,编译器的行为就变得确定了。使用 noinline 声明,明确告知编译器不要内联展开该函数;而使用 always_inline 属性声明,则明确告知编译器必须内联展开该函数。

通过这种方式,开发者可以更精细地控制函数的内联行为,从而优化程序的性能。

 

拓展:inline、always_inline、noinline的区别

  • inline:仅仅是建议编译器内联,但不一定内联。

  • always_inline :强制内联。

  • noinline:强制不内联。

代码演示

我们可以编写一个 C 语言程序,然后使用反汇编工具(如 objdump)来查看不同内联策略下的汇编代码。这将帮助我们直观地理解 inlinealways_inline 和 noinline 的实际效果。首先,编写一个 C 语言程序,包含三种不同内联策略的函数。

头文件 inline_functions.h :

#ifndef INLINE_FUNCTIONS_H
#define INLINE_FUNCTIONS_H

// 使用inline
inline int addInline(int x, int y) {
return x + y;
}

// 使用always_inline
__attribute__((always_inline)) inline int addAlwaysInline(int x, int y) {
return x * y;
}

// 使用noinline
__attribute__((noinline)) int addNoInline(int x, int y) {
return x - y;
}

#endif // INLINE_FUNCTIONS_H

源文件 inline_test.c

#include 
#include
#include "inline_functions.h"

int main() {
int result;

// 调用addInline
result = addInline(10, 20);
printf("addInline(10, 20) = %d\n", result);

// 调用addAlwaysInline
result = addAlwaysInline(10, 20);
printf("addAlwaysInline(10, 20) = %d\n", result);

// 调用addNoInline
result = addNoInline(10, 20);
printf("addNoInline(10, 20) = %d\n", result);

return 0;
}

让我们使用 -O2 优化级别编译程序,以确保编译器能够应用内联优化。

gcc -O2 -g -o inline_test inline_test.c

使用 objdump 工具反汇编生成的目标文件。

objdump -d -S inline_test > inline_test.asm

打开生成的 inline_test.asm 文件,找到 main 函数的反汇编代码,观察不同内联策略的效果。

main 函数的反汇编代码如下:

int main() {
4004b0: 48 83 ec 08 sub $0x8,%rsp
int result;

// 调用addInline
result = addInline(10, 20);
printf("addInline(10, 20) = %d\n", result);
4004b4: be 1e 00 00 00 mov $0x1e,%esi
4004b9: bf 98 06 40 00 mov $0x400698,%edi
4004be: 31 c0 xor %eax,%eax
4004c0: e8 db ff ff ff callq 4004a0

// 调用addAlwaysInline
result = addAlwaysInline(10, 20);
printf("addAlwaysInline(10, 20) = %d\n", result);
4004c5: be c8 00 00 00 mov $0xc8,%esi
4004ca: bf b0 06 40 00 mov $0x4006b0,%edi
4004cf: 31 c0 xor %eax,%eax
4004d1: e8 ca ff ff ff callq 4004a0

// 调用addNoInline
result = addNoInline(10, 20);
4004d6: be 14 00 00 00 mov $0x14,%esi
4004db: bf 0a 00 00 00 mov $0xa,%edi
4004e0: e8 0b 01 00 00 callq 4005f0
printf("addNoInline(10, 20) = %d\n", result);
4004e5: bf ce 06 40 00 mov $0x4006ce,%edi
4004ea: 89 c6 mov %eax,%esi
4004ec: 31 c0 xor %eax,%eax
4004ee: e8 ad ff ff ff callq 4004a0

return 0;
}
4004f3: 31 c0 xor %eax,%eax
4004f5: 48 83 c4 08 add $0x8,%rsp
4004f9: c3 retq

汇编代码解析如下:

  1. main 函数的入口
4004b0:       48 83 ec 08             sub    $0x8,%rsp
  • sub $0x8,%rsp:调整栈指针,为局部变量分配空间。
  1. 调用 addInline
4004b4:       be 1e 00 00 00          mov    $0x1e,%esi
4004b9: bf 98 06 40 00 mov $0x400698,%edi
4004be: 31 c0 xor %eax,%eax
4004c0: e8 db ff ff ff callq 4004a0
  • mov $0x1e,%esi:将 30(即 10 + 20 的结果)加载到 %esi 寄存器,作为 printf 的第一个参数。

  • mov $0x400698,%edi:将格式字符串的地址 0x400698 加载到 %edi 寄存器,作为 printf 的第二个参数。

  • xor %eax,%eax:清零 %eax 寄存器,表示没有浮点数参数。

  • callq 4004a0 printf@plt:调用 printf 函数。

  1. 调用 addAlwaysInline
4004c5:       be c8 00 00 00          mov    $0xc8,%esi
4004ca: bf b0 06 40 00 mov $0x4006b0,%edi
4004cf: 31 c0 xor %eax,%eax
4004d1: e8 ca ff ff ff callq 4004a0
  • mov $0xc8,%esi:将 200(即 10 * 20 的结果)加载到 %esi 寄存器,作为 printf 的第一个参数。

  • mov $0x4006b0,%edi:将格式字符串的地址 0x4006b0 加载到 %edi 寄存器,作为 printf 的第二个参数。

  • xor %eax,%eax:清零 %eax 寄存器,表示没有浮点数参数。

  • callq 4004a0 printf@plt:调用 printf 函数。

  1. 调用 addNoInline
4004d6:       be 14 00 00 00          mov    $0x14,%esi
4004db: bf 0a 00 00 00 mov $0xa,%edi
4004e0: e8 0b 01 00 00 callq 4005f0
  • mov $0x14,%esi:将 10 加载到 %esi 寄存器,作为 addNoInline 的第一个参数。

  • mov $0xa,%edi:将 20 加载到 %edi 寄存器,作为 addNoInline 的第二个参数。

  • callq 4005f0 :调用 addNoInline 函数。

4004e5:       bf ce 06 40 00          mov    $0x4006ce,%edi
4004ea: 89 c6 mov %eax,%esi
4004ec: 31 c0 xor %eax,%eax
4004ee: e8 ad ff ff ff callq 4004a0
  • mov $0x4006ce,%edi:将格式字符串的地址 0x4006ce 加载到 %edi 寄存器,作为 printf 的第二个参数。

  • mov %eax,%esi:将 addNoInline 的返回值(存储在 %eax 寄存器中)加载到 %esi 寄存器,作为 printf 的第一个参数。

  • xor %eax,%eax:清零 %eax 寄存器,表示没有浮点数参数。

  • callq 4004a0 printf@plt:调用 printf 函数。

  1. main 函数的退出
4004f3:       31 c0                   xor    %eax,%eax
4004f5: 48 83 c4 08 add $0x8,%rsp
4004f9: c3 retq
  • xor %eax,%eax:清零 %eax 寄存器,表示 main 函数的返回值为 0

  • add $0x8,%rsp:恢复栈指针。

  • retq:返回到调用者。

上述解析总结如下:

  • addInline:编译器将 addInline 内联展开了,因此在 main 函数中直接计算了 10 + 20 的结果,并将结果 30 直接传递给 printf

  • addAlwaysInline:编译器将 addAlwaysInline 内联展开了,因此在 main 函数中直接计算了 10 * 20 的结果,并将结果 200 直接传递给 printf

  • addNoInline:编译器没有将 addNoInline 内联展开,因此在 main 函数中通过调用

    addNoInline 函数来计算 10 - 20 的结果,然后将结果传递给 printf

通过反汇编代码,我们可以清楚地看到不同内联策略对函数调用的影响。

总结

使用 inlinealways_inline 和 noinline 关键字可以帮助程序员更精确地控制函数内联行为,从而影响程序的性能和可维护性。虽然 inline 是 C 标准的一部分,但always_inline 和 noinline 则是编译器提供的扩展功能,使用时应确保目标编译器支持这些特性。合理利用这些工具可以优化关键路径上的代码执行效率,但也需要注意过度使用内联可能导致代码膨胀的问题。在实际开发中,应该根据具体需求和测试结果来决定是否使用以及如何使用这些内联选项。


Linux二进制 Linux编程、内核模块、网络原创文章分享,欢迎关注"Linux二进制"微信公众号
评论 (0)
  •   智慧华盛恒辉国有单位科研项目审计管理系统介绍   1、建设国有单位科研项目审计管理系统的重大意义   其深远意义体现在科研项目管理的核心环节,不仅关乎管理效能与成果质量的飞跃,还深刻影响着科研资金的优化配置、科研行为的规范性以及国家科技发展战略的顺利推进。   应用案例   目前,已有多个科研项目审计管理系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润科研项目审计管理系统。这些成功案例为科研项目审计管理系统的推广和应用提供了有力支持。   (1)强化科研项目管理的效
    华盛恒辉l58ll334744 2025-04-20 22:54 38浏览
  •   电磁环境模拟软件系统深度解读   北京华盛恒辉电磁环境模拟软件系统是专业的技术工具,可生成、捕捉与分析电磁信号,为电气和电子设备搭建仿真测试环境。以下从功能、技术特性、应用场景、主流软件及发展趋势展开介绍。   应用案例   目前,已有多个电磁环境模拟软件系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润电磁环境模拟软件系统。这些成功案例为电磁环境模拟软件系统的推广和应用提供了有力支持。   一、核心功能   电磁环境模拟   信号生成与处理   场景构建与仿真
    华盛恒辉l58ll334744 2025-04-21 10:21 10浏览
  • 在智能家居与物联网(IoT)技术快速发展的背景下,语音播报功能已成为烟雾报警器等安防设备提升用户体验的核心技术之一。厂家凭借其WTV、WTN、WT588F及WT2605C系列语音芯片,推出了三大烟雾报警器语音方案,覆盖传统、高集成度与智能化需求,为不同场景提供灵活选择。以下从技术特性、应用场景及行业价值三方面展开分析。一、方案对比与技术特性 方案类型核心芯片型号技术优势局限性适用场景传统分立方案WTN6/WT588F/WTV系列音质纯净,模块化设计便于维护;兼容性强,支持外接功放优化音
    广州唯创电子 2025-04-21 08:53 60浏览
  •   智慧华盛恒辉国有单位招标标书查重系统介绍   1、建设国有单位招标标书查重系统的重大意义   (1)保障招标过程的公正性与透明度   在国有单位复杂的招标环境中,标书查重系统犹如一把利剑,精准切割出公平竞争的道路。该系统利用自动化比对与检测技术,快速揭露投标文件中潜藏的相似或重复内容,有效遏制了围标、串标及抄袭等恶劣行为,为招标过程披上了一层公正与透明的外衣。这不仅减少了人为干预的空间,更保障了合法投标人的权益,维护了市场的健康秩序,让每一次招标都成为真正的实力较量。   应用案例
    华盛恒辉l58ll334744 2025-04-20 23:07 52浏览
  •   国有单位科研项目审计管理系统解析   一、系统建设意义   北京华盛恒辉国有单位科研项目审计管理系统对科研项目管理至关重要,其意义贯穿管理效能提升、资金优化配置、科研合规推进等核心环节,深刻影响国家科技战略实施。   应用案例   目前,已有多个国有单位科研项目审计管理系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润国有单位科研项目审计管理系统。这些成功案例为国有单位科研项目审计管理系统的推广和应用提供了有力支持。   提升科研项目管理质效:作为数字化、智能化管理工
    华盛恒辉l58ll334744 2025-04-20 23:21 50浏览
  •   战略仿真推演系统设计方案   一、系统概述   1.1 系统定位   北京华盛恒辉战略仿真推演系统是面向政府、企业及军事机构的决策支持工具。它通过搭建虚拟环境,模拟真实战略场景,助力用户评估不同策略的潜在影响,优化决策流程,提升战略规划的科学性与前瞻性。   应用案例   目前,已有多个战略仿真推演系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润战略仿真推演系统。这些成功案例为战略仿真推演系统的推广和应用提供了有力支持。   二、系统架构设计   2.1 总体架
    华盛恒辉l58ll334744 2025-04-20 16:27 45浏览
  •   电磁环境模拟平台系统全解析   北京华盛恒辉电磁环境模拟平台系统是通过技术手段生成、调控和再现复杂电磁环境的专用设备,广泛应用于通信、电子、航空航天、国防等领域。其核心作用是为设备研发、测试和评估提供可控的电磁环境,验证系统在复杂电磁干扰下的性能与可靠性。   应用案例   目前,已有多个电磁环境模拟平台系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润电磁环境模拟平台系统。这些成功案例为电磁环境模拟平台系统的推广和应用提供了有力支持。   一、系统构成   信号发生
    华盛恒辉l58ll334744 2025-04-21 09:40 43浏览
  •   北京华盛恒辉作战仿真系统软件是现代军事领域不可或缺的重要工具,用于模拟作战环境、训练人员和评估作战方案。它借助计算机技术搭建虚拟战场,支撑复杂作战场景推演分析,为军事决策提供科学依据。以下从核心功能、技术架构、应用场景和发展趋势四个方面展开解析:   应用案例   目前,已有多个作战仿真系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润作战仿真系统。这些成功案例为作战仿真系统的推广和应用提供了有力支持。   核心功能   战场环境建模:构建逼真的虚拟战场环境,涵盖地形、
    华盛恒辉l58ll334744 2025-04-20 11:06 26浏览
  •   北京华盛恒辉作战仿真系统软件平台是现代军事领域中用于模拟作战环境、评估作战方案、训练军事人员的重要工具。这些平台通过计算机技术构建虚拟战场,支持多兵种、多武器系统的协同作战仿真,为军事决策、战术训练和装备研发提供科学依据。以下从平台类型、核心技术、应用场景及发展趋势等方面进行详细介绍。   应用案例   目前,已有多个作战仿真系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润作战仿真系统。这些成功案例为作战仿真系统的推广和应用提供了有力支持。   一、作战仿真系统软件平台
    华盛恒辉l58ll334744 2025-04-20 15:37 35浏览
  • 曾几何时,电视作为家庭娱乐的核心,是每个家庭客厅里不可或缺的存在。一家人围坐在电视机前,共享欢乐时光,这样的场景承载了无数人的回忆。然而,时过境迁,如今的彩电市场早已今非昔比。据奥维云网(AVC)推总数据显示,2024年中国彩电市场零售量为3086万台,同比微降1.8%。虽然零售额同比大幅增长15.7%,达到1271亿元,但这主要得益于产品结构升级、大屏化以及高端化趋势,并不意味着行业的全面复苏。从2024年618期间的数据来看,中国彩电线上市场销量规模为247.9万台,同比下降18.1%,销额
    用户1742991715177 2025-04-19 22:14 23浏览
  •   数字化战场:军事态势视景仿真推演系统软件解析   北京华盛恒辉军事态势视景仿真推演系统软件是现代军事领域模拟战场态势、辅助指挥决策的关键工具。伴随信息技术发展,其在提升军事训练质量、优化作战指挥决策等方面的作用愈发显著。   一、系统概述   该系统借助数字化技术,构建高精度三维战场环境,模拟各类作战场景与武器装备运行状态,为军事指挥员及作战人员打造沉浸式战场体验。系统融合地理信息系统(GIS)、计算机图形学、人工智能等多学科技术,实现战场态势实时感知、动态推演与可视化呈现。   应
    华盛恒辉l58ll334744 2025-04-20 11:24 31浏览
  •   战略仿真推演平台是一种基于计算机技术和仿真模型构建的决策支持系统,旨在通过模拟复杂战略环境,帮助决策者评估不同战略方案的效果、预测潜在风险并优化决策过程。此类平台广泛应用于军事、经济、能源、城市规划等领域,为高层决策提供科学依据。   应用案例   目前,已有多个战略仿真推演平台在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润战略仿真推演平台。这些成功案例为战略仿真推演平台的推广和应用提供了有力支持。   一、核心功能   多维度战略建模   动态推演与情景分析   
    华盛恒辉l58ll334744 2025-04-20 16:16 49浏览
  • 一、市场背景与竞争优势随着智能家居市场的爆发式增长,消费者对小家电的智能化、交互性需求显著提升。WTVxxx系列语音芯片凭借高性价比、卓越音质与功能集成度,已成为智能小家电领域的核心驱动方案。该系列芯片通过以下优势重塑行业格局:成本优化:集成MCU、语音播报、驱动控制等多功能模块,显著降低硬件成本与开发复杂度;智能化升级:支持语音交互、状态显示与智能控制,契合现代用户对高端体验的追求;快速迭代:兼容主流芯片架构,支持远程更新与硬件扩展,助力产品持续迭代。目前,WTVxxx芯片已广泛应用于扫地机器
    广州唯创电子 2025-04-21 08:32 60浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦