C/C++ 函数调用以及Linux中系统调用 开销介绍:介绍C/C函数调用以及Linux中系统调用的开销情况

简介: C/C++ 函数调用以及Linux中系统调用 开销介绍:介绍C/C函数调用以及Linux中系统调用的开销情况

函数调用开销的原因

1.参数入栈,将函数参数入栈,这是现在函数调用的标准方式。所以参数越多,开销越大

2.将控制权转移至函数中.

3.建立新的栈帧,也就是当前函数使用的“一片”栈空间,使用ebp的值来标识新的栈帧,因此要将原栈帧首地址保存下来,方便回到原来的即调用者的栈帧。

4.恢复原栈帧,然后将控制权转移至调用者.


不同函数的开销

  • 普通函数

C/C++ 普通函数的开销包括函数调用、参数传递、局部变量的创建和销毁等。
其中函数调用是最主要的开销,因为每次函数调用都需要保存当前函数的返回地址和上下文信息,然后跳转到被调用函数的代码段执行,最后再返回到调用函数的代码段继续执行。
这个过程会涉及到栈的操作,所以栈的使用也是C/C++普通函数开销的一部分。

  • 模板函数

模板函数需要在编译时进行实例化,会增加内存消耗,调用时开销与普通函数一致

  • 内联 、宏函数

宏函数和内联函数在某些方面确实有相似之处,它们都可以避免函数调用的开销,直接将函数的代码插入到调用处。但是,它们之间还是存在一些区别。
首先,宏函数是在预处理阶段展开的,展开结果是一个代码块,没有函数的调用栈,没有参数的传递,没有返回值的处理,因此宏函数在展开时可能会出现代码膨胀、重复计算等问题,进而影响其效率。
其次,宏函数在展开时是直接替换代码的,没有类型检查和作用域限制,可能会导致一些潜在的问题。例如,宏函数中的变量可能会与调用它的代码块中的变量重名,从而引发错误。
相比之下,内联函数在编译时展开,展开结果是一个函数体,可以进行类型检查、作用域限制等,避免了宏函数可能存在的问题。此外,内联函数还可以使用函数参数和返回值,避免了宏函数可能存在的重复计算等问题。
因此,虽然宏函数和内联函数在某些方面有相似之处,但是在实现方式和效率上还是存在一定的差异。在具体的应用场景中,需要根据实际情况进行选择和权衡。

  • 虚函数

C++虚函数运行时调用的额外开销产生于虚函数表的查找和函数指针的间接调用。

  • 内建函数

内建函数(built-in function)是编译器内置的函数,它们通常在编译时被处理,而不是在运行时调用,因此,使用内建函数通常不会引入函数调用的开销。
不过,内建函数的优化和性能表现与编译器和编译器的优化设置有关。在某些情况下,编译器可能会选择将内建函数作为普通函数来处理,这样就会引入函数调用的开销。此外,某些内建函数可能会导致代码大小增加,从而降低缓存性能。


内建函数的优化程度可以很高,是因为它是由编译器特殊处理的函数,通常采用特殊的汇编指令或者是库函数来实现,可以在一定程度上消除函数调用的开销。
在某些情况下,内建函数的优化效果可能高于内联函数,例如编译器可以使用特殊的指令或者硬件加速来实现内建函数,从而达到很高的优化效果。

因此不同函数的开销对比:内联 、宏函数 、内建函数< 普通函数、模板函数 < 虚函数


系统调用开销

当一个程序需要访问操作系统提供的服务时,它需要通过系统调用来请求操作系统执行相应的操作。

  • Linux 系统调用的流程如下:
  1. 应用程序通过系统调用库函数(如 glibc)中的接口向内核发起系统调用请求。
  2. 系统调用库函数将系统调用号和参数传递给内核。
  3. 内核根据系统调用号找到相应的系统调用处理函数,并将参数从用户空间拷贝到内核空间。
  4. 内核执行系统调用处理函数,完成相应的操作,并将结果返回给用户空间。
  5. 系统调用库函数将结果从内核空间拷贝到用户空间,并返回给应用程序。
  • Linux 系统调用的开销通常比普通函数的开销要高几倍,因为系统调用需要从用户态切换到内核态,这个过程需要进行一些额外的操作,比如保存和恢复 CPU 寄存器、切换内存地址空间等。
  • 系统调用虽然使用了“快速系统调用”指令,但耗时仍大约在200ns+,多的可能到十几us
  • 每个系统调用内核要进行许多工作,大约需要执行1000条左右的CPU指令

Shell 之 time 命令(检测命令执行时间)

“time a simple command or give resource usage”,
即测量命令的执行时间,或者给出系统资源的使用情况。

#如果你想查看一条命令(比如 ls)到底执行了多长时间,我们可以这样做:
[roc@linux ~]$ time ls
program  public_html  repo  rocscm
real    0m0.002s
user    0m0.002s
sys     0m0.000s

(1) real:从进程 ls 开始执行到完成所耗费的 CPU 总时间。该时间包括 ls 进程执行时实际使用的 CPU 时间,ls 进程耗费在阻塞上的时间(如等待完成 I/O 操作)和其他进程所耗费的时间(Linux 是多进程系统,ls 在执行过程中,可能会有别的进程抢占 CPU)。

(2) user:进程 ls 执行用户态代码所耗费的 CPU 时间。该时间仅指 ls 进程执行时实际使用的 CPU 时间,而不包括其他进程所使用的时间和本进程阻塞的时间。

(3) sys:进程 ls 在内核态运行所耗费的 CPU 时间,即执行内核系统调用所耗费的 CPU 时间。

说明:单核处理器:real_time≥ (user_time+sys_time) ,多核则不一定。

目录
相关文章
|
1月前
|
算法 Linux C++
【Linux系统编程】解析获取和设置文件信息与权限的Linux系统调用
【Linux系统编程】解析获取和设置文件信息与权限的Linux系统调用
29 0
|
17天前
|
存储 算法 Linux
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
【实战项目】网络编程:在Linux环境下基于opencv和socket的人脸识别系统--C++实现
40 6
|
1月前
|
Linux 编译器 程序员
【Linux 调试秘籍】深入探索 C++:运行时获取堆栈信息和源代码行数的终极指南
【Linux 调试秘籍】深入探索 C++:运行时获取堆栈信息和源代码行数的终极指南
69 0
|
1月前
|
消息中间件 Linux 调度
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
【Linux 进程/线程状态 】深入理解Linux C++中的进程/线程状态:阻塞,休眠,僵死
70 0
|
1月前
|
监控 Linux 编译器
Linux C++ 定时器任务接口深度解析: 从理论到实践
Linux C++ 定时器任务接口深度解析: 从理论到实践
70 2
|
1月前
|
存储 Linux 程序员
【Linux C/C++ 堆内存分布】深入理解Linux进程的堆空间管理
【Linux C/C++ 堆内存分布】深入理解Linux进程的堆空间管理
79 0
|
1月前
|
存储 算法 Linux
深入理解Linux内存管理brk 和 sbrk 与以及使用C++ list实现内存分配器
深入理解Linux内存管理brk 和 sbrk 与以及使用C++ list实现内存分配器
37 0
|
1月前
|
存储 监控 Linux
Linux 使用getrusage系统调用获取cpu信息:一个C++实例分析
Linux 使用getrusage系统调用获取cpu信息:一个C++实例分析
50 0
|
1月前
|
Unix Linux C++
【C/C++ 造轮子】Linux异步计时器:深入探讨和应用 (Linux Asynchronous Timers: An In-depth Exploration and Application)
【C/C++ 造轮子】Linux异步计时器:深入探讨和应用 (Linux Asynchronous Timers: An In-depth Exploration and Application)
60 1
|
1月前
|
设计模式 存储 算法
【C++ 函数调用操作符】探究C++中的函数调用操作符 基础到高级应用
【C++ 函数调用操作符】探究C++中的函数调用操作符 基础到高级应用
277 0