【DPDK】【CPU usage】DPDK應用如何計算當前系統的壓力

【前言】linux

  使用DPDK開發的朋友應該都瞭解使用dpdk的fwd線程的工做模式是polling模式,即100%輪詢的方式去加速網絡IO,這樣咱們在操做系統層面上來觀察目標processer會發現usage一直爲100%,可是這真的是系統的真實負載麼?很顯然並非,本文給出一種方法來計算dpdk的fwd線程的真實負載的方法。api

【場景】緩存

  使用DPDK頭痛的一點就是DPDK的fwd線程工做在polling模式,會直接消耗一整個processer的計算資源,有的時候爲了性能考慮,每每還會給當前processer設置isolcpus,將當前processer從內核的CFS調度器中「剝離」出來,防止有其餘的task被「不長眼」的CFS調度器調度到和fwd線程同一個processer上,出現context switch,引發性能降低。網絡

  而工做在polling模式的fwd線程會出現很是蛋疼的一點就是面臨「沒法有效的感知當前processer的壓力」的問題。查看操做系統的相關信息,會發現這個processer的usage一直處於100%,可是真實狀況真的是這樣麼?並非,在流量處於低谷的時候,這個processer每每會出現空轉的狀況,就是調用dpdk的api收包函數調100次,次次收包個數都是0,由於根本就沒有流量,因此須要一種新的方法來計算使用dpdk fwd線程的負載狀況。app

  額外多說一點,爲了防止fwd線程出現空轉,目前有不一樣種方法來「儘可能」解決這種空轉問題,主流的一般有兩種:函數

  1. 利用sleep函數,簡單粗暴,結合內核的NAPI策略,設定一個指望收包個數值,當實際收包個數小於這個數值就判斷當前流量不大,sleep一下。
  2. 利用dpdk的rsc中斷來解決,因爲uio驅動只有一箇中斷號,所以這種方法在uio驅動基本無法用,只能在vfio場景下用。

  固然怎麼防止dpdk fwd線程出現空轉的解決方法不是這篇文章想討論的主題,我後續會寫我我的的解決方案,本篇文章更多的會聚焦在如何估算負載狀況。性能

【分析】測試

  這裏首先說明一下,這個方法裏面有一部分是來自於dpdk社區的一篇文章,由intel專家ilia所寫,這裏是文章原文地址,本文中會有很多部分來自於這篇文章原文,可是本篇文章在實際應用中仍然是有些地方須要注意的。ui

https://software.intel.com/en-us/vtune-cookbook-core-utilization-in-dpdk-appsspa

  接下來我會結合實際應用和ilia的文章來闡述怎麼「估算」dpdk fwd線程的負載狀況。

  先直接說結論:

在不一樣的流量壓力下,dpdk fwd線程對某個網卡隊列的收包行爲其實是存在必定的分佈的

  這句話怎麼理解呢?實際上這句話就是intel專家ilia那篇文章的主要思想。這句話完整的解釋是這樣的:

在不一樣的流量壓力下,dpdk fwd線程對某個網卡的某條隊列進行收包操做,單位時間內的收包次數中,收到數據包的個數存在必定的分佈

  舉個例子:

  我在10s內,執行了1000次收包操做,在滿載的流量壓力下,1000次收包可能次次收包都能收上來32個數據包;在50%壓力下,我可能只有300次收包是一次調用收上來32個包;在10%的壓力下,我可能只有不到100次的收包是一次性收上來32個包。

  若是能明白這個例子,就能明白前面所說的結論,關於這個結論能夠直接看【測試結果】一章,觀察測試結果是否符合結論。

  以收包個數爲0的次數爲基準,那麼能夠推導出一個公式:

 

 

  此公式也正是ilia的文章中提到的公式。

  公式的解釋就是在循環必定次數的收包狀況下,收包個數爲0的次數佔比,即爲dpdk rx spin time,直譯就是dpdk 收包空轉次數,那麼再將此值用1減去,便可獲得dpdk fwd的壓力狀況。

【測試結果】

   以新建來測試上述公式的結果(新建比較吃cpu...)

 

測試結果1

 

 測試結果2

 

 測試結果3

【結論】

   根據測試結果來看,能夠清晰的看到,在不一樣的系統壓力下,DPDK在單位時間內的收包個數爲0的次數佔單位時間內總的收包次數的比重是存在必定分佈的,在壓力越高的狀況下,收包爲0的次數越少,在100%滿載壓力的狀況下,收包個數爲0的次數與收包個數爲32的次數比的差距已經很是巨大,這也是ilia那篇文章中主要說的內容,可是是存在如下幾個問題。

  1. 在真實場景下,一個fwd線程每每不止對單獨的某個網卡隊列進行收包,可能會有多個網卡隊列,這個時候怎麼來評估負載呢?
  2. 這種估算方法實際上依賴於DPDK的收包隊列,可是若是某個fwd線程沒有隊列呢?這種狀況每每發生在設備processer的數量大於全部的網卡隊列之和的狀況。就是所謂的「僧多粥少」狀況,面對這種狀況,常見的處理方法是接管網卡驅動的processer收上來數據包後,經過計算RSS將數據包經過ring發送到其餘沒有接管網卡隊列的processer上,那麼面對這些沒有「搶到」網卡隊列的processer來講,怎麼計算出負載呢?
  3. fwd線程使用了sleep方法來休眠減小空轉,理論上通過sleep後會影響最終估算的「負載」,使計算的負載值偏大。這個也很好理解,利用進程sleep的方法去減小壓力,最多見的影響就是包的時延會增大,數據包會在進程睡眠的時候在rx ring中「積壓」,那麼每次收數據包的時候收到的數據包的個數就會偏多,出現0的次數就會少。
  4. fwd線程開了rxq中斷,這種狀況也不須要用本文的方法去計算負載了,直接pidstat就能夠看了...

  面對上述幾個問題:

  1. 多隊列的狀況下,我我的認爲能夠採起方案是取壓力最大的隊列的壓力值,實際測試以後發現效果還不錯,所以最後採用了這種方案。
  2. 這種狀況下,我我的以爲能夠經過和收網卡隊列相同的思路,在沒有接管到網卡隊列的fwd線程去收ring中的數據包時,也採用這種計算策略,當時因爲需求問題,我本人並無驗證,有興趣的道友能夠去嘗試一下。
  3. sleep的這種狀況比較難以免,須要根據實際狀況去分析,因爲採用sleep下降空轉的策略中經常會有兩個參數,一個是sleep的時間,一個是指望的權重,我本人實際測試,這二者的參數設置的不一樣對負載的估算是不一樣的,一樣須要實際場景你測試調整。

【另外一個問題:DPDK應用怎麼「預見」即將可能發生的流量過載】

  在估算出實際fwd線程的壓力後,會發現有這樣的一個問題,系統能夠感知逼近的壓力,可是沒法得知壓力的具體大小,舉個例子,當上述公式計算出最後fwd線程壓力爲100%時,此時是過載仍是滿載呢?在性能測試時,這兩種狀態雖然在表現上是壓力皆爲100%,可是滿載的狀況下,並不會發生丟包,而過載的狀況下網卡會發生無差異丟包。

  相信搞過性能的朋友經常會遇到一種丟包狀況:

rx_missed

  rx_missed,這種錯誤在性能測試時測試系統上線會經常預見,在傳統linux場景下,利用ethtool -S [port name]便可觀察到此種丟包。這種丟包經常是因爲網卡的rx隊列被數據包「打爆」了,cpu收包的速度比不上實際數據包來的速度,那麼就會造成相似於「漏斗」同樣的流程,漏斗上方的進水量大於漏斗下方的出水量,那麼只要時間足夠,漏斗上方溢水是早晚的事情。在網卡收包時,cpu從rx ring中收取數據包,可是當流量壓力過大時,rx ring會充滿待處理的數據包,此時網卡沒法再將數據包扔到rx ring中,那麼網卡會將接下來來臨的數據包進行無差異丟棄,並在rx_missed計數上進行增長。

  那麼面對這種場景,有沒有方法能夠儘量的預見到即將可能來臨的流量高峯呢?通過上述的敘述相信心中已經有了一種答案,那就是查看網卡rx ring,查看rx ring中還有多少待處理的描述符(description)便可,這裏須要對processer怎麼從網卡上收包有必定了解,不瞭解的話也不要緊,我大概介紹一下原理接口,先上圖

圖4.收包原理圖

P.S.這個圖一樣來自ilia專家的那篇文章中,感謝專家...

  上圖是一個網卡和processer常見的收包協做圖(發包就是反過來),一般網卡的rx ring上會有兩個index變量,一個叫作Head,一個叫作Tail,網卡會將收取的數據包push到Head指向的包描述符的內存中(不知道描述符是啥的童鞋就當作網卡收包向Head指針指向的空間去扔就好了),而後Head++,一樣,processer從Tail指向的包描述符的內存中去收數據包,這樣一個環形隊列,網卡做爲producer,而processer做爲consumer演出了一場網卡收包的協奏。這種場景下,而且網卡在將數據包扔到rx ring時,會將對應位置的包描述符回寫一個0x01的狀態位,以ixgbe驅動爲例:代碼目錄drivers/net/ixgbe/base/ixgbe_type.h

/* Receive Descriptor bit definitions */
#define IXGBE_RXD_STAT_DD    0x01 /* Descriptor Done */
#define IXGBE_RXD_STAT_EOP    0x02 /* End of Packet */
#define IXGBE_RXD_STAT_FLM    0x04 /* FDir Match */
#define IXGBE_RXD_STAT_VP    0x08 /* IEEE VLAN Packet */

  也就是上述代碼中的IXGBE_RXD_STAT_DD標誌,那綜上所述,咱們只須要統計Tai -> Head之間有多少個0X01狀態的描述符就能夠肯定(也就是上述圖4的右側Tail和Head之間的方塊數),目前網卡rx ring中「積存」了多少數據包。

  可是,上述在操做,在DPDK的代碼中都已經實現啦!

uint32_t
ixgbe_dev_rx_queue_count(struct rte_eth_dev *dev, uint16_t rx_queue_id) { #define IXGBE_RXQ_SCAN_INTERVAL 4 volatile union ixgbe_adv_rx_desc *rxdp; struct ixgbe_rx_queue *rxq; uint32_t desc = 0; rxq = dev->data->rx_queues[rx_queue_id]; rxdp = &(rxq->rx_ring[rxq->rx_tail]); while ((desc < rxq->nb_rx_desc) && (rxdp->wb.upper.status_error & rte_cpu_to_le_32(IXGBE_RXDADV_STAT_DD))) { desc += IXGBE_RXQ_SCAN_INTERVAL; rxdp += IXGBE_RXQ_SCAN_INTERVAL; if (rxq->rx_tail + desc >= rxq->nb_rx_desc) rxdp = &(rxq->rx_ring[rxq->rx_tail + desc - rxq->nb_rx_desc]); } return desc; }

  上述代碼位置:drivers/net/ixgbe/ixgbe_rxtx.c中,可是上述函數有個很大的問題就是時間複雜度很高...最極端的狀況下要循環4096/4 = 1024次(網卡rx ring最大4096,4是因爲上述函數遍歷的步長爲4)才能夠算出有多少個待處理的包。因此上述函數能夠利用二分法來加速獲取計算的過程,最極端的狀況下也只須要循環12次就能夠算出網卡隊列中積存的數據包個數,關鍵就在於IXGBE_RXDADV_STAT_DD這個標誌,這裏就不說了,有興趣的能夠思考一下。

  那獲得了網卡隊列中積存的數據包個數以後咱們怎麼才能判斷出是否將要出現「過載流量」呢?

  這個也很簡單,只要網卡隊列中積存的數據包處於一個較低的水平,那麼就不會出現丟包的可能;若是網卡隊列中積存的數據包數量忽然上升,那麼頗有可能網卡的rx ring直接被流量打爆,在高端設備中,數據包量很是龐大的場景下,打爆最大4096長度的網卡隊列(最多隻能緩存4096個數據包)就是一瞬間的事情,這個一樣也很好理解,仍是以漏斗舉例,實際上,若是注水的速度小於出水的速度,不管注水的時間長短,漏斗中積存的水量一定爲一個較低的水平,或者是根本不會有積存的水量;而當注水的速度大於出水的速度,那麼將漏斗打滿只是時間問題。

  通過個人實際測試,滿載的狀況下,網卡隊列中積存的數據包一直處於300個如下的水平,可是隻要測試機的網絡流量超過了系統的處理能力,網卡隊列中積存的數據包很快就上了1000以上.....

【一點想法】

單單靠ilia專家的公式只能計算出當前系統的壓力狀況(且有條件限制),沒法預知即將到來的流量高峯;單單靠網卡rx ring中積存的數據包個數只能判斷出即未來流量高峯,可是卻沒法得知fwd線程壓力,那麼咱們須要一套組合拳:

if (fwd_rx_load >= 90 && rte_get_rx_queue_count() >= 512)
    //須要採起措施,流量極可能即將過載
相關文章
相關標籤/搜索