函数调用:即调用函数调用被调用函数,调用函数压栈,被调用函数执行,调用函数出栈,调用函数继续执行的一个看似简单的过程,系统底层却做了大量操作。
操作:
1, 调用函数帧指针(函数参数,局部变量,栈帧状态值,函数返回地址)入栈,栈指针自减
2, 保存调用函数的状态数据入寄存器
3, 被调用函数帧指针入栈,执行当前的被调用函数
4, 被调用函数执行结束,退栈,返回到调用函数的帧指针,从寄存器中恢复当时状态数据
5, 继续执行调用函数,直至结束
即整个调用操作有一个压栈出栈,保存和恢复状态数据的过程。而系统栈内存是有默认的固有大小。有多少次函数调用就会分配多少栈帧。故,函数调用性能影响有如下因素:
1,函数递归层数;
2,参数个数(参数签名所占内存大小)
2.1同类型不同参数个数;
2.2同参数个数不同参数类型;
2.3同参数类型同参数个数,但参数类型所占内存大小不同;
3,函数栈大小,即函数局部变量所占栈大小。
为了测试C语言函数调用性能(时间消耗)因素,编写了一个简单程序运行在如下环境中:
Windows7操作系统上的Vmware虚拟机中的ubuntu系统
在函数调用的开始与结束处,用time.h中的clock()函数返回CPU时钟计时单位数(下表中的starttime和endtime),用durationtime=endtime-starttime表示函数调用的时间消耗。如下:
clock_t starttime=clock();
函数调用…
clock_t endtime=clock();
//除以CLOCKS_PER_SEC,得到以秒为单位的时间结果
double durationtime=(double)(endtime-starttime)/CLOCKS_PER_SEC;
一.函数递归层数
递归层数 |
参数个数 |
函数栈大小 |
starttime |
endtime |
durationtime(秒) |
10 |
5 |
1024 |
951 |
985 |
0.000034 |
10 |
5 |
1024 |
925 |
961 |
0.000036 |
10 |
5 |
1024 |
957 |
992 |
0.000035 |
20 |
5 |
1024 |
950 |
1004 |
0.000054 |
20 |
5 |
1024 |
897 |
952 |
0.000055 |
20 |
5 |
1024 |
996 |
1045 |
0.000049 |
30 |
5 |
1024 |
1075 |
1145 |
0.00007 |
30 |
5 |
1024 |
1031 |
1096 |
0.000065 |
30 |
5 |
1024 |
976 |
1040 |
0.000064 |
50 |
5 |
1024 |
1068 |
1167 |
0.000099 |
50 |
5 |
1024 |
1097 |
1196 |
0.000099 |
50 |
5 |
1024 |
1232 |
1331 |
0.000099 |
100 |
5 |
1024 |
940 |
1463 |
0.000523 |
100 |
5 |
1024 |
939 |
1113 |
0.000174 |
100 |
5 |
1024 |
938 |
1118 |
0.00018 |
100 |
5 |
1024 |
946 |
1144 |
0.000198 |
100 |
5 |
1024 |
968 |
1148 |
0.00018 |
500 |
5 |
1024 |
1033 |
2047 |
0.001014 |
500 |
5 |
1024 |
882 |
1983 |
0.001101 |
500 |
5 |
1024 |
801 |
1762 |
0.000961 |
500 |
5 |
1024 |
819 |
1842 |
0.001023 |
500 |
5 |
1024 |
859 |
1896 |
0.001037 |
1000 |
5 |
1024 |
983 |
3530 |
0.002547 |
1000 |
5 |
1024 |
936 |
3967 |
0.003031 |
1000 |
5 |
1024 |
917 |
3223 |
0.002306 |
1000 |
5 |
1024 |
1424 |
3500 |
0.002076 |
1000 |
5 |
1024 |
1008 |
3496 |
0.002488 |
1000 |
5 |
1024 |
963 |
3004 |
0.002041 |
1500 |
5 |
1024 |
763 |
4971 |
0.004208 |
1500 |
5 |
1024 |
903 |
7195 |
0.006292 |
1500 |
5 |
1024 |
1035 |
5375 |
0.00434 |
1500 |
5 |
1024 |
851 |
4375 |
0.003524 |
1500 |
5 |
1024 |
740 |
4002 |
0.003262 |
2000 |
5 |
1024 |
1065 |
5891 |
0.004826 |
2000 |
5 |
1024 |
1129 |
5701 |
0.004572 |
2000 |
5 |
1024 |
975 |
5019 |
0.004044 |
2000 |
5 |
1024 |
976 |
6288 |
0.005312 |
2000 |
5 |
1024 |
1017 |
7311 |
0.006294 |
3000 |
5 |
1024 |
762 |
11168 |
0.010406 |
3000 |
5 |
1024 |
832 |
8093 |
0.007261 |
3000 |
5 |
1024 |
1000 |
10505 |
0.009505 |
3000 |
5 |
1024 |
849 |
12248 |
0.011399 |
3000 |
5 |
1024 |
936 |
11466 |
0.01053 |
5000 |
5 |
1024 |
1144 |
18218 |
0.017074 |
5000 |
5 |
1024 |
1084 |
18654 |
0.01757 |
5000 |
5 |
1024 |
913 |
18257 |
0.017344 |
5000 |
5 |
1024 |
905 |
22046 |
0.021141 |
5000 |
5 |
1024 |
1043 |
18823 |
0.01778 |
6000 |
5 |
1024 |
847 |
29291 |
0.028444 |
6000 |
5 |
1024 |
824 |
21972 |
0.021148 |
6000 |
5 |
1024 |
743 |
20547 |
0.019804 |
6000 |
5 |
1024 |
890 |
19888 |
0.018998 |
6000 |
5 |
1024 |
810 |
19193 |
0.018383 |
7000 |
5 |
1024 |
962 |
23460 |
0.022498 |
7000 |
5 |
1024 |
1040 |
21367 |
0.020327 |
7000 |
5 |
1024 |
986 |
20902 |
0.019916 |
7000 |
5 |
1024 |
1017 |
24210 |
0.023193 |
7000 |
5 |
1024 |
977 |
21932 |
0.020955 |
上表测试平均值如下:
递归层数 |
参数个数 |
函数栈大小 |
Avrg_durationtime(微秒)[平均值] |
每次递归平均值(微秒) |
10 |
5 |
1024 |
35 |
3.5 |
20 |
5 |
1024 |
52.66 |
2.633 |
30 |
5 |
1024 |
66.33 |
2.211 |
50 |
5 |
1024 |
99 |
1.98 |
100 |
5 |
1024 |
251 |
2.51 |
500 |
5 |
1024 |
1027.2 |
2.0544 |
1000 |
5 |
1024 |
2414.83 |
2.414 |
1500 |
5 |
1024 |
4325.2 |
2.883 |
2000 |
5 |
1024 |
5009.6 |
2.5048 |
3000 |
5 |
1024 |
9820.2 |
3.2734 |
5000 |
5 |
1024 |
18181.8 |
3.63636 |
6000 |
5 |
1024 |
21355.4 |
3.5592 |
7000 |
5 |
1024 |
21377.8 |
3.0597 |
根据平均值表绘折线图如下:(系统在递归层数超过7000会栈溢出)
在同函数参数个数同函数栈大小的情况下,每次函数调用平均时间消耗图:
二.参数个数
递归层数 |
参数个数 |
函数栈大小 |
starttime |
endtime |
durationtime(秒) |
2000 |
1 |
1024 |
904 |
8318 |
0.007414 |
2000 |
1 |
1024 |
982 |
8508 |
0.007526 |
2000 |
1 |
1024 |
968 |
8361 |
0.007393 |
2000 |
1 |
1024 |
1212 |
8101 |
0.006889 |
2000 |
1 |
1024 |
1269 |
5964 |
0.004695 |
2000 |
1 |
1024 |
906 |
9532 |
0.008626 |
2000 |
1 |
1024 |
786 |
8110 |
0.007324 |
2000 |
1 |
1024 |
929 |
7708 |
0.006779 |
2000 |
1 |
1024 |
895 |
7900 |
0.007005 |
2000 |
1 |
1024 |
959 |
4732 |
0.003773 |
2000 |
3 |
1024 |
1569 |
9858 |
0.008289 |
2000 |
3 |
1024 |
999 |
6132 |
0.005133 |
2000 |
3 |
1024 |
1083 |
6673 |
0.00559 |
2000 |
3 |
1024 |
1170 |
8269 |
0.007099 |
2000 |
3 |
1024 |
946 |
5683 |
0.004737 |
2000 |
3 |
1024 |
846 |
6908 |
0.006062 |
2000 |
3 |
1024 |
828 |
7849 |
0.007021 |
2000 |
3 |
1024 |
977 |
7079 |
0.006102 |
2000 |
3 |
1024 |
845 |
8521 |
0.007676 |
2000 |
3 |
1024 |
1047 |
8368 |
0.007321 |
2000 |
5 |
1024 |
911 |
7754 |
0.006843 |
2000 |
5 |
1024 |
1173 |
5677 |
0.004504 |
2000 |
5 |
1024 |
1477 |
12230 |
0.010753 |
2000 |
5 |
1024 |
908 |
5397 |
0.004489 |
2000 |
5 |
1024 |
988 |
10145 |
0.009157 |
2000 |
5 |
1024 |
759 |
8113 |
0.007354 |
2000 |
5 |
1024 |
885 |
8954 |
0.008069 |
2000 |
5 |
1024 |
917 |
6160 |
0.005243 |
2000 |
5 |
1024 |
831 |
6685 |
0.005854 |
2000 |
5 |
1024 |
900 |
7814 |
0.006914 |
2000 |
10 |
1024 |
1359 |
6479 |
0.00512 |
2000 |
10 |
1024 |
1678 |
7318 |
0.00564 |
2000 |
10 |
1024 |
1956 |
9234 |
0.007278 |
2000 |
10 |
1024 |
964 |
8546 |
0.007582 |
2000 |
10 |
1024 |
939 |
6501 |
0.005562 |
2000 |
10 |
1024 |
829 |
7508 |
0.006679 |
2000 |
10 |
1024 |
891 |
8894 |
0.008003 |
2000 |
10 |
1024 |
877 |
7126 |
0.006249 |
2000 |
10 |
1024 |
930 |
6761 |
0.005831 |
2000 |
10 |
1024 |
920 |
7762 |
0.006842 |
上表测试平均值如下:
递归层数 |
参数个数 |
函数栈大小 |
Avrg_durationtime(微秒)[平均值] |
每次递归平均值(微秒) |
2000 |
1 |
1024 |
6742.4 |
3.3712 |
2000 |
3 |
1024 |
6503 |
1.0838*3=3.2514 |
2000 |
5 |
1024 |
6918 |
0.6918*5=3.459 |
2000 |
10 |
1024 |
6478.6 |
0.32393*10=3.2393 |
根据平均值表绘折线图如下:
每个参数每次递归平均值折线图:
结论:在同函数栈大小和同递归层数的情况下,函数参数个数没造成规模性差异的时候,平均时间消耗几乎相近。
三.函数栈大小
递归层数 |
参数个数 |
函数栈大小 |
starttime |
endtime |
durationtime(秒) |
2000 |
5 |
1024 |
1242 |
10890 |
0.009648 |
2000 |
5 |
1024 |
1337 |
14279 |
0.012942 |
2000 |
5 |
1024 |
1069 |
8970 |
0.007901 |
2000 |
5 |
1024 |
1023 |
12279 |
0.011256 |
2000 |
5 |
1024 |
970 |
8142 |
0.007172 |
2000 |
5 |
2048 |
1177 |
14290 |
0.013113 |
2000 |
5 |
2048 |
984 |
16388 |
0.015404 |
2000 |
5 |
2048 |
1051 |
12327 |
0.01276 |
2000 |
5 |
2048 |
899 |
15451 |
0.014552 |
2000 |
5 |
2048 |
1372 |
14674 |
0.013302 |
2000 |
5 |
3072 |
1238 |
15792 |
0.014554 |
2000 |
5 |
3072 |
914 |
13615 |
0.012701 |
2000 |
5 |
3072 |
1406 |
17867 |
0.016461 |
2000 |
5 |
3072 |
978 |
17876 |
0.016898 |
2000 |
5 |
3072 |
1008 |
16438 |
0.01543 |
2000 |
5 |
4096 |
1242 |
10890 |
0.009648 |
2000 |
5 |
4096 |
1337 |
14279 |
0.012942 |
2000 |
5 |
4096 |
1069 |
8970 |
0.007901 |
2000 |
5 |
4096 |
1023 |
12279 |
0.011256 |
2000 |
5 |
4096 |
970 |
8142 |
0.007172 |
上表测试平均值如下:
递归层数 |
参数个数 |
函数栈大(int) |
Avrg_durationtime(微秒)[平均值] |
每个参数每次递归平均值(微秒) |
2000 |
5 |
1024 |
5009.6 |
2.5048/5=0.50096 |
2000 |
5 |
2048 |
9783.8 |
4.8919/5=0.97838 |
2000 |
5 |
3072 |
13826.2 |
6.9131/5=1.38262 |
2000 |
5 |
4096 |
15208.8 |
7.6044/5=1.52088 |
根据平均值表绘折线图如下:(函数栈大小超过5120会栈溢出)
每个参数每次递归平均值折线图:
结论:单个函数参数单词函数调用所需要的时间消耗与函数栈大小几乎成对等倍数增长。
四. 结论
1, 在函数参数和函数栈大小一定时,函数递归调用的时间消耗在一定递归层数范围内与递归层数呈现正相关关系。
2, 在函数递归层数和函数参数个数一定时,函数递归调用的时间消耗在一定函数栈大小范围内与函数栈大小呈现正相关关系。
3, 在函数参数个数没有造成规模性差异的情况下(即函数签名所占栈内存不大时),函数参数个数所造成的时间消耗几乎相近。
4, 在函数参数个数没有造成规模性差异的情况下(即函数签名所占栈内存不大时),函数调用层数和函数栈大小对函数调用的时间消耗产生明显影响。
注:1,由于程序测试的操作系统环境的差异,可能会导致数据误差。
2,计时方法的差异,可能会导致数据误差。
3,个人编码能力也可能导致数据误差。