一区二区三区三上|欧美在线视频五区|国产午夜无码在线观看视频|亚洲国产裸体网站|无码成年人影视|亚洲AV亚洲AV|成人开心激情五月|欧美性爱内射视频|超碰人人干人人上|一区二区无码三区亚洲人区久久精品

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

利用eBPF程序繞過內(nèi)核以加速存儲(chǔ)訪問

SSDFans ? 來源: SSDFans ? 2025-03-01 16:09 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

【摘要】

隨著微秒級(jí)NVMe存儲(chǔ)的蓬勃發(fā)展,Linux內(nèi)核存儲(chǔ)棧的開銷幾乎是存儲(chǔ)訪問時(shí)間的兩倍,已經(jīng)成為性能瓶頸。作者提出XRP——一個(gè)允許應(yīng)用通過在NVMe驅(qū)動(dòng)的eBPF hook運(yùn)行用戶定義的存儲(chǔ)函數(shù)(如索引查找)的框架,它可以安全地繞過內(nèi)核大部分存儲(chǔ)棧。為了保持文件系統(tǒng)的語義,當(dāng)用戶注冊(cè)的eBPF函數(shù)被調(diào)用時(shí),XRP向它的NVMe驅(qū)動(dòng)hook傳遞了一小部分內(nèi)核的狀態(tài)。作者測試了兩個(gè)KV存儲(chǔ)——BPF-KV,一個(gè)簡單的B+樹KV存儲(chǔ),和WiredTiger,一個(gè)流行的LSM樹的存儲(chǔ)引擎,并證明了它們從XRP中可以獲得極大的帶寬和延遲收益。

【背景和動(dòng)機(jī)】

1、動(dòng)機(jī)實(shí)驗(yàn)配置

CPU 6-core i5-8500 3GHz
DRAM 16GB
Ubuntu 20.04
Linux 5.8.0
Processor C-states no
turbo boost no
governor maximum performance
KPTI no

2、軟件開銷成為存儲(chǔ)性能瓶頸

如下圖,像3D-Xpoint這樣的新介質(zhì)和低延遲NAND可以讓新的NVMe設(shè)備表現(xiàn)出個(gè)位數(shù)微秒級(jí)延遲和1e6(十萬)級(jí)別的IOPS。從訪問一代快速NVMe設(shè)備開始,軟件開銷占比就達(dá)到15%,到現(xiàn)在訪問最新的NVMe設(shè)備時(shí),軟件開銷占比達(dá)到50%。

cd701a80-f30b-11ef-9310-92fbcf53809c.png

作者進(jìn)一步對(duì)內(nèi)核軟件開銷進(jìn)行分解,其中使用了O_DIRECT繞過了page cache。如下表,開銷最大的層是文件系統(tǒng)層(ext4),其次是塊設(shè)備層(bio)和kernel crossing(上下文切換),整體軟件開銷占比達(dá)到48.6%。

cdb77f10-f30b-11ef-9310-92fbcf53809c.png

那么,為什么不直接繞過內(nèi)核呢?

這種辦法并非靈丹妙藥:這意味著把存儲(chǔ)設(shè)備的訪問完全暴露給應(yīng)用,同時(shí)應(yīng)用必須實(shí)現(xiàn)自己的文件系統(tǒng)——這意味著沒有保證數(shù)據(jù)孤立的機(jī)制,不同應(yīng)用無法共享同一個(gè)設(shè)備的空間。此外,用戶態(tài)應(yīng)用也無法接受中斷——這意味著當(dāng)I/O并非性能瓶頸時(shí),CPU資源會(huì)被浪費(fèi),利用率低下。而且當(dāng)多個(gè)polling的線程共享一個(gè)CPU時(shí),CPU競爭+缺乏同步機(jī)制會(huì)導(dǎo)致所有polling的線程的尾端延遲巨大,帶寬巨低。

3、BPF簡介

BPF(Berkeley Packet Filter)是一個(gè)允許用戶把一個(gè)簡單的函數(shù)注入內(nèi)核執(zhí)行的接口。Linux中實(shí)現(xiàn)的BPF框架叫eBPF。函數(shù)在注入前,需要經(jīng)過幾秒鐘的檢驗(yàn)(verification),隨后這個(gè)eBPF函數(shù)就可以被正常調(diào)用了。

BPF的潛在優(yōu)勢

當(dāng)一個(gè)請(qǐng)求需要進(jìn)行多次I/O查找(resubmission)時(shí),BPF可以避免內(nèi)核態(tài)和用戶態(tài)之間的數(shù)據(jù)遷移。例如,查找B樹的索引時(shí),需要先從根節(jié)點(diǎn)查起,直到找到葉子節(jié)點(diǎn)。而這一過程若使用多個(gè)系統(tǒng)調(diào)用,則每一次系統(tǒng)調(diào)用都要遍歷整個(gè)內(nèi)核存儲(chǔ)棧。而若使用BPF函數(shù),可以把剛剛的查找函數(shù)注入NVMe驅(qū)動(dòng)層,這樣只有第一次查找需要遍歷整個(gè)內(nèi)核存儲(chǔ)棧,之后的查詢結(jié)果只需要返回驅(qū)動(dòng)層即可。二者的對(duì)比如下圖:

cde8960e-f30b-11ef-9310-92fbcf53809c.png

B-Tree Lookup from User Space

ce089b02-f30b-11ef-9310-92fbcf53809c.png

B-Tree Lookup With an In-Kernel Function

其他數(shù)據(jù)結(jié)構(gòu)(LSM樹)、其他操作(范圍查詢、迭代、計(jì)算統(tǒng)計(jì)數(shù)據(jù))也可以從這種機(jī)制中獲益。這些操作的特點(diǎn)是有大量“中間“(或者說”備用“)的I/O操作,而對(duì)用戶而言只需要最后的單個(gè)結(jié)果或者I/O返回的一小部分對(duì)象。

除了在NVMe驅(qū)動(dòng)層,BPF函數(shù)也可以放在其他層,如系統(tǒng)調(diào)用。下圖是普通的系統(tǒng)調(diào)用和兩個(gè)使用BPF函數(shù)分發(fā)(或者重發(fā))I/O操作時(shí)的不同路徑。

ce2a620a-f30b-11ef-9310-92fbcf53809c.png

作者在21年HotOS上的文章中比較了BPF函數(shù)在這兩個(gè)不同地方時(shí)獲得的性能收益,如下表,比較的基準(zhǔn)是read()系統(tǒng)調(diào)用。

ce5e6f46-f30b-11ef-9310-92fbcf53809c.png

當(dāng)CPU達(dá)到飽和態(tài)后,在NVMe驅(qū)動(dòng)層重發(fā)I/O請(qǐng)求帶來的提升最終轉(zhuǎn)化為1.8x-2.5x的帶寬增大。(具體增大多少取決于工作集中線程的數(shù)目)

擴(kuò)展:線程的數(shù)目如何影響帶寬的提升呢?

當(dāng)CPU未飽和時(shí),線程越多,提升越小。而CPU飽和后,提升更加明顯。下圖6個(gè)線程后CPU飽和。

ce7cb316-f30b-11ef-9310-92fbcf53809c.png

BPF函數(shù)越靠近底層,性能提升越大。所以,XRP的重發(fā)hook放在NVMe驅(qū)動(dòng)層。

前文提到的都是同步I/O,那么異步I/O的表現(xiàn)如何呢?

Linux內(nèi)核新提出的io_uring可以實(shí)現(xiàn)批量下發(fā)異步I/O,一定程度上攤銷了用戶態(tài)陷入內(nèi)核態(tài)的開銷。然而每一個(gè)I/O請(qǐng)求依然要穿過整個(gè)內(nèi)核存儲(chǔ)棧。事實(shí)上,io_uring和BPF I/O重發(fā)可以相互補(bǔ)充:

io_uring可以高效地批量下發(fā)I/O請(qǐng)求,而每個(gè)I/O請(qǐng)求觸發(fā)不同的I/O鏈,eBPF函數(shù)處理這樣的I/O鏈。下圖是和只使用io_uring相比,使用io_uring+BPF hook時(shí)的帶寬提升。

ce9a9b88-f30b-11ef-9310-92fbcf53809c.png

注:I/O Chain Length指初始I/O和重發(fā)的I/O總數(shù)(如,B-樹的深度);batch size指在每個(gè)io_uring調(diào)用中打包的系統(tǒng)調(diào)用數(shù)。

【設(shè)計(jì)挑戰(zhàn)與原則】

1、挑戰(zhàn)1:地址轉(zhuǎn)化和安全

以索引查詢?yōu)槔?,XRP會(huì)處理一個(gè)讀I/O請(qǐng)求,然后調(diào)用BPF函數(shù)解析下一個(gè)塊的偏移量。然而,對(duì)NVMe層而言,這個(gè)偏移量沒有任何意義——因?yàn)樗狈ξ募獢?shù)據(jù),所以不知道這個(gè)偏移量對(duì)應(yīng)的塊號(hào)。開發(fā)者確實(shí)可以把塊的物理地址也編進(jìn)去,但是這樣一來,BPF函數(shù)可以訪問設(shè)備上的任何塊——而有些塊用戶沒有訪問權(quán)限。

2、挑戰(zhàn)2:并發(fā)訪問

在NVMe驅(qū)動(dòng)層的XRP無法看到page cache層的寫。若有的寫操作修改了數(shù)據(jù)結(jié)構(gòu)的layout(如指針指向的下一塊),而與此同時(shí),若有一個(gè)讀請(qǐng)求在訪問這一部分,XRP可能會(huì)訪問到錯(cuò)誤的數(shù)據(jù)。這些可以通過鎖解決,但是在NVMe驅(qū)動(dòng)層訪問鎖開銷過高。

觀察:大多數(shù)盤上的數(shù)據(jù)結(jié)構(gòu)是穩(wěn)定的。

許多存儲(chǔ)引擎(B樹、LSM樹)的文件相對(duì)穩(wěn)定。有些數(shù)據(jù)結(jié)構(gòu)不會(huì)就地更新盤上數(shù)據(jù),如當(dāng)LSM樹寫它的索引文件(SSTable)時(shí),在它們被刪除前,這些文件都是不可修改的。這樣同步就不用那么費(fèi)勁了。有些B樹的實(shí)現(xiàn)雖然實(shí)現(xiàn)了就地更新,他們的文件的extent基本上穩(wěn)定。作者使用YCSB在MariaDB上運(yùn)行TokuDB 24小時(shí),使用B樹,發(fā)現(xiàn)文件的extent平均每159秒改變一次,所以可以在NVMe驅(qū)動(dòng)層緩存文件系統(tǒng)元數(shù)據(jù)。

3、設(shè)計(jì)原則

一次只處理一個(gè)文件的鏈?zhǔn)街匕l(fā)

不支持遍歷需要鎖的數(shù)據(jù)結(jié)構(gòu)

用戶定義的cache(不支持page cache)

慢路徑回調(diào):若XRP的遍歷失敗了,應(yīng)用要試著重新試一次,或者使用系統(tǒng)調(diào)用來分發(fā)I/O請(qǐng)求。

【XRP的設(shè)計(jì)與實(shí)現(xiàn)】

本論文中,XRP基于Linux的eBPF和ext4文件系統(tǒng)。

1、修改NVMe驅(qū)動(dòng)中的中斷控制器:實(shí)現(xiàn)重發(fā)I/O

ceba86fa-f30b-11ef-9310-92fbcf53809c.png

當(dāng)NVMe請(qǐng)求完成時(shí),設(shè)備向主機(jī)發(fā)送中斷,進(jìn)入中斷處理器,此時(shí)XRP通過bio調(diào)用BPF函數(shù),訪問元數(shù)據(jù)摘要,進(jìn)行地址轉(zhuǎn)化,最后準(zhǔn)備下一個(gè)NVMe請(qǐng)求,并重發(fā),把請(qǐng)求放入NVMe提交隊(duì)列(SQ)中。

具體重發(fā)的次數(shù)是由NVMe請(qǐng)求注冊(cè)的特定BPF函數(shù)決定的。例如查找一個(gè)數(shù)狀的結(jié)構(gòu),那么遇到中間節(jié)點(diǎn)時(shí)BPF函數(shù)會(huì)重發(fā),直到遇到葉子節(jié)點(diǎn)時(shí)終止。目前的XRP實(shí)現(xiàn)中,對(duì)重發(fā)次數(shù)沒有硬限制。如果需要實(shí)現(xiàn),可以在I/O請(qǐng)求描述器中增加一個(gè)重發(fā)計(jì)數(shù)器。這個(gè)計(jì)數(shù)器既不能被用戶訪問,也不能被XRP的BPF函數(shù)訪問,所以即使多個(gè)BPF函數(shù)執(zhí)行重發(fā),計(jì)數(shù)器也不會(huì)超量。

BPF函數(shù)的上下文以請(qǐng)求為單位的,而元數(shù)據(jù)摘要?jiǎng)t所有核和中斷控制器共享,它的同步訪問由RCU(read-copy-update)控制。

1.1 BPF hook

BPF的每個(gè)類型對(duì)應(yīng)對(duì)應(yīng)一種程序,XRP引入了新的BPF類型——BPF_PROG_TYPE_XRP。它的簽名如下:

cee0b78a-f30b-11ef-9310-92fbcf53809c.png

任何符合這個(gè)簽名的BPF程序都可以被這個(gè)hook調(diào)用。下一部分會(huì)展示一個(gè)具體的符合該簽名的BPF程序。

BPF_PROG_TYPE_XRP程序的上下文有五個(gè)域:

data 緩存從磁盤讀上來的數(shù)據(jù)(如一個(gè)B-樹頁)
done 標(biāo)識(shí)重發(fā)是否完成的布爾變量
next_adr 下一個(gè)塊的邏輯地址的數(shù)組
size 下一個(gè)塊的大小的數(shù)組(0表示沒有I/O)
scratch 一個(gè)緩沖區(qū),用于用戶向BPF函數(shù)傳遞參數(shù)、或者BPF函數(shù)存儲(chǔ)I/O重發(fā)過程中的中間數(shù)據(jù)、或者向用戶返回?cái)?shù)據(jù)。 這個(gè)buffer的大小為4KB,若需要存儲(chǔ)更多的中間數(shù)據(jù),BPF函數(shù)可以使用BPF map。

每個(gè)BPF上下文都為一個(gè)NVMe 請(qǐng)求所私有,因此使用BPF上下文時(shí)不需要鎖。而且,由用戶提供一個(gè)scratch緩沖區(qū),而非使用BPF map,避免了進(jìn)程和函數(shù)不得不調(diào)用bpf_map_lookup_elem來訪問scratch buffer。

1.2 BPF Verifier

BPF的寄存器分為scalar和pointer兩種,其中pointer有不同的類型,如PTR-TO-MEM,指向一塊固定大小的內(nèi)存區(qū)域。在XRP上下文中,data和scratch的類型是PTR TO MEM,而剩下的是scalar。BPF Verifier會(huì)根據(jù)數(shù)據(jù)類型檢測寄存器的數(shù)據(jù),防止訪問區(qū)域外的數(shù)據(jù)。

此外,作者還實(shí)現(xiàn)了XRP類型對(duì)應(yīng)的is_valid_acces()函數(shù),可以檢測對(duì)上下文的訪問是否正確以及返回上下文域的值的類型。1.3 元數(shù)據(jù)摘要

中斷控制器并沒有文件以及邏輯地址到物理地址的轉(zhuǎn)換的概念。作者在此實(shí)現(xiàn)了“元數(shù)據(jù)摘要“,即一個(gè)中斷控制器與文件系統(tǒng)之間的接口,這樣,文件系統(tǒng)就可以和中斷控制器共享地址映射了從而支持基于eBPF的盤上重發(fā)。

元數(shù)據(jù)摘要包括兩個(gè)部分,如下圖:

cf036140-f30b-11ef-9310-92fbcf53809c.png

更新函數(shù):當(dāng)?shù)刂酚成浔桓聲r(shí),由文件系統(tǒng)調(diào)用

查詢函數(shù):由中斷控制器調(diào)用,返回給定偏移和長度的地址映射。同時(shí)也實(shí)現(xiàn)了訪問控制。

這兩類函數(shù)的實(shí)現(xiàn)方式多種多樣,例如在本文中,作者實(shí)現(xiàn)了ext4的extent狀態(tài)樹的緩存,因此元數(shù)據(jù)摘要中包括了這個(gè)狀態(tài)樹的版本號(hào)。同時(shí)使用RCU進(jìn)行并發(fā)控制——查詢函數(shù)可以很快而且無鎖。當(dāng)extent更新的同時(shí)還有查找在進(jìn)行,extent的版本號(hào)會(huì)更新。查找到的數(shù)據(jù)在返回BPF函數(shù)前會(huì)進(jìn)行二次查詢,若發(fā)現(xiàn)版本號(hào)不一致,則放棄本次操作。

不過,一般應(yīng)用不會(huì)允許查找與更新同一塊區(qū)域同時(shí)進(jìn)行,所以若發(fā)生這種情況要么是應(yīng)用代碼出錯(cuò),要么是惡意應(yīng)用。

當(dāng)然,也可以不實(shí)現(xiàn)extent狀態(tài)樹緩存——這樣update函數(shù)什么也不用做。但是,此時(shí)查詢需要先獲得自旋鎖,會(huì)變慢很多。

目前的XRP只支持ext4文件系統(tǒng)。但如f2fs等的元數(shù)據(jù)摘要也可以輕易實(shí)現(xiàn)。

1.4 重發(fā)NVMe請(qǐng)求

為了避免調(diào)用kmalloc的開銷,XRP重用舊的NVMe請(qǐng)求結(jié)構(gòu),在重發(fā)時(shí)僅僅修改physical sector和block address。

不過,重發(fā)的I/O請(qǐng)求只能抓取和舊I/O請(qǐng)求一樣多的物理段。如果BPF函數(shù)返回了多個(gè)指針(next_addr),而NVMe請(qǐng)求不支持這多物理段的抓取,那么XRP會(huì)拋棄這個(gè)請(qǐng)求。

2、同步控制

BPF目前僅支持自旋鎖,而且同時(shí)只能獲得一個(gè)鎖,且在結(jié)束前必須釋放這個(gè)鎖。用戶應(yīng)用也無法直接訪問這個(gè)鎖,必須經(jīng)過系統(tǒng)調(diào)用bpf()來讀寫鎖保護(hù)的區(qū)域。因此,需要在多個(gè)讀和寫之間同步的復(fù)雜的修改無法在用戶態(tài)實(shí)現(xiàn)。

用戶也可以使用BPF的原子操作自己實(shí)現(xiàn)自旋鎖,這樣用戶和BPF程序都可以直接獲得這個(gè)鎖。但是,BPF函數(shù)不能一直等待鎖被釋放——無法通過verifier。

另一種實(shí)現(xiàn)同步的方式是用RCU,XRP BPF程序在NVMe中斷控制器中實(shí)現(xiàn),這本身就是不可搶占的,所以它們已經(jīng)在RCU的讀關(guān)鍵區(qū)了。

3、和Linux調(diào)度器的交互

進(jìn)程調(diào)度器:

作者觀察到,超低延遲存儲(chǔ)和Linux的CFS調(diào)度器存在沖突,例如當(dāng)I/O密集進(jìn)程和計(jì)算密集進(jìn)程共享一個(gè)核時(shí),I/O密集進(jìn)程產(chǎn)生的中斷可能打斷計(jì)算密集進(jìn)程的運(yùn)行,導(dǎo)致計(jì)算密集進(jìn)程被餓死。最壞的情況下,計(jì)算密集進(jìn)程只能獲得34%的CPU時(shí)間。而當(dāng)使用較慢的設(shè)備時(shí),則不存在這個(gè)問題。XRP進(jìn)一步加劇了這個(gè)問題——產(chǎn)生多個(gè)鏈?zhǔn)街袛唷W髡甙堰@一問題留給未來的工作解決。

I/O調(diào)度器:

XRP bypass了Linux的I/O調(diào)度器。不過,noop調(diào)度器目前已經(jīng)是NVMe設(shè)備的默認(rèn)I/O調(diào)度器。而若需要保證公平,它們?cè)谟布?duì)列中有仲裁機(jī)制。

【案例分析】

如下圖,應(yīng)用可以通過調(diào)用這些函數(shù)來使用XRP。

cf2688e6-f30b-11ef-9310-92fbcf53809c.png

bpf_prog_load:一個(gè)已有的函數(shù)。用于加載BPF_PROG_TYPE_XRP類型的BPF函數(shù)到驅(qū)動(dòng)中。

read_xrp:用戶可以用它把一個(gè)特定的BPF函數(shù)應(yīng)用到一個(gè)具體的請(qǐng)求上。

在這一部分中將介紹兩個(gè)案例,以表明應(yīng)用應(yīng)該做什么樣的修改來使用XRP。

1、BPF-KV

BPF-KV是作者構(gòu)造的一個(gè)簡單的KV存儲(chǔ),它用來存儲(chǔ)小對(duì)象并且提供優(yōu)秀的讀性能。其中,BPF-KV的索引結(jié)構(gòu)是B+樹,而對(duì)象存儲(chǔ)在一個(gè)無序的日志(log)中。簡單起見,BPF-KV使用固定大小的key(8B)和value(64B)。索引和日志一起被放在一個(gè)大文件里。索引節(jié)點(diǎn)(index node)使用簡單的頁結(jié)構(gòu)(每個(gè)index node就是一個(gè)頁):頭+key+value;葉子節(jié)點(diǎn)包含一個(gè)指向下一個(gè)葉子節(jié)點(diǎn)的文件偏移量。對(duì)象的大小固定(64B),所以對(duì)象可以在log中被就地更新,新插入的項(xiàng)會(huì)被附加在log后面,它們的索引一開始存儲(chǔ)在內(nèi)存的哈希表中,而當(dāng)哈希表滿后,BPF-KV會(huì)將其和盤上的B+樹文件混合。

cf53360c-f30b-11ef-9310-92fbcf53809c.png

1.1 緩存

為了減少查找時(shí)的I/O數(shù)量,BPF-KV把頭k層B+樹索引緩存到內(nèi)存中。當(dāng)object足夠多時(shí),不可能在內(nèi)存中緩存全部的索引。若BPF-KV用于存儲(chǔ)10 billiion 64B對(duì)象,index node的大小是512B(和Optane SSD的訪問粒度匹配),因此,每個(gè)中間節(jié)點(diǎn)可以存儲(chǔ)31的指向孩子的指針。因此,10 billion的object需要8層index。

注:

設(shè)B+樹有n層,則葉子節(jié)點(diǎn)層有

cf76f8bc-f30b-11ef-9310-92fbcf53809c.png 個(gè)指向object的指針,而object有 cf9b8146-f30b-11ef-9310-92fbcf53809c.png 個(gè),解得n=8.

把6層放到DRAM里面已經(jīng)很貴了(14GB),更別說7層(436GB)或更多了。所以,為了支持更多的key,在每次查詢中,BPF-KV會(huì)需要至少3~4個(gè)I/O(包括最終的I/O)。

除此之外,BPF-KV還有一個(gè)LRU對(duì)象緩存,里面存儲(chǔ)了最近最常使用的key-value對(duì)。

當(dāng)查詢一個(gè)object時(shí),先查找LRU對(duì)象緩存,若沒找到,則在hash表中查找index,若沒找到,則在前k層緩存的index中查找。若遇到了不在內(nèi)存中index,則在磁盤中查詢。

1.2 BPF函數(shù)

下圖是在BPF-KV中使用的BPF函數(shù),此處忽略了最終步(查詢log)的部分。struct node地能夠以了B+樹的index node的layout(大小為512B)。BPF函數(shù)bpfkv_bpf首先從scratch中提取目標(biāo)key,然后搜索當(dāng)前節(jié)點(diǎn)找到要讀的下一個(gè)節(jié)點(diǎn)。

cfb6c2e4-f30b-11ef-9310-92fbcf53809c.png

1.3 接口修改

作者把read()系統(tǒng)調(diào)用換成了read_xrp(),在調(diào)用read_xrp之前,BPF-KV會(huì)先分配scratch空間,計(jì)算開始查詢的offset。

1.4 范圍查詢

BPF-KV支持返回一些object的范圍查詢。在scratch中,存儲(chǔ)了BPF函數(shù)的狀態(tài)(查詢的范圍)和已接受的object。scratch最多頓出32個(gè)72 byte的key-value對(duì)。第一次調(diào)用時(shí),函數(shù)找到具有開始key的葉子節(jié)點(diǎn),隨后把葉子節(jié)點(diǎn)存在scratch中,隨后在緩存中葉子節(jié)點(diǎn)中查找,當(dāng)葉子節(jié)點(diǎn)讀完后,函數(shù)提交下一個(gè)葉子節(jié)點(diǎn)的查找,依次類推。當(dāng)找到范圍末端/查完整個(gè)index集/scratch空間滿了之后,函數(shù)返回。

1.5 統(tǒng)計(jì)計(jì)算(Aggregation)

BPF-KV支持如SUM、MAX和MIN的統(tǒng)計(jì)計(jì)算。這個(gè)計(jì)算是基于返回查詢的,在范圍查詢的上層設(shè)置了一個(gè)bit標(biāo)識(shí)是否要統(tǒng)計(jì)計(jì)算。計(jì)算后的值存在scratch中。

2 WiredTiger

WiredTiger是MongoDB默認(rèn)的KV存儲(chǔ)。其使用LSM樹組織數(shù)據(jù):數(shù)據(jù)被分到不同的層,每一層都有一個(gè)單獨(dú)的文件,每個(gè)文件中使用B-樹索引(頁大小為512B),而KV對(duì)存在樹的葉子節(jié)點(diǎn)中。這些文件是只讀的,更新和插入都會(huì)先寫入內(nèi)存的buffer中,buffer滿之后,數(shù)據(jù)會(huì)寫入一個(gè)新的文件。作者修改了大約500行代碼,包括buffer分配、擴(kuò)展函數(shù)簽名和封裝XRP系統(tǒng)調(diào)用。XRP加速磁盤讀,不會(huì)影響更新和插入。

2.1 BPF函數(shù)

WiredTiger安裝了和BPF-KV中類似的BPF函數(shù),不同點(diǎn)在于查找下一個(gè)node的指針時(shí),WiredTiger的BPF函數(shù)把原有的for循環(huán)換成解析WiredTiger的B樹節(jié)點(diǎn)頁的端口

2.2 緩存

WiredTiger使用LRU鏈表把一部分內(nèi)部節(jié)點(diǎn)和葉子節(jié)點(diǎn)緩存在cache中。當(dāng)查詢一個(gè)新的KV對(duì)時(shí),WiredTiger把整個(gè)查詢路徑(包括葉子節(jié)點(diǎn))緩存下來。為了遵從WiredTiger的語義,上文中提到的BPF函數(shù)額外返回了所有查詢的節(jié)點(diǎn),這樣WiredTiger就可以緩存它們了。這些查詢到的節(jié)點(diǎn)會(huì)放在scratch里,當(dāng)scratch滿了后,BPF函數(shù)直接返回。WiredTiger把這些節(jié)點(diǎn)加入cache后,它再次調(diào)用read_xrp,繼續(xù)查詢。

scratch的大小是4KB,所以它一共可以存6個(gè)查到的512B大小的節(jié)點(diǎn)(預(yù)留了1KB存必要的元數(shù)據(jù))。

2.3 接口修改

作者把普通的read系統(tǒng)調(diào)用全部換成了read_xrp。WiredTiger的緩存策略保證了未緩存的節(jié)點(diǎn)沒有已緩存的兒子,所以可以放心調(diào)用read_xrp返回剩余的讀路徑上的節(jié)點(diǎn)。如果read_xrp失敗了,WiredTiger會(huì)調(diào)用舊的查詢辦法。data和scratch會(huì)事先分好以避免分配和釋放緩沖區(qū)的開銷。WiredTiger同時(shí)只處理一個(gè)請(qǐng)求,以避免并發(fā)。

【評(píng)估測試】

CPU 6-core i5-8500 3GHz server
DRAM 16GB
Ubuntu 20.04
Linux 5.12.0
SSD Intel Optane 5800X
page cache no(繞過了page cache)
hyper-threading no
processor C-states no(不支持進(jìn)入低功耗模式)
turbo boost(睿頻加速) no(以保證CPU頻率穩(wěn)定)
governor maximum performance(讓CPU性能最高)
KPTI yes(保證BPF程序無法竊取內(nèi)核信息)
WiredTiger 4.4.0(目前最新版本11.1.0)

Baselines:

(a) XRP

(b) SPDK

(c) read()

(d) io_uring syscalls

1、在存儲(chǔ)場景下使用BPF的開銷有多大?

1.1 延遲

在本實(shí)驗(yàn)中,作者執(zhí)行了

d077eb9a-f30b-11ef-9310-92fbcf53809c.png 次隨機(jī)讀操作。為了關(guān)注查詢盤上數(shù)據(jù)的開銷,作者關(guān)閉了內(nèi)存的緩存(緩存對(duì)象和緩存index)。平均延遲的對(duì)比如下表,最左一列的I/O數(shù)不包括獲得最終要的數(shù)據(jù)的最后一次I/O。

d090d664-f30b-11ef-9310-92fbcf53809c.png

1)因?yàn)閄RP省去了大部分軟件棧的開銷,相比read(),XRP的性能更好。而且,可以看到每增加一次index I/O,XRP的延遲增加3.5~3.9μs——約等于設(shè)備的延遲,側(cè)面證明XRP幾乎實(shí)現(xiàn)了重發(fā)請(qǐng)求的最優(yōu)性能。

注意,重發(fā)request是同步的,所以io_uring和read的表現(xiàn)差不多。

2)SPDK的性能比XRP要好,因?yàn)樵诘谝淮蜪/O時(shí),XRP依然要穿過整個(gè)I/O棧。但是XRP不需要使用polling,進(jìn)程可以繼續(xù)利用CPU完成其他事情。下圖是99和99.9的尾端延遲隨著線程數(shù)的增加的變化。

d0b4a88c-f30b-11ef-9310-92fbcf53809c.png

當(dāng)線程數(shù)超過9之后,SPDK的99.99尾端延遲劇烈增加,因?yàn)樗鼈兊木€程使用輪詢,無法高效共享CPU。作者還測試了請(qǐng)求延遲大于等于1ms的占比,如下圖:

d0cf6640-f30b-11ef-9310-92fbcf53809c.png

當(dāng)線程數(shù)為7時(shí),SPDK有0.03%的請(qǐng)求延遲大于1ms,而當(dāng)線程數(shù)達(dá)到24時(shí),這個(gè)比例達(dá)到0.28%。而其他幾個(gè)機(jī)制,這個(gè)比例一直低于0.01%。

1.2 帶寬(注意單位是KOPs)

如下圖,當(dāng)index深度增加時(shí),XRP的帶寬提升和標(biāo)準(zhǔn)的系統(tǒng)調(diào)用相比更高了。

d0ed6cd0-f30b-11ef-9310-92fbcf53809c.png

下圖是當(dāng)index深度分別為3和6時(shí),隨著線程數(shù)提升的帶寬變化??梢姰?dāng)I/O和XRP函數(shù)被擴(kuò)展到多個(gè)核時(shí),XRP的帶寬提升相比標(biāo)準(zhǔn)系統(tǒng)調(diào)用并沒有下降。此外,當(dāng)線程數(shù)超過9后,XRP的帶寬和SPDK相等甚至更高。

d10bdf30-f30b-11ef-9310-92fbcf53809c.png

2、XRP擴(kuò)展到多線程場景下表現(xiàn)如何?

現(xiàn)在的存儲(chǔ)應(yīng)用常常使用大量線程訪問存儲(chǔ)設(shè)備,因此XRP也需要在多線程下表現(xiàn)良好。在本實(shí)驗(yàn)中,作者進(jìn)行了一次open loop實(shí)驗(yàn),負(fù)載量和Intel設(shè)備的最大帶寬一致(5M IOPS),如下圖,index深度為6時(shí),XRP(內(nèi)部是io_uring)和SPDK在BPF-KV中的帶寬隨線程數(shù)的變化。

d13667be-f30b-11ef-9310-92fbcf53809c.png

當(dāng)使用6個(gè)線程時(shí),SPDK和XRP都可以達(dá)到硬件允許的最大帶寬。而線程數(shù)增多后,SPDK的帶寬開始下滑。因?yàn)镾PDK的輪詢線程必須等到Linux CFS調(diào)度(6ms)后才能被迫放棄CPU,而XRP的空閑線程可以主動(dòng)放棄CPU,讓CPU做有意義的工作。

下圖是12個(gè)線程時(shí),隨著負(fù)載的變化,帶寬和延遲之間的關(guān)系變化。可見SPDK中平均延遲和尾端延遲都比XRP增長更快。

d153ed7a-f30b-11ef-9310-92fbcf53809c.png

3、XRP在范圍查詢的場景下表現(xiàn)如何?

每次范圍查詢時(shí),都先執(zhí)行一次index檢索找到第一個(gè)對(duì)象,之后遍歷葉子節(jié)點(diǎn)找到后面的對(duì)象。樹的深度為6,盡管XRP一次只能檢索32個(gè)對(duì)象,但由下圖可見和read相比,XRP提高的延遲很穩(wěn)定。

d17227ae-f30b-11ef-9310-92fbcf53809c.png

4、XRP可以加速真實(shí)世界的KV存儲(chǔ)嗎?

作者使用YCSB產(chǎn)生負(fù)載,在WiredTiger中評(píng)估了XRP的性能。作者使用了YCSB A、YCSB B、YCSB C、YCSB D、YCSB E、YCSB F六種不同的負(fù)載,每種負(fù)載運(yùn)行的時(shí)間接近。A、B、C、F中有10M個(gè)操作,D中有50M個(gè),E中有3M個(gè)。

baseline:

pread()

read_xrp()

configure:

key-value中key和value都16B,總大小為46GB。KV對(duì)一共有10^9對(duì)。當(dāng)cahce塊滿時(shí),WiredTiger會(huì)把頁剔除。使用2個(gè)剔除線程。

4.1 帶寬

d1a30842-f30b-11ef-9310-92fbcf53809c.png

大部分工作集中,XRP有穩(wěn)定的帶寬提升,其中最大提升1.25倍。緩存越大,XRP的提升越小。因?yàn)閃iredTiger僅花費(fèi)63%的時(shí)間在I/O操作上,所以相比BPF-KV,XRP對(duì)WiredTiger的提升更小。此外,在YCSB D和YCSB E中,XRP的提升不明顯。YCSB D中,最新插入的對(duì)象總是最常被訪問,所以大部分訪問都在cache中完成了。而YCSB E的大多數(shù)操作是遍歷和插入,在遍歷場景下,XRP只能在獲取遍歷的初始KV對(duì)時(shí)有收益,因?yàn)槭O碌腒V對(duì)一般在同一個(gè)葉子節(jié)點(diǎn)里或者只需要一次額外的I/O就可以獲得下一個(gè)葉子節(jié)點(diǎn)。

作者進(jìn)一步探究了負(fù)載的偏移對(duì)XRP性能的影響。如下圖是調(diào)整TCSB C中Zipfian分布的不同常數(shù)和均勻分布的性能提升對(duì)比,可見負(fù)載偏移越大,XRP的提升越小(注,正常這個(gè)常數(shù)>0.99后說明負(fù)載偏移程度非常大了)。

d1e4903c-f30b-11ef-9310-92fbcf53809c.png

4.2 尾端延遲

作者讓每個(gè)YCSB ABCDF的每個(gè)線程以20kop/s的速率下發(fā)負(fù)載,而YCSB E以5kop/s的速率下發(fā)負(fù)載。如下圖,XRP最多可以減少40%的尾端讀延遲(YCSB E是scan延遲)。和帶寬類似,隨著cache size大小增大,XRP的尾端延遲減少下降了。

d20639bc-f30b-11ef-9310-92fbcf53809c.png

致謝

感謝本次論文解讀者,來自華東師范大學(xué)的碩士生俞丁翠,主要研究方向?yàn)楦咝阅艽鎯?chǔ)優(yōu)化。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 內(nèi)核
    +關(guān)注

    關(guān)注

    3

    文章

    1416

    瀏覽量

    41429
  • 存儲(chǔ)
    +關(guān)注

    關(guān)注

    13

    文章

    4532

    瀏覽量

    87455
  • 程序
    +關(guān)注

    關(guān)注

    117

    文章

    3826

    瀏覽量

    82979

原文標(biāo)題:談?wù)劺胑BPF程序繞過內(nèi)核以加速存儲(chǔ)訪問

文章出處:【微信號(hào):SSDFans,微信公眾號(hào):SSDFans】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    解構(gòu)內(nèi)核源碼eBPF樣例編譯過程

    了解和掌握純c語言的ebpf編譯和使用,有助于我們加深對(duì)于eBPF技術(shù)原理的進(jìn)一步掌握,也有助于開發(fā)符合自己業(yè)務(wù)需求的高性能的ebpf程序。
    的頭像 發(fā)表于 04-17 14:05 ?1957次閱讀

    關(guān)于 eBPF 安全可觀測性,你需要知道的那些事兒

    、TLS 和 TCP 等協(xié)議的網(wǎng)絡(luò)活動(dòng),以及系統(tǒng)調(diào)用層的事件,審計(jì)系統(tǒng)調(diào)用和跟蹤進(jìn)程執(zhí)行。從日志、跟蹤及度量三個(gè)維度檢查相關(guān)輸出,進(jìn)而來衡量系統(tǒng)內(nèi)部安全狀態(tài)的能?。eBPF 的安全可觀測性表現(xiàn)為對(duì)內(nèi)核
    發(fā)表于 09-08 15:31

    openEuler 倡議建立 eBPF 軟件發(fā)布標(biāo)準(zhǔn)

    eBPF 是一個(gè)能夠在內(nèi)核運(yùn)行沙箱程序的技術(shù),提供了一種在內(nèi)核事件和用戶程序事件發(fā)生時(shí)安全注入代碼的機(jī)制,使得非
    發(fā)表于 12-23 16:21

    如何利用C/C++編寫應(yīng)用程序加速內(nèi)核運(yùn)行

    SDAccel編譯器支持OpenCL C,C和C ++,用于定義FPGA執(zhí)行的內(nèi)核功能。 了解如何利用用C / C ++編寫的現(xiàn)有函數(shù)作為FPGA上運(yùn)行的OpenCL應(yīng)用程序加速
    的頭像 發(fā)表于 11-20 06:40 ?3156次閱讀

    如何利用SPT和多個(gè)內(nèi)核加速雷達(dá)信號(hào)處理

    基于S32R如何利用SPT和多個(gè)內(nèi)核加速雷達(dá)信號(hào)處理。
    的頭像 發(fā)表于 01-18 07:16 ?5042次閱讀
    如何<b class='flag-5'>利用</b>SPT和多個(gè)<b class='flag-5'>內(nèi)核</b><b class='flag-5'>加速</b>雷達(dá)信號(hào)處理

    一文手把手教你Android中的 eBPF 流量監(jiān)控

    應(yīng)用訪問網(wǎng)絡(luò)。從該工具收集的統(tǒng)計(jì)數(shù)據(jù)存儲(chǔ)在稱為 eBPF maps 的內(nèi)核數(shù)據(jù)結(jié)構(gòu)中,并且相應(yīng)結(jié)果由 NetworkStatsService 等服務(wù)用來提供自設(shè)備上次啟動(dòng)以來的持久流量
    的頭像 發(fā)表于 06-20 10:34 ?5109次閱讀
    一文手把手教你Android中的 <b class='flag-5'>eBPF</b> 流量監(jiān)控

    教你們?nèi)绾问褂?b class='flag-5'>eBPF追蹤LINUX內(nèi)核

    1. 前言 我們可以使用BPF對(duì)Linux內(nèi)核進(jìn)行跟蹤,收集我們想要的內(nèi)核數(shù)據(jù),從而對(duì)Linux中的程序進(jìn)行分析和調(diào)試。與其它的跟蹤技術(shù)相比,使用BPF的主要優(yōu)點(diǎn)是幾乎可以訪問Linu
    的頭像 發(fā)表于 04-20 11:26 ?2715次閱讀
    教你們?nèi)绾问褂?b class='flag-5'>eBPF</b>追蹤LINUX<b class='flag-5'>內(nèi)核</b>

    openEuler倡議建立eBPF軟件發(fā)布標(biāo)準(zhǔn)

    eBPF 是一個(gè)能夠在內(nèi)核運(yùn)行沙箱程序的技術(shù),提供了一種在內(nèi)核事件和用戶程序事件發(fā)生時(shí)安全注入代碼的機(jī)制,使得非
    的頭像 發(fā)表于 12-06 10:29 ?764次閱讀

    Linux 內(nèi)核eBPF優(yōu)勢和eBPF潛力總結(jié)

    Express Data Path (XDP):網(wǎng)絡(luò)驅(qū)動(dòng)程序是最早可以附加 XDP BPF 鉤子的點(diǎn)。當(dāng)收到一個(gè)數(shù)據(jù)包時(shí),eBPF 程序就會(huì)被觸發(fā)運(yùn)行。
    發(fā)表于 01-10 11:37 ?3746次閱讀

    Linux內(nèi)核觀測技術(shù)eBPF中文入門指南

    eBPF(extened Berkeley Packet Filter)是一種內(nèi)核技術(shù),它允許開發(fā)人員在不修改內(nèi)核代碼的情況下運(yùn)行特定的功能。eBPF 的概念源自于 Berkeley
    的頭像 發(fā)表于 02-08 09:45 ?2794次閱讀

    什么是eBPF,eBPF為何備受追捧?

    用云杉網(wǎng)絡(luò) VP 向陽的話來說:“ eBPF 最重要(沒有之一)的特點(diǎn)是安全性” 。他表示,以往必須編寫內(nèi)核模塊才能做到的工作現(xiàn)在基本都能做到。
    的頭像 發(fā)表于 05-06 11:41 ?2689次閱讀

    eBPF,何以稱得上是革命性的內(nèi)核技術(shù)?

    eBPF 的全稱是 extended Berkeley Packet Filter,它被稱之為 “革命性” 的內(nèi)核技術(shù),可以在 Linux 內(nèi)核中運(yùn)行沙盒程序,而無需更改
    的頭像 發(fā)表于 05-08 08:26 ?1059次閱讀
    <b class='flag-5'>eBPF</b>,何以稱得上是革命性的<b class='flag-5'>內(nèi)核</b>技術(shù)?

    基于ebpf的性能工具-bpftrace

    運(yùn)行情況對(duì)于診斷問題、優(yōu)化性能以及進(jìn)行安全監(jiān)控至關(guān)重要。bpftrace作為一款強(qiáng)大的跟蹤工具,為開發(fā)人員和系統(tǒng)管理員提供了一種獨(dú)特的方式來監(jiān)視和分析Linux系統(tǒng)的內(nèi)部運(yùn)行。本文描述bpftrace的原理和使用。 bpftrace 「bpftrace是基于eBPF和BBC實(shí)現(xiàn)了通過探針機(jī)制采集
    的頭像 發(fā)表于 09-04 16:02 ?993次閱讀
    基于<b class='flag-5'>ebpf</b>的性能工具-bpftrace

    ebpf的快速開發(fā)工具--libbpf-bootstrap

    )和libbpf的程序。eBPF是一種可以在Linux內(nèi)核中運(yùn)行的程序,提供了強(qiáng)大的網(wǎng)絡(luò)過濾、系統(tǒng)調(diào)用監(jiān)控和性能分析等功能。libbpf是一個(gè)庫,用于加載和管理
    的頭像 發(fā)表于 09-25 09:04 ?1503次閱讀
    <b class='flag-5'>ebpf</b>的快速開發(fā)工具--libbpf-bootstrap

    eBPF動(dòng)手實(shí)踐系列三:基于原生libbpf庫的eBPF編程改進(jìn)方案簡析

    在上一篇文章《eBPF動(dòng)手實(shí)踐系列二:構(gòu)建基于純C語言的eBPF項(xiàng)目》中,我們初步實(shí)現(xiàn)了脫離內(nèi)核源碼進(jìn)行純C語言eBPF項(xiàng)目的構(gòu)建。libbpf庫在早期和
    的頭像 發(fā)表于 03-19 14:19 ?1272次閱讀
    <b class='flag-5'>eBPF</b>動(dòng)手實(shí)踐系列三:基于原生libbpf庫的<b class='flag-5'>eBPF</b>編程改進(jìn)方案簡析