放大缩小保证div对齐_NFS Write IO 不对齐深度分析
導讀:NFSClient 對大多數(shù)的應用寫入沒有做對齊優(yōu)化,本文根據(jù) IO 不對齊的原因給出了若干實踐建議。
作者 | 裴曉輝
背景
最近團隊小伙伴弗曼統(tǒng)計了線上用戶數(shù)據(jù)寫入對齊情況,通過統(tǒng)計數(shù)據(jù)發(fā)現(xiàn)了一個有趣的現(xiàn)象: 用戶寫入請求中近 70% 的數(shù)據(jù)塊 4K 不對齊,這也就是說 NFSClient 對大多數(shù)的應用寫入沒有做對齊優(yōu)化。
下面會從 NFSClient BufferWrite 實現(xiàn)流程的維度解釋 IO 不對齊的原因,最后據(jù)此給出了若干實踐建議。
場景分析
應用程序一般可以使用 DirectIO 或 BufferIO 兩種方式向文件寫入數(shù)據(jù)。在 DirectIO 模式時 NFSClient 直接將用戶 IO 通過 RPC 發(fā)送給服務端,因此 DirectIO 方式寫入 NFSClient 不會做對齊,但考慮到應用程序使用 DirectIO 時一般會在應用側(cè)做對齊,因此非對齊 IO 的多數(shù)應該是 BufferIO 方式。
內(nèi)核使用 struct nfs_req 對象記錄某個緩存頁更改情況,同時使用 struct page 對象的 private 字段保存,因此只需分析 BufferIO 時 nfs_req 的處理邏輯就能夠知道對齊規(guī)則。NFSClient 調(diào)用 nfs_updatepage() 更新 nfs_req 對象,其核心代碼如下:
從上圖紅色框中代碼可以看出來,NFSClient 會嘗試按照緩存頁大小對 offset/count 對齊,繼續(xù)查看 nfs_can_extent_write() 函數(shù)實現(xiàn):
從上述代碼可知:
1) 若為同步寫,則不嘗試對齊,這是因為對同步寫做對齊并沒有明顯收益還會放大 IO;
2) 若緩存頁內(nèi)容不是最新,則不允許對齊,否則就要讀懲罰,注意 nfs_write_begin() 函數(shù)已處理是否需要讀懲罰;
3) 若持有 Write Delegation,則允許對齊,因為 Write Delegation 保證本地緩存數(shù)據(jù)一定是最新,關(guān)于 Delegation 可參考文章《NFSClient Delegation 實現(xiàn)與陷阱》;
4) 若不持有文件鎖,則允許對齊,注意此處依據(jù) NFSClient 設(shè)計哲學的一個假設(shè):應用在不持有文件鎖寫入數(shù)據(jù)時,應用側(cè)應該保證文件不會被多客戶端修改;
5) 若持有全文件的寫鎖,則允許對齊,因為寫鎖保證了本地緩存數(shù)據(jù)肯定是有效的且不存在多客戶端更改,此外這里要求全文件鎖的原因是為了簡化代碼邏輯。
合并規(guī)則
上面提到內(nèi)核使用 struct nfs_req 對象表示每個緩存頁的更改情況,考慮到 struct nfs_req 只能表示單區(qū)間,因此 BufferIO 需合并同一個緩存頁的多次更改,合并規(guī)則較為簡單:
兩個寫合并后必須是一個連續(xù)的區(qū)間。
相關(guān)代碼實現(xiàn)可參考 nfs_try_to_update_request() 函數(shù),代碼較為簡單,故不再詳細描述。從合并規(guī)則還可以知道:同一個緩存頁上的兩個非連續(xù) IO 需要兩次 RPC 寫入。
緩存頁 UpdateToDate 設(shè)置
從場景分析中知道緩存頁是否設(shè)置了 UpdateToDate 標記是同一緩存頁上的兩個 IO 能否合并的重要前提條件,那 NFSClient 是什么時候?qū)⒕彺骓撛O(shè)置為 UpdateToDate 狀態(tài)的呢?總的來說,NFSClient 在兩個地方嘗試設(shè)置該標記:
1) 將緩存頁加入到 address space 時,實現(xiàn)代碼是 nfs_write_begin() 函數(shù),相關(guān)邏輯如下:
繼續(xù)查看 nfs_want_read_modify_write() 函數(shù)實現(xiàn):
上圖中藍色框中是 pNFS 相關(guān)處理暫時不做討論,紅色框則是一般情況下將緩存頁加入到 address space 時是否需要讀懲罰(讀取數(shù)據(jù)后緩存頁內(nèi)容自然為最新),具體的需滿足如下幾個條件:
a) 若應用打開文件模式允許讀,則允許讀懲罰,這是因為根據(jù)數(shù)據(jù)的局部性原理,剛寫入的數(shù)據(jù)很可能再次讀;
b) 緩存頁內(nèi)容不是最新,若已經(jīng)是最新很顯然沒必要再次讀老數(shù)據(jù);
c) 當前緩存頁沒有正在進行的 IO,也就是說沒有向服務端有讀寫請求,注意 nfs_req 只能表示單個 IO,顯然此時不允許讀;
d) 本次修改只是緩存頁的局部內(nèi)容,顯然如果全覆蓋是沒有必要讀入老數(shù)據(jù);
2) 當內(nèi)核將待修改的數(shù)據(jù)拷貝到緩存頁后會調(diào)用 nfs_write_end() 函數(shù)通知 NFSClient 數(shù)據(jù)已經(jīng)拷貝到緩存頁,此時 NFSClient 需根據(jù)寫入情況設(shè)置緩存頁是否為最新的狀態(tài):
通過上面代碼可知:
a) 藍色框中表示緩存頁是文件變大時新追加的緩存頁,此時緩存頁內(nèi)容為全零,自然可設(shè)置為最新;
b) 紅色框中表示緩存頁中所有的有效數(shù)據(jù)均被覆蓋,此時緩存頁內(nèi)容必然為最新;
總之,數(shù)據(jù)寫入后若完全覆蓋該緩存頁的所有原有效數(shù)據(jù),則設(shè)置為最新。
實踐建議
至此基本搞清楚了 NFSClient BufferWrite 的對齊實現(xiàn)邏輯,感興趣的同學可自行編寫測試用例驗證。結(jié)合對齊實現(xiàn)和測試驗證,初步的可給出如下建議:
1) O_SYNC 方式不會做緩存頁對齊;
2) 當文件被大量小塊 IO 重復覆蓋寫時,可考慮用 O_RDWR 方式打開(注意副作用是會有讀懲罰),有利于聚合同一個緩存頁的寫 IO,減少 RPC 次數(shù);
3) 使用 O_WRONLY 方式打開時,同一緩存頁的不連續(xù)更改不會做聚合,每個 IO 都會觸發(fā)一次 RPC,降低訪問性能;
4) 使用類 MPI 方式多客戶端并發(fā)修改同一文件時,條帶大小應該做到緩存頁對齊,否則可能會導致數(shù)據(jù)被錯誤覆蓋;
5) 不恰當?shù)氖褂梦募i會導致不做緩存頁對齊;
6) 不使用文件鎖時小塊 IO 可能會緩存頁對齊,導致 IO 放大。
總結(jié)
緩存頁對齊是提高 BufferIO 訪問性能地有效手段,NFSClient 在設(shè)計上盡量會嘗試緩存頁對齊,但受限于 NFS 共享特性的約束,也只能對較為有限的情況做緩存頁對齊,這就潛在地要求應用側(cè)配合才可以達到最優(yōu)性能。
總結(jié)
以上是生活随笔為你收集整理的放大缩小保证div对齐_NFS Write IO 不对齐深度分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python与机械教育初探_Python
- 下一篇: c++ hough变换代码_hough变