概述

隨著網際網路的高速發展,雲產業的快速突起,基礎架構網路逐漸偏向基於通用計算平臺或模組化計算平臺的架構融合,來支援多樣化的網路功能,傳統的PC機器在分散式計算平臺上的優勢更為明顯。在這些針對海量資料處理或海量使用者的服務場景,高效能程式設計顯得尤為重要。

全文路線

  • 分析了目前的傳統伺服器結構以及可能存在的問題引出需求
  • 提出DPDK開發套件如何突破作業系統限制
  • 之後分析了dpdk的整體結構
  • 最後對相關聯的技術和場景做擴充套件.

背景分析

前10年中,網路程式效能優化的目標主要是為了解決C10K問題,其研究主要集中在如何管理數萬個客戶端併發連線,各種I/O框架下如何進行效能優化,以及作業系統引數的一些優化。當前,解決C10K問題的伺服器已經非常多。Nginx和Lighttpd兩款非常優秀的基於事件驅動的web服務框架,Tornado和Django則是基於python開發的非阻塞的web框架這些軟體使得C10K已經不再是問題了。

  • 從整體上看
    為了滿足日益增長的需求主要採用分散式叢集來分擔負荷,應對大量的使用者請求.

叢集.
  • 從結構上來看一個節點的伺服器框架包含
    • 網路模組
    • 事件驅動模組
    • 隔離,多核業務分發模組
    • 業務層
  • 在單個節點上,核的使用來看,主要包括
    • 單執行緒伺服器
      優點是無競爭,缺點是沒有充分利用系統資源
    • 多程序模型
      隔離性好,利用了系統更多的資源,缺點是程序間資源共享難
    • 多執行緒模型
      充分利用系用資源,競爭需要小心處理

需求分析

綜合分析

  • 在應對網路密集型的巨大資料量時,一般選擇是橫向擴充套件節點,但是節點的增多會變相的增加裝置成本和技術風險,且當叢集節點到一定的量後,節點之間的互動成本本身就會成為瓶頸。
  • 在特定場合下,譬如嵌入式裝置上的後臺服務,伺服器不可能搭建叢集。

因此提升伺服器本身效能同樣重要。

具體分析

傳統伺服器可能有下面的潛在問題

  • 非同步模式的弊端
    一般我們使用epoll來高效的處理網路讀寫事件。在基於多執行緒的伺服器設計框架中,在沒有請求到來的時候,執行緒將會休眠,當資料到來時,將由作業系統喚醒對應的執行緒,也就是說核心需要負責執行緒間頻繁的上下文切換,我們是在依靠作業系統排程系統來服務網路包的排程。在網路負載很大的場景下只會造成核滿轉且不斷相互切換,進一步增加負荷.那麼就需要回到最原始的方式,使用輪詢方式來完成一切操作,來提升效能。

  • 協議棧的擴充套件性
    Linix誕生之初就是為電話電報控制而設計的,它的控制平面和資料轉發平面沒有分離,不適合處理大規模網路資料包。並且為了全面的支援使用者空間的各個功能,協議棧中嵌入了大量用於對接的介面,如果能讓應用程式直接接管網路資料包處理、記憶體管理以及CPU排程,那麼效能可以得到一個質的提升。為了達到這個目標,第一個要解決的問題就是繞過Linux核心協議棧,因為Linux核心協議棧效能並不是很優秀,如果讓每一個數據包都經過Linux協議棧來處理,那將會非常的慢。像Wind River和6 Wind Gate等公司自研的核心協議棧宣稱比Linux UDP/TCP協議棧效能至少提高500%以上,因此能不用Linux協議棧就不用。
    不用協議棧的話當然就需要自己寫驅動了,應用程式直接使用驅動的介面來收發報文。PF_RING,Netmap和intelDPDK等可以幫助你完成這些工作,並不需要我們自己去花費太多時間。
    Intel官方測試文件給出了一個性能測試資料,在1S Sandbridge-EP 8*2.0GHz cores伺服器上進行效能測試,不用核心協議棧在使用者態下吞吐量可高達80Mpps(每個包處理消耗大約200 cpu clocks),相比之下,使用Linux核心協議棧效能連1Mpps都無法達到。

  • 多核的可擴充套件性
    多核的可擴充套件性對效能提升也是非常重要的,因為伺服器中CPU頻率提升越來越慢,納米級工藝改進已經是非常困難的事情了,但可以做的是讓伺服器擁有更多的CPU和核心,像國家超級計算中心的天河二號使用了超過3w顆Xeon E5來提高效能。在程式設計過程中,即使在多核環境下也很快會碰到瓶頸,單純的增加了處理器個數並不能線性提升程式效能,反而會使整體效能越來越低。一是因為編寫程式碼的質量問題,沒有充分利用多核的並行性,二是伺服器軟體和硬體本身的一些特性成為新的瓶頸,像匯流排競爭、儲存體公用等諸多影響效能平行擴充套件的因素。那麼,我們怎樣才能讓程式能在多個CPU核心上平行擴充套件:儘量讓每個核維護獨立資料結構;使用原子操作來避免衝突;使用無鎖資料結構避免執行緒間相互等待;設定CPU親緣性,將作業系統和應用程序繫結到特定的核心上,避免CPU資源競爭;在NUMA架構下儘量避免遠端記憶體訪問

  • 記憶體的可擴充套件性
    記憶體的訪問速度永遠也趕不上cache和cpu的頻率,為了能讓效能平行擴充套件,最好是少訪問。
    從記憶體消耗來看,如果每個使用者連線佔用2K的記憶體,10M個使用者將消耗20G記憶體,而作業系統的三級cache連20M都達不到,這麼多併發連線的情況下必然導致cache失效,從而頻繁的訪問記憶體來獲取資料。而一次記憶體訪問大約需要300 cpuclocks,這期間CPU幾乎被空閒。因此減少訪存次數來避免cachemisses是我們設計的目標。
    指標不要隨意指向任意記憶體地址,因為這樣每一次指標的間接訪問可能會導致多次cache misses,最好將需要訪問的資料放到一起,方便一次性載入到cache中使用。
    按照4K頁來計算,32G的資料需要佔用64M的頁表,使得頁表甚至無法放到cache中,這樣每次資料訪問可能需要兩次訪問到記憶體,因此建議使用2M甚至1G的大頁表來解決這個問題。

解決方案

  • 控制層留給Linux做,其它資料層全部由應用程式來處理。
    減少系統排程、系統呼叫、系統中斷,上下文切換等
  • 摒棄Linux核心協議棧,將資料包傳輸到使用者空間定製協議棧
  • 使用多核程式設計技術替代多執行緒,將OS綁在指定核上執行
  • 針對SMP系統,使CPU儘量使用所在NUMA系統節點的記憶體,減少記憶體刷寫
  • 使用大頁面,減少訪問
  • 採用無鎖技術解競爭

而DPDK恰好為我們提供瞭解決問題的腳手架。

DPDK

概述

Intel® DPDK全稱Intel Data Plane Development Kit,是intel提供的資料平面開發工具集,為Intel architecture(IA)處理器架構下使用者空間高效的資料包處理提供庫函式和驅動的支援,它不同於Linux系統以通用性設計為目的,而是專注於網路應用中資料包的高效能處理。目前已經驗證可以執行在大多數Linux作業系統上,包括FreeBSD 9.2、Fedora release18、Ubuntu 12.04 LTS、RedHat Enterprise Linux 6.3和Suse EnterpriseLinux 11 SP2等。DPDK使用了BSDLicense,極大的方便了企業在其基礎上來實現自己的協議棧或者應用。
需要強調的是,DPDK應用程式是執行在使用者空間上利用自身提供的資料平面庫來收發資料包,繞過了Linux核心協議棧對資料包處理過程。Linux核心將DPDK應用程式看作是一個普通的使用者態程序,包括它的編譯、連線和載入方式和普通程式沒有什麼兩樣。

總體結構

主要有以下幾個核心

  • 網路層模組
  • 記憶體管理模組
  • 核心管理模組

網路模組

總體分析

DPDK對從核心層到使用者層的網路流程相對傳統網路模組進行了特殊處理,下面對傳統網路模組結構和DPDK中的網路結構做對比

傳統linux網路層:

硬體中斷--->取包分發至核心執行緒--->軟體中斷--->核心執行緒在協議棧中處理包--->處理完畢通知使用者層

使用者層收包-->網路層--->邏輯層--->業務層

dpdk網路層:

硬體中斷--->放棄中斷流程
使用者層通過裝置對映取包--->進入使用者層協議棧--->邏輯層--->業務層

對比後總結

dpdk優勢:

  • 減少了中斷次數。
  • 減少了記憶體拷貝次數。
  • 繞過了linux的協議棧,進入使用者協議棧,使用者獲得了協議棧的控制權,能夠定製化協議棧降低複雜度

dpdk劣勢

  • 核心棧轉移至使用者層增加了開發成本.
  • 低負荷伺服器不實用,會造成核心空轉.

具體分析

  • 攔截中斷,不觸發後續中斷和流程流程,繞過協議棧。
    如下所示,通過UIO能夠重設核心中終端回撥行為從而繞過協議棧後續的處理流程

    trigger
  • 無拷貝收發包,減少記憶體拷貝開銷
    如圖所示

    • dpdk的包全部在使用者空間使用記憶體池管理。
    • 核心空間與使用者空間的記憶體互動不用進行拷貝,只做控制權轉移。

      packet trans
  • 協議棧庫
    dpdk為使用者提供了部分協議處理封裝,使使用者能輕鬆定製化協議棧。

記憶體管理

hugepage技術

Linux系統的記憶體管理依賴於儲存器山,如下所示
Linux在記憶體管理中採用受保護的虛擬地址模式,在程式碼中地址分為3類:邏輯地址、線性地址、實體地址。程式使用具體記憶體簡單說就是邏輯地址通過分段機制對映轉化為線性地址,然後線性地址通過分頁機制對映轉化為實體地址的過程,而在實際使用中,僅將線性地址對映為實體地址的過程中,需要從記憶體中讀取至少四次頁目錄表(Page Directory)和頁表 (Page Table),為了加快核心讀取速度,CPU在硬體上對頁表做了快取,就是TLB。
線性地址先從TLB獲取快取記憶體記憶體,如果不存在就從記憶體表獲取,如果有直接的對映,直接從記憶體讀取,沒有則產生缺頁中斷,從新分配實體記憶體,或者從硬碟上將swap讀取。具體圖示如下:


hupage.jpg

普通頁大小是每個4K,如果是4K頁的定址如下,使用實體記憶體時需要多級查詢才能找到對應的記憶體


page-1.gif

4K的頁表是linux針對一般情況得出的合適大小,然而對於特殊應用可以通過擴大頁表面積提高記憶體使用效率。

dpdk使用hupage的思想就是讓程式儘量獨佔記憶體防止記憶體換出,擴大頁表提高hash命中率,通過hugage技術擴大了該使用的頁表大小,設定為更適合高頻記憶體使用程式的狀態,獲得了以下幾點優勢。

  • 無需交換。也就是不存在頁面由於記憶體空間不足而存在換入換出的問題
  • 減少TLB負載。
  • 降低page table查詢負載

NUMA

為了解決單核帶來的CPU效能不足,出現了SMP,但傳統的SMP系統中,所有處理器共享系統匯流排,當處理器數目越來越多時,系統匯流排競爭加大,系統匯流排稱為新的瓶頸。NUMA(非統一記憶體訪問)技術解決了SMP系統可擴充套件性問題,已成為當今高效能伺服器的主流體系結構之一。
NUMA系統節點一般是由一組CPU和本地記憶體組成。NUMA排程器負責將程序在同一節點的CPU間排程,除非負載太高,才遷移到其它節點,但這會導致資料訪問延時增大。下圖是2顆CPU支援NUMA架構的示意圖,每顆CPU物理上有4個核心。

dpdk記憶體分配上通過proc提供的記憶體資訊,使cpu儘量使用靠近其所在節點的記憶體,避免訪問遠端記憶體影響效率。


numa.png

核心管理模組

Affinity是程序的一個屬性,這個屬性指明瞭程序排程器能夠把這個程序排程到哪些CPU上。在Linux中,我們可以利用CPU affinity 把一個或多個程序繫結到一個或多個CPU上。CPU Affinity分為2種,soft affinity和hard affinity。soft affinity僅是一個建議,如果不可避免,排程器還是會把程序排程到其它的CPU上。hard affinity是排程器必須遵守的規則。為什麼需要CPU繫結?

  • 增加CPU快取的命中率
    CPU之間是不共享快取的,如果程序頻繁的在各個CPU間進行切換,需要不斷的使舊CPU的cache失效。如果程序只在某個CPU上執行,則不會出現失效的情況。在多個執行緒操作的是相同的資料的情況下,如果把這些執行緒排程到一個處理器上,大大的增加了CPU快取的命中率。但是可能會導致併發效能的降低。如果這些執行緒是序列的,則沒有這個影響。

  • 適合time-sensitive應用在real-time或time-sensitive應用中,我們可以把系統程序繫結到某些CPU上,把應用程序繫結到剩餘的CPU上。典型的設定是,把應用繫結到某個CPU上,把其它所有的程序繫結到其它的CPU上。

核管理結構

dpdk啟動時會建立會分析系統的邏輯核屬性建立對映表並統一管理,每個核主要屬性如下.

每個核屬性包括邏輯核id, 硬核id, numa節點id。dpdk會根據系統預設狀態生成一一繫結的對映表,使用者可以根據需求更改對映表,後續dpdk框架會根據該對映表進行核繫結。

class core{
    lcore_id;           //邏輯核id
    core_id;            //硬核id
    socket_id;         //NUMA節點id
}

class core coremap[ ]     //所有邏輯核的對映表

多核排程框架

  • 伺服器啟動時選取一個邏輯核做主核
  • 然後啟動其他核做從核
  • 所有執行緒都根據對映表做核繫結
  • 控制核主要完成pci,記憶體,日誌等系統的初始化
  • 從核啟動後等待主核初始化完畢後掛載業務處理入口
  • 從核執行業務程式碼

thread

競爭處理

  • 多執行緒在構建伺服器時常常要處理競爭問題,dpdk提供了支援多個執行緒操作的無鎖迴圈佇列來規避衝突,交換資料。
  • 資料包等需要大量重複使用的結構可以相互隔離,執行緒持有獨立的可用記憶體池.

效能分析

Seastar是開源的C++框架用於構建在現代硬體上構建高效能的伺服器應用。該框架基於DPDK,利用Seastar開發的應用可以執行在Linux 或 OSv 之上。

下面是seastar的介紹以及利用其開發的記憶體伺服器與其他伺服器的對比,可見dpdk效能相對傳統框架有一定優勢,且在網路密集型的場景下效果很好。

  • Seastar uses a shared-nothing model that shards all requests onto individual cores.
  • Seastar offers a choice of network stack, including conventional Linux networking for ease of development, DPDK for fast user-space networking on Linux, and native networking on OSv.
  • an advanced new model for concurrent applications that offers C++ programmers both high performance and the ability to create comprehensible, testable high-quality code.
  • a design for sharing information between CPU cores without time-consuming locking.

效能分析

擴充套件

熱備

DPDK在多執行緒管理上隔離性相當好,主核和從核通過管道進行命令互動,主核可以輕鬆的將業務下發給從核,因此可以很容易的利用這個特點做業務介面熱備擴充套件

底層轉發

如下圖所示,大部分程式互動時是在傳輸層及以上,但DPDK從二層切入,在構建叢集需要大規模轉發資料時可以使用三層轉發,這樣將使資料包轉發降低1層協議棧的開銷.


7層模型