我問大家一個問題,下圖中一個最簡單的例子,會導致哪些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.函式呼叫太多了會有性能問題嗎?
我的公眾號是「開發內功修煉」,在這里我不是單純介紹技術理論,也不只介紹實踐經驗,而是把理論與實踐結合起來,用實踐加深對理論的理解、用理論提高你的技術實踐能力,歡迎你來關注我的公眾號,也請分享給你的好友~~~
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/198455.html
標籤:其他
上一篇:開發工具:Mybatis.Plus.插件三種方式的逆向工程
下一篇:函式呼叫太多了會有性能問題嗎?
