DPDK Programmer’s Guide(5)Ring Library

相關Ring性能測試介紹鏈接如下:https://blog.csdn.net/NachtZ/article/details/72789820

官方文檔查看地址:
http://doc.dpdk.org/guides/prog_guide/ring_lib.html
PDF下載地址:
https://www.intel.com/content/www/us/en/embedded/technology/packet-processing/dpdk/dpdk-programmers-guide.html

本篇難度係數:
翻譯:★★★★☆
理解:★★★☆☆

5.環庫
該環允許管理隊列。rte_ring沒有無限大小的鏈表,而是具有以下屬性:

  • 先進先出
  • 最大大小是固定的,指針存儲在一個表中
  • 無鎖的實現
  • 多用戶或單用戶出隊列
  • 多生產者或單生產者入隊列
  • 批量出隊列——如果成功,則退出指定的對象數量;否則失敗
  • 批量入隊列——如果成功,則對指定的對象計數進行排隊;否則失敗
  • 突發出隊列——如果無法完成指定的計數,則刪除最大可用對象
  • 突發入隊列——如果無法完成指定的計數,則對最大可用對象排隊

這種數據結構相對於鏈表隊列的優點如下:

  • 快;只需要一個sizeof(void *)的比較和交換指令,而不是幾個雙比較和交換指令。
  • 比完整的無鎖隊列更簡單。

適用於批量入/出隊列操作。由於指針存儲在表中,多個對象的dequeue不會產生與鏈接隊列中一樣多的緩存丟失。此外,許多對象的批量脫隊列並不比簡單對象的脫隊列花費更多。

缺點:

  • 尺寸是固定的
  • 與鏈表隊列相比,擁有多個環需要更多的內存。空環至少包含N個指針。

一個簡單的環表示形式如下所示:消費者和生產者的頭和尾指針指向存儲在數據結構中的對象。

在這裏插入圖片描述
5.1在FreeBSD*中實現環的參考資料
以下代碼是在FreeBSD 8.0中添加的,並在一些網絡設備驅動程序中使用(至少在Intel驅動程序中):

  • 在FreeBSD bufring.h
  • 在FreeBSD bufring.c

5.2。Linux*中的無鎖環緩衝區

下面是描述Linux無鎖環緩衝區設計的鏈接。 Linux Lockless Ring Buffer Design http://lwn.net/Articles/340400/

5.3附加功能

5.3.1名字

一個環由一個唯一的名字來標識。不可能創建具有相同名稱的兩個環(如果嘗試這樣做,rte_ring_create()將返回NULL)。

5.4用例

Ring庫的用例包括:

  • DPDK中的應用程序之間的通信
  • 內存池分配器使用

5.5環緩衝器的解剖

本節解釋循環緩衝區的工作方式。環結構由兩對頭尾對組成;一個供生產者使用,一個供消費者使用。以下部分的圖將它們稱爲prod_head、prod_tail、cons_head和cons_tail。

每個圖表示一個簡化的環形狀態,即一個循環緩衝區。函數局部變量的內容在圖的頂部表示,環結構的內容在圖的底部表示。

5.5.1單一生產者入隊

本節解釋當生產者向環中添加對象時會發生什麼。在本例中,只修改了生產者head和tail (prod_head和prod_tail),並且只有一個生產者。

初始狀態是使prod_head和prod_tail指向相同的位置。

5.5.1.1入隊的第一步

首先,在局部變量中複製ring->prod_head和ring->cons_tail。prod_next局部變量指向表的下一個元素,或者在批量排隊時指向表後的幾個元素。如果環中沒有足夠的空間(通過檢查cons_tail可以檢測到),它將返回一個錯誤。

在這裏插入圖片描述
5.5.1.2入隊第二步

第二步是修改環結構中的ring->prod_head,使其指向與prod_next相同的位置。

一個指向添加對象的指針被複制到環中(obj4)

在這裏插入圖片描述
5.5.2單個消費者出列

本節解釋當使用者將對象從環中取出時會發生什麼。在本例中,只修改了使用者head和tail (cons_head和cons_tail),並且只有一個使用者。

初始狀態是cons_head和cons_tail指向同一個位置。

5.5.2.1出列的第一步

首先,在局部變量中複製ring->cons_head和ring->prod_tail。cons_next局部變量指向表的下一個元素,或者在批量脫隊列的情況下指向多個元素。如果環中沒有足夠的對象(通過檢查prod_tail來檢測),則返回一個錯誤。
在這裏插入圖片描述
5.5.2.2出列第二步

第二步是修改環結構中的ring->cons_head,使其指向與cons_next相同的位置。

指向dequeued對象(obj1)的指針被複制到用戶給出的指針中。
在這裏插入圖片描述
5.5.2.3出列最後一步

最後,將環結構中的ring->cons_tail修改爲指向與ring->cons_head相同的位置。退出隊列操作已經完成。
在這裏插入圖片描述
5.5.3多個生產者排隊

本節解釋當兩個生產者同時向環中添加一個對象時會發生什麼。在本例中,只修改了生產者head和tail (prod_head和prod_tail)。

初始狀態是使prod_head和prod_tail指向相同的位置。

5.5.3.1第一步是多個生產者排隊

在兩個核心上,ring->prod_head和ring->cons_tail都被複制到局部變量中。prod_next局部變量指向表的下一個元素,或者在批量排隊的情況下指向後幾個元素。

如果環中沒有足夠的空間(通過檢查cons_tail可以檢測到),它將返回一個錯誤。
在這裏插入圖片描述
5.5.3.2多個生產者進入第二步

第二步是修改環結構中的ring->prod_head,使其指向與prod_next相同的位置。這個操作是使用比較和交換(CAS)指令完成的,它原子性地執行以下操作:

  • 如果ring->prod_head與本地變量prod_head不同,則CAS操作失敗,代碼在第一步重新啓動。
  • 否則,將ring->prod_head設置爲local prod_next, CAS操作成功,處理繼續。

在圖中,操作在core 1上成功,步驟1在core 2上重新啓動。
在這裏插入圖片描述
5.5.3.3多個生產者進入第三步

CAS操作在core 2上重試成功。核心1更新環的一個元素(obj4),核心2更新另一個元素(obj5)。
在這裏插入圖片描述
5.5.3.4多個生產者排隊進入第四步

現在每個核心都想要更新ring->prod_tail。核心只能在ring->prod_tail等於prod_head局部變量時更新它。這隻適用於core 1。核心1上的操作已經完成。
在這裏插入圖片描述
5.5.3.5最後一步是多個生產者排隊

一旦core 1更新了ring->prod_tail, core 2也可以更新它。操作也在core 2上完成。
在這裏插入圖片描述
5.5.4模32位的索引

在前面的圖中,prod_head、prod_tail、cons_head和cons_tail索引由箭頭表示。在實際實現中,這些值不像假設的那樣介於0和size(ring)-1之間。索引的值在0到2^32 -1之間,當我們訪問指針表(環本身)時,將屏蔽它們的值。32位模數還意味着,如果結果超出32位數字範圍,索引上的操作(例如,加法/減法)將自動執行2^32的模數運算。

下面是兩個示例,它們有助於解釋索引如何在環中使用。

請注意
爲了簡化解釋,使用了模16位而不是模32位的運算。此外,這四個索引被定義爲無符號16位整數,而在更實際的情況下定義爲無符號32位整數。
在這裏插入圖片描述
請注意
爲了便於理解,我們在上面的示例中使用了模65536操作。在實際的執行案例中,這對於低效率來說是冗餘的,但是當結果溢出時就會自動執行。

代碼始終在生產者和消費者之間保持0和size(ring)-1之間的距離。由於這個屬性,我們可以在一個32位基中對兩個索引值進行減法:這就是索引溢出不是問題的原因。

在任何時候,條目和free_entries都在0和size(ring)-1之間,即使只有減法的第一項溢出:

uint32_t entries = (prod_tail - cons_head);
uint32_t free_entries = (mask + cons_tail -prod_head);

5.6 參考文獻

bufring.h in FreeBSD (version 8) https://svnweb.freebsd.org/base/release/8.0.0/sys/sys/buf_ring.h?revision=199625&view=markup
bufring.c in FreeBSD (version 8) https://svnweb.freebsd.org/base/release/8.0.0/sys/kern/subr_bufring.c?revision=199625&view=markup
Linux Lockless Ring Buffer Design https://lwn.net/Articles/340400/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章