共计 2408 个字符,预计需要花费 7 分钟才能阅读完成。
我问大家一个问题,下图中一个最简略的例子,会导致哪些 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 里获取了一条数据而已。那么让咱们看看它到底会产生哪些开销?
1. 零碎调用
# 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 操作都须要执行屡次零碎调用才可实现。
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
每次 get 都会导致过程进入被迫上下文切换,在网络 IO 密集型的利用里被迫上下文切换要比工夫片到了被动切换要多的多!
3、软中断
每次在 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 篇专辑:
- 1. 你认为你的多核 CPU 都是真核吗?多核“假象”
- 2. 据说你只知内存,而不知缓存?CPU 示意很伤心!
- 3.TLB 缓存是个神马鬼,如何查看 TLB miss?
- 4. 过程 / 线程切换到底须要多少开销?
- 5. 协程到底比线程牛在什么中央?
- 6. 软中断会吃掉你多少 CPU?
- 7. 一次零碎调用开销到底有多大?
- 8. 一次简略的 php 申请 redis 会有哪些开销?
- 9. 函数调用太多了会有性能问题吗?
我的公众号是「开发内功修炼」,在这里我不是单纯介绍技术实践,也不只介绍实践经验。而是把实践与实际联合起来,用实际加深对实践的了解、用实践进步你的技术实际能力。欢送你来关注我的公众号,也请分享给你的好友~~~