首页 » 网站推广 » php53技巧_函数调用太多了会有机能问题吗

php53技巧_函数调用太多了会有机能问题吗

访客 2024-11-29 0

扫一扫用手机浏览

文章目录 [+]

我当时给她的回答是,没问题放心吧,函数调用的开销很小的,不必担心。
但回答完她的问题之后,我转头一想,我只知道函数调用的开销很小,但是详细是多大,我心里并吃不准,这就在我心里又种下了草。
后来终于抽空进行了一次实践研究,把草拔掉了。

C措辞测试

1) 准备测试代码

php53技巧_函数调用太多了会有机能问题吗

测试代码很大略,这便是一个for循环的函数调用。

php53技巧_函数调用太多了会有机能问题吗
(图片来自网络侵删)

#include <stdio.h> int func(int p){ return 1;} int main() { int i; for(i=0; i<100000000; i++){ func(2); } return 0; }

2) 函数调用耗时测试

我们用time命令来进行耗时测试

# gcc main.c -o main # time ./main real 0m0.335s user 0m0.334s sys 0m0.000s #perf stat ./main ...... 1,100,989,673 instructions # 1.37 insns per cycle ......

不过上面的实验中有个多余的开销,那便是for循环。
我们单独打算一下这个for的开销,把func()调用那行注释掉,单独保留1亿次的for循环,再重新编译实行一遍。
结果是

time ./main real 0m0.293s user 0m0.292s sys 0m0.000s perf stat ./main ...... 301,252,997 instructions # 0.43 insns per cycle......

通过上面两步测试的数据,(0.335-0.293)/100000000=0.4ns。
我们可以得出结论1:每个c函数调用耗时大约是0.4ns旁边。

3) 函数调用CPU指令数剖析

我们用perf命令可以统计到程序运行的底层CPU指令个数。
1亿次的函数调用统计结果如下:

# perf stat ./main ...... 1,100,989,673 instructions # 1.37 insns per cycle ......

去掉for循环后,单独1亿次的for循环统计如下:

# perf stat ./main ...... 301,252,997 instructions # 0.43 insns per cycle......

通过这两个数据,(1,100,989,673-301,252,997)/100000000=8个。
以是我们得出结论2:每个c函数须要的CPU指令数是8个!

4) 函数调用CPU指令阐发

如果有同学和我一样好奇结论2中的每个c函数的CPU指令到底干了些啥,请和我一起来,否则请开启3倍速快进。
还是上述的实验代码,我们通过gdb的disassemble来查看一下其内部汇编实行过程,编译之。

gcc -g main.c -o main

再用gdb命令调试:

gdb ./mainstartdisassemblemov $0x2,%edi

看到函数到了main函数处,并打印出了main函数的汇编代码

......=> 0x0000000000400486 <+4>: mov $0x2,%edi 0x000000000040048b <+9>: callq 0x400474 <func>......

这是进入函数调用的两个CPU指令,每个指令大概含义如下:

指令1:mov $0x2,%edi是为了调用函数做准备,把参数放到寄存器中。
指令2:callq表示cpu开始实行func函数的代码段。

接下来让我们进入到func函数内部看一下:

break funcrun

这时函数停在了func函数的入口处, 连续利用gdb的disassemble命令查看汇编指令:

(gdb) disassembleDump of assembler code for function func: 0x0000000000400474 <+0>: push %rbp 0x0000000000400475 <+1>: mov %rsp,%rbp 0x0000000000400478 <+4>: mov %edi,-0x4(%rbp)=> 0x000000000040047b <+7>: mov $0x1,%eax 0x0000000000400480 <+12>: leaveq 0x0000000000400481 <+13>: retq End of assembler dump.

这6个指令是对应在函数内部实行,以及函数返回的操作。
加上前面2个,这样在结论2中的每个函数8个CPU指令就都底细毕露了。

指令3:push %rbp bp寄存器的值压入调用栈,即将main函数栈帧的栈底地址入栈(对应一次压栈操作,内存IO)指令4:mov %rsp,%rbp被调函数的栈帧栈底地址放入bp寄存器,建立func函数的栈帧(一次寄存器操作)。
指令5:mov %edi,-0x4(%rbp)是从寄存器的地址-4的内存中取出,即获取输入参数(内存IO)指令6:mov $0x1,%eax对应return 0,即是将返回参数写到寄存器中(内存读IO)

再接下来的两个实行令是进行调用栈的退栈,以便于返回到main函数连续实行。
是指令3和指令4的逆操作。

指令7:leave q等价于mov %rbp, %rsp,寄存器操作指令8:retq 等价于pop %rbp(内存IO)

总结:8次CPU指令中大部分都是寄存器的操作,纵然有“内存IO”,也是在栈上进行。
而栈操作密集,符合局部性事理,早就被L1缓存住了,实在都是L1的IO,以是耗时很低。
前口试验结果表明1次函数调用的开销是0.4ns, 耗时竟然小于1次真正物理内存IO的耗时(40ns旁边),

5) 先容指令并行

不知道大家有没有人把稳到,前面两次perf stat的结果等分别有如下两个提示

0.43 insns per cycle1.37 insns per cycle

这是说当代的CPU可以通过流水线的办法对CPU指令进行并行处理,当指令符合并行规则的时候,每个CPU周期内实行的指令数可能会大于1。
这便是CPU指令并行的功劳。
以是增加函数调用后耗时并没有增加太多,除了函数调用本身开销不大的缘故原由以外,还有一个缘故原由便是函数调用让CPU的流水线并行技能得以施展,每秒处理的CPU指令数更多了。

PHP措辞测试

很多同学又会问题,你用的是C措辞进行测试,性能当然高了。

“我用的可是PHP,这可是脚本措辞”“我用的可是Java,中间可还有一层虚拟机”“我用的可是...”

好了,不抬杠,我们连续试一试不就完了么。
就用php来连续实验一把。

<?php function func(){ return true; } for($i=0;$i<10000000;$i++){ func(); }

实验结果:

php7: 1000W次耗时0.667s,减去0.140s的for循环耗时,均匀每次函数调用耗时52nsphp53:1000W次耗时2.1s,减去0.5s的for循环耗时,均匀每次耗时160ns结论

php的函数调用确实比c的要慢很多,从不到1ns升高到了50ns旁边。
由于php又用c虚拟了一层指令集,这层指令集还须要变成CPU的指令集后才可以真正运行。
但是要知道的是ns这个韶光单位太小了,如果你用的框架特殊变态,一个用户要求来了直接就搞了1000次的函数调用,那么花费在函数调用上的韶光会是50ns1000=50us。
这和代码框架化后给团队项目带来的便利性来比拟的话,这点韶光开销,我以为仍旧是可以忽略的。

标签:

相关文章

网络优化,关键词布局的艺术与方法

网络优化成为企业提升品牌知名度、提高用户体验的关键因素。而关键词优化作为网络优化的重要环节,其合理布局对于网站SEO(搜索引擎优化...

网站推广 2025-03-21 阅读0 评论0

SEO工具在现代营销中的应用与优化步骤

搜索引擎优化(SEO)已经成为企业网络营销的重要组成部分。SEO工具作为辅助企业进行SEO工作的工具,其作用不言而喻。本文将围绕S...

网站推广 2025-03-21 阅读0 评论0