C 这种内联结果常见吗?
由于大学的工作,我不得不研究一个简单的优化,内联 以下是基本代码:C 这种内联结果常见吗?,c,optimization,inline,inlining,C,Optimization,Inline,Inlining,由于大学的工作,我不得不研究一个简单的优化,内联 以下是基本代码: #include <stdio.h> #include <sys/time.h> #include <stdlib.h> #define ITER 1000 #define N 3000000 int i, j; float x[N], y[N], z[N]; void add(float x, float y, float *z){ *z = x + y; } void in
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#define ITER 1000
#define N 3000000
int i, j;
float x[N], y[N], z[N];
void add(float x, float y, float *z){
*z = x + y;
}
void initialVersion(){
struct timeval inicio, final;
double time;
gettimeofday(&inicio, 0);
for(j = 0; j < ITER; j++){
for(i = 0; i < N; i++){
add(x[i], y[i], &z[i]);
}
}
gettimeofday(&final, 0);
time = (final.tv_sec - inicio.tv_sec + (final.tv_usec - inicio.tv_usec)/1.e6);
printf("Time: %f\n", time);
}
#包括
#包括
#包括
#定义ITER 1000
#定义300万新西兰元
int i,j;
浮点数x[N],y[N],z[N];
无效添加(浮动x、浮动y、浮动*z){
*z=x+y;
}
void initialVersion(){
结构timeval inicio,最终版本;
双倍时间;
gettimeofday(&inicio,0);
对于(j=0;j
下面是带有内联的代码:
#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#define ITER 1000
#define N 3000000
int i, j;
float x[N], y[N], z[N];
void inliningVersion(){
struct timeval inicio, final;
double time;
gettimeofday(&inicio, 0);
for(j = 0; j < ITER; j++){
for(i = 0; i < N; i++){
z[i] = x[i] + y[i];
}
}
gettimeofday(&final, 0);
time = (final.tv_sec - inicio.tv_sec + (final.tv_usec - inicio.tv_usec)/1.e6);
printf("Time: %f\n", time);
}
#包括
#包括
#包括
#定义ITER 1000
#定义300万新西兰元
int i,j;
浮点数x[N],y[N],z[N];
无效InlineVersion(){
结构timeval inicio,最终版本;
双倍时间;
gettimeofday(&inicio,0);
对于(j=0;j
使用带有gcc的选项-O0进行编译,基本版本的结果为14.27秒,带有内联的版本的结果为4.45秒。这很普遍吗?我执行了10次程序,结果总是相似的。你觉得怎么样
然后,使用选项-O1编译,两个版本的结果相似,大约为1.5秒,因此我假设gcc使用O1为我进行内联
顺便说一句,我知道gettimeofday计算总时间,不仅计算程序本身使用的时间,还要求我专门使用该函数
提前谢谢 让我们分析GCC 7.2(使用
O0
)为两个版本的代码生成的程序集输出
无内联 首先,让我们检查计算机需要完成多少工作才能通过单独的功能完成任务:
void add(float x, float y, float *z){
*z = x + y;
}
int main ()
{
float x[100], y[100], z[100];
for(int i = 0; i < 100; i++){
add(x[i], y[i], &z[i]);
}
}
代码的处理部分大约需要32条指令(介于L4
和L3
之间的指令以及add
函数的指令)
大部分指令用于进行函数调用
理解函数调用如何工作的一种简化方法是:
带内联 现在让我们检查一下,如果函数是内联的,计算机需要做多少工作
int main ()
{
float x[100], y[100], z[100];
for(int i = 0; i < 100; i++){
z[i] = x[i] + y[i];
}
}
处理代码(标签L3
和L2
之间的指令)大约有14条指令。在此程序集输出中,不存在所有负责进行函数调用的指令,这节省了大量CPU周期
通常,当函数的运行时间是函数调用开销的几倍以上时,函数调用的开销是不相关的。在代码中,函数的运行时间非常短,因此函数调用开销非常大
如果使用O1
标志,编译器确实会为您进行内联。您可以通过检查使用O1
生成的程序集来查找,也可以直接检查GCC手册中使用O1
尝试的程序集
您可以使用
-S
标志生成程序集输出,也可以使用在线生成(本篇文章从这里获取了程序集输出)。什么样的问题是“您认为如何?”?因此,这不是一个讨论论坛。你要求CPU做的越多,花费的时间就越多,而且考虑到每次只需执行一个简单的计算,函数调用的成本就越高。编译器优化器会删除或多或少明显的不必要代码,甚至可能会重新排列代码。我可以编辑问题并询问“这是否常见?”。为了避免重复,我问了“你觉得怎么样”,因为在标题中我问的是第一个@melpomeneYes,内联可以节省时间,这就是为什么编译器会为您这样做。同时,当您使用优化进行编译时,内联节省大量时间并不常见,因为编译器无论如何都会为您这样做。正如我上面所说的,有时手工内联是值得的(对于C以外的其他语言更是如此),但因为它降低了可读性,编译器在实际使用中可能会为您做一些事情,所以您只会在热路径中进行内联,并测量以确保实际获得(在某些情况下,您甚至可以使事情变得更慢)无需假设,自动内联是任何C编译器中的标准优化。只要您启用优化器,您就会发现。因此,手工操作是没有必要的。而且可能是有害的,您可能会内联太多,从而降低处理器缓存的效率。也许今天不会,但代码很少变小。但最重要的是,它永远不足以损害代码的可读性。
int main ()
{
float x[100], y[100], z[100];
for(int i = 0; i < 100; i++){
z[i] = x[i] + y[i];
}
}
main:
pushq %rbp
movq %rsp, %rbp
subq $1096, %rsp
movl $0, -4(%rbp)
.L3:
cmpl $99, -4(%rbp)
jg .L2
movl -4(%rbp), %eax
cltq
movss -416(%rbp,%rax,4), %xmm1
movl -4(%rbp), %eax
cltq
movss -816(%rbp,%rax,4), %xmm0
addss %xmm1, %xmm0
movl -4(%rbp), %eax
cltq
movss %xmm0, -1216(%rbp,%rax,4)
addl $1, -4(%rbp)
jmp .L3
.L2:
movl $0, %eax
leave
ret