引:
記憶體拷貝(memcpy)這個操作看似簡單,但長期以來存在很多關於其優化的討論,各種程式語言庫也都有對應實現,而對於memcpy效能評估測試的討論就更多了。
那麼如下的memcpy實現到底有什麼問題?
void * ******_memcpy(void *dst, const void *src, size_t n)12
3456
78910
11void * ******_memcpy(void *dst, const void *src, size_t n)
{const uint8_t *_src = src;
uint8_t *_dst = dst;
size_t i;
for (i = 0; i < n; ++i)
_dst[i] = _src[i];
return dst;
很簡單,首先,這看起來太簡單,不夠高階,氣勢上就先輸了;同時,**沒有使用vector指令,沒有指令級並行,沒有做位址對齊處理,最終效能完全依賴於編譯器的優化——然而這些並沒有什麼問題,在某些應用場景中這個函式的效能甚至會比glibc的memcpy效能更高——當然,這的確完全得益於編譯器的優化。
常見的優化方法
關於memcpy的優化存在大量各種官方和民間資料,因此不再贅述,在這裡只簡單地進行總結。
通常memcpy的效能開銷包含:
分支**
通用的memcpy優化方向:1. 最大限度使用memory/cache頻寬(vector指令、指令級並行)
load/store位址對齊
集中順序訪問
適當使用non-temporal訪存執令
適當使用string指令來加速較大的拷貝
最後,所有的指令都經過cpu的流水線執行,因此對流水線效率的分析至關重要,需要優化指令順序以避免造成流水線阻塞。
如何進行優化
從2023年初開始dpdk對rte_memcpy做了數次優化,優化方向是加速dpdk中memcpy的應用場景,例如vhost收發包,所有的分析和**修改都可以通過git log進行檢視。關於如何著手進行優化這個問題,並沒有唯一的答案。最簡單直接的方法就是暴力破解法:僅憑經驗通過想象和猜測進行各種改進嘗試,並一一在目標場景中進行驗證,然後通過一組評價標準來選擇其中較優的乙個。這是比較接近人工智慧的一種方法,也就是說其實這並不怎麼智慧型,但是往往能夠得到一些效果。
另一種常見的方法是通過對現有**進行執行時取樣分析,得出理論最佳效能,並分析現有**的缺陷,思考是否存在改進方法。當然,這需要大量的經驗和分析,即是用人工代替人工智慧。乙個dpdk 16.11中的真實案例:花了四周時間進行取樣和分析,最後將三行**的位置進行了移動,得到了1.7倍的效能。這是暴力破解法很難完成的,因為搜尋空間實在太大。不過這種看似高階的方法也存在較大的風險,極有可能分析了四周然後不了了之。
至於如何進行取樣分析,如perf、vtune等都是非常有效的效能分析工具,其重點不在於工具多樣,而在於知道需要什麼資料。
最終用資料說話
優化的最終目的是為了加速應用程式的效能,這就需要結合**、資料特徵以及實際硬體平台來進行評估,至於評估的方法則是五花八門。對於memcpy來說,使用micro benchmark可以很方便地得出乙個一目了然的資料,不過這缺乏實際參考價值。因為memcpy演算法本身沒有太大的改進空間,相關優化都是針對具體平台和應用場景所做的程式語言級和指令級的優化,而這個層面的優化都是針對特定的**和硬體平台而言的,不同的場景需要不同的優化方法。因此,一套memcpy的**在某個micro benchmark中效能出色只能證明其指令流適應這個場景。
此外,通過計算某段memcpy的cpu週期數來評估效能也是不可取的。例如,dpdk中vhost enqueuer/dequeue函式,不能簡單地通過rdtsc來標記memcpy占用了多少個時鐘週期,從而判斷memcpy的效能。因為目前的cpu都有著非常複雜的流水線,支援預取和亂序執行,這就造成rdtsc測量小粒度時間間隔時誤差非常大,雖然加入序列化指令可以進行強制同步,但是這改變了指令流執行順序並降低了程式效能,違背了效能測試的初衷。同時,經過編譯器高度優化的**,其指令相對於程式語言來說也是亂序的,如果強制順序編譯則會影響效能,其結果也不具有參考價值。此外,一段指令流的執行時間除流水線的理論執行時間外,還包括資料訪問帶來的延時,通過改變程式的行為很容易讓一段**看起來執行時間更短,而實際上只是將某些資料訪問延時轉提前或延後而已。這些錯綜複雜的因素讓看似簡單的memcpy效能評估變得似乎毫無頭緒。
因此,任何優化工作都應該用最終的效能指標來作為評估依據。例如針對ovs在cloud中的應用,其中的vhost收發函式大量使用了memcpy,那麼針對這個特定的場景,應當以最終的包**速率作為效能評估標準。
下圖展示了乙個接近實際應用場景的例子:首先測試基於dpdk 17.02的ovs-dpdk的包**效能,再通過使用glibc所提供的memcpy替換dpdk vhost中rte_memcpy得到對比資料。測試結果顯示,僅通過使用rte_memcpy加速整個程式中的vhost收發部分就能提供最大約22%的總頻寬提公升。
附錄測試環境: · pvp流:使用ixia發包到物理網絡卡,ovs-dpdk將物理網絡卡收到的包**到虛擬機器,虛擬機器則將包處理後通過ovs-dpdk傳送回物理網絡卡,最後回到ixia
· 虛擬機器中使用dpdk testpmd的mac-forwarding
· ovs-dpdk版本: commit f56f0b73b67226a18f97be2198c0952dad534f1c
· dpdk版本:17.02
· gcc/glibc版本:6.2.1/2.23
· linux:4.7.5-200.fc24.x86_64
· cpu:intel® xeon® cpu e5-2699 v3 @ 2.30ghz
ovs-dpdk編譯和啟動命令如下:
make 『cflags=-g -ofast -march=native』
./ovs-vsctl –no-wait set open_vswitch . other_config:dpdk-init=true
./ovs-vsctl –no-wait set open_vswitch . other_config:dpdk-socket-mem=」1024,1024″
./ovs-vsctl add-br ovsbr0 — set bridge ovsbr0 datapath_type=netdev
./ovs-vsctl add-port ovsbr0 vhost-user1 — set inte***ce vhost-user1 type=dpdkvhostuser
./ovs-vsctl add-port ovsbr0 dpdk0 — set inte***ce dpdk0 type=dpdk options:dpdk-devargs=0000:06:00.0
./ovs-vsctl set open_vswitch . other_config:pmd-cpu-mask=0x10000
./ovs-ofctl del-flows ovsbr0
./ovs-ofctl add-flow ovsbr0 in_port=1,action=output:2
./ovs-ofctl add-flow ovsbr0 in_port=2,action=output:1
虛擬機器中使用dpdk testpmd進行**,命令如下:
set fwd mac
start
sql的效能調優
週末火速趕到了客戶現場,週六開始幹活,查詢程式效能差的原因。經過分析,有一些還是我們需要注意總結的。如 com 元件的預設事務隔離級別,在大併發下面,是乙個很大的瓶頸。asp.net對於sp的呼叫,事務是否正常使用,也是乙個很大的瓶頸。但是對於pssdiag分析出來的結果,我還不知道應該怎麼用?只能...
Wins2003系統中Apache效能優化方法
為了滿足 高負荷的要求,在調整apache引數時發現程序經常占用記憶體過多導致當機。經過不斷的優化和修改引數組合,終於讓伺服器穩定 下來,可以滿足大量訪問的考驗和應用要求。筆者總結了除錯過程中的問題和解決辦法如下,以供有類似需求的網管員參考 系統環境為windows server 2003和apac...
TCP IP 棧的效能調優
給出了幾個可調節的引數,它們可以幫助您提高 linux tcp ip 棧的效能。表 1.tcp ip 棧效能使用的可調節核心引數 可調節的引數 預設值選項說明 proc sys net core rmem default 110592 定義預設的接收視窗大小 對於更大的 bdp 來說,這個大小也應該...