我问大家一个问题,下图中一个最简略的例子,会导致哪些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.函数调用太多了会有性能问题吗?

我的公众号是「开发内功修炼」,在这里我不是单纯介绍技术实践,也不只介绍实践经验。而是把实践与实际联合起来,用实际加深对实践的了解、用实践进步你的技术实际能力。欢送你来关注我的公众号,也请分享给你的好友~~~