发布于2021-03-14 05:42 阅读(822) 评论(0) 点赞(30) 收藏(5)
我问大家一个问题,下图中一个最简单的例子,会导致哪些CPU开销产生?你是否能够说清楚?
<?php
...
$redis->get('test');
...
这个例子一下子就把大家在我的文章里学到的东西和你的实际工作结合起来了。怎么样,是不是足够简单?就是一句php代码从redis实例中获取一个key的value值而已,相信类似的代码你天天都在写。对这句redis get实际开销的理解水平,就代表了你的内功的深度。
在前面的文章中介绍了一些和CPU相关的硬件、内核知识,把开销大户进程上下文切换、系统调用、软中断的具体开销都挨个分析了一遍。没有看过的同学请关注并翻看我以前的文章。不过,这时候我觉得有很多开发同学都有一个疑惑,仍然是觉得:“我是应用层的开发,这么底层的开销和我有什么关系?” 或者是说:“线上服务器的运维不都应该是运维的工作吗?和开发又没关系”
我想说的是,如果你只是一个初级或者中级开发工程师,这些确实没有必要了解。但是如果你想成为一名高级、或者是资深开发工程师,那么你应该具备大致估算你手下写出的每一行代码开销的能力,要对自己代码在线上的运行开销负责!
接下来,我们就来带大家从更深层次的方向认识到这句简单代码的开销。为了便于测试,我们对代码进行一些简单的改造,最终的实际测试代码如下:
<?php
$redis = new Redis();
$redis->connect({某Redis server的IP}, 6339);
sleep(60);
echo "Test begin\n";
for($i=0; $i<10000; $i++){
$redis->get('test');
}
echo "Test end!\n ";
sleep(60);
例子非常的简单,就是一句后端同学代码里经常出现的从Redis里获取了一条数据而已。那么让我们看看它到底会产生哪些开销?
# 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
我们代码所调用的get函数,其实是php的一个redis扩展提供的。该扩展又会去调用Linux系统的网络库函数,库函数再去调用内核提供的系统调用。这个调用层次模型如下:
从实际测试结果可见,每次get操作都需要执行多次系统调用才可完成。
每次调用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
每次get都会导致进程进入自愿上下文切换,在网络IO密集型的应用里自愿上下文切换要比时间片到了被动切换要多的多!
每次在redis服务器返回数据的时候,网卡都会通过软中断的方式来让内核处理数据包。因此
# 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是机器上其它的小服务)。
每次get请求收到数据返回的时候,内核必须要支出一次软中断的开销!
看似一次非常简单的redis get操作就会把所有系统态的高开销操作都涉及到了。一次进程上下文切换、一次软中断、若干次系统调用。在经过了前面多篇文章的历练,相信大家对它们的开销有了一个量化的拿捏。其实除了上面这些容易评估到的开销外,还有如L1、L2 cache miss,以及TLB cache miss这些在进程被切换掉都会造成cache命中率下降,也会导致额外开销。
所以,你以为的简单,其实不一定简单!
开发内功修炼之CPU篇专辑:
我的公众号是「开发内功修炼」,在这里我不是单纯介绍技术理论,也不只介绍实践经验。而是把理论与实践结合起来,用实践加深对理论的理解、用理论提高你的技术实践能力。欢迎你来关注我的公众号,也请分享给你的好友~~~
原文链接:https://www.cnblogs.com/kfngxl/p/13910801.html
作者:肚子里的大哥
链接:http://www.phpheidong.com/blog/article/3039/a82a3962be6de4e102c6/
来源:php黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 php黑洞网 All Rights Reserved 版权所有,并保留所有权利。 京ICP备18063182号-4
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!