前面说了这么多,可能有的同学就会想了:“我是应用层的开发,这么底层的开销和我有什么关系?”。我想说的是,如果你只是一个初级或者中级开发工程师,这些确实没有必要了解。但是如果你想成为一名高级、或者是资深开发工程师,那么你应该具备大致估算你手下写出的每一行代码开销的能力。
在经过了前面多篇文章的历练,相信大家对系统调用、进程上下文切换、软中断的开销也有了拿捏。那么接下来让我们用一个实践中php最简单的例子来演练一下。
测试代码:
<?php
$redis = new Redis();
$redis->connect('*.*.*.*', 6339);
sleep(60);
echo "Test begin\n";
for($i=0; $i<10000; $i++){
$redis->get('test');
}
echo "Test end!\n ";
sleep(60);
例子非常的简单,就是一句后端同学代码里经常出现的从Redis里获取了一条数据而已。那么让我们看看它到底会产生哪些开销?
1.系统调用
平均每次get都需要进行多次系统调用才可完成。
# strace -c php main.php
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
97.24 0.039698 1 30003 poll
2.20 0.000899 0 10003 sendto
0.30 0.000122 0 10000 recvfrom
0.13 0.000053 0 10069 gettimeofday
0.03 0.000013 2 6 socket
0.03 0.000012 0 408 munmap
0.02 0.000008 0 657 read
2、进程上下文切换
每次get都会导致进程进入主动上下文切换。
# php main.php
然后再另起一个控制台,分别赶在实验开始前和实验开始后执行如下两行命令:
# grep ctxt /proc/14862/status
voluntary_ctxt_switches: 4
nonvoluntary_ctxt_switches: 43
# grep ctxt /proc/14862/status
voluntary_ctxt_switches: 10005
nonvoluntary_ctxt_switches: 49
3、软中断
软中断
# cat /proc/softirqs
CPU0 CPU1 CPU2 CPU3
HI: 0 0 0 0
TIMER: 196173081 145428444 154228333 163317242
NET_TX: 0 0 0 0
NET_RX: 178159928 116073 10108 160712
# cat /proc/softirqs
CPU0 CPU1 CPU2 CPU3
HI: 0 0 0 0
TIMER: 196173688 145428634 154228610 163317624
NET_TX: 0 0 0 0
NET_RX: 178170212 116073 10108 160712
该虚机的软中断亲和性在CPU0上,178170212-178159928 = 10284(多出来的284是机器上其它的小服务)。
个人公众号“开发内功修炼”,打通理论与实践的任督二脉。
总结
看似一次非常简单的redis get操作就会把所有系统态的高开销操作都涉及到了。一次进程上下文切换、一次软中断、若干次系统调用。 其实除了上面这些容易评估到的开销外,还有如L1、L2 cache miss,以及TLB cache miss这些在进程被切换掉的时候也会有额外的开销。
网友评论