摘 要:網(wǎng)絡(luò)轉(zhuǎn)存的功能是把記錄儀中記錄的海量數(shù)據(jù)通過網(wǎng)絡(luò)導(dǎo)給上位機(jī)。因?yàn)閿?shù)據(jù)的海量性,此時(shí)網(wǎng)絡(luò)轉(zhuǎn)存的效率成為瓶頸。本文主要介紹Altera Nios平臺下如何逐步提升記錄儀網(wǎng)絡(luò)轉(zhuǎn)存的效率及其過程分析。
關(guān)鍵詞:記錄儀;網(wǎng)絡(luò)轉(zhuǎn)存;海量數(shù)據(jù)
中圖分類號:TP311
海量數(shù)據(jù)記錄儀是基于FPGA(Altera Nios II軟核+MicroC/OS-II實(shí)時(shí)操作系統(tǒng))開發(fā)的嵌入式系統(tǒng),可以實(shí)時(shí)記錄海量大數(shù)據(jù),具備記錄、回放、轉(zhuǎn)存、網(wǎng)絡(luò)轉(zhuǎn)存等功能。
轉(zhuǎn)存是通過光纖和PCIE將海量數(shù)據(jù)導(dǎo)給轉(zhuǎn)存服務(wù)器上的磁盤陣列,它需要額外的轉(zhuǎn)存服務(wù)器和上位機(jī)一起工作才能共同完成。當(dāng)只需要提取記錄儀上不太大的一段數(shù)據(jù)進(jìn)行數(shù)據(jù)分析時(shí),通過轉(zhuǎn)存來完成顯得麻煩。網(wǎng)絡(luò)轉(zhuǎn)存正是為解決這個(gè)問題加入的新功能,上位機(jī)本身就可以完成。
記錄儀通過網(wǎng)絡(luò)將需要的數(shù)據(jù)發(fā)給上位機(jī),是為網(wǎng)絡(luò)轉(zhuǎn)存。由于數(shù)據(jù)的海量性,網(wǎng)絡(luò)轉(zhuǎn)存的效率此時(shí)成為最大的一個(gè)瓶頸。如何提升其效率就是本文重點(diǎn)介紹的內(nèi)容。
1 網(wǎng)絡(luò)轉(zhuǎn)存過程
記錄儀和上位機(jī)之間是通過百兆網(wǎng)絡(luò)進(jìn)行通信的,通過UDP報(bào)文協(xié)議,上位機(jī)給記錄儀發(fā)送開始網(wǎng)絡(luò)轉(zhuǎn)存的命令,記錄儀收到命令后,找到正確的文件位置,讀取數(shù)據(jù),并打包通過UDP發(fā)給上位機(jī)。見圖1。
圖1 記錄儀上位機(jī)網(wǎng)絡(luò)轉(zhuǎn)存通信協(xié)議
由于百兆網(wǎng)的物理特性,其最大的傳輸速度在8MB/s(每秒8兆字節(jié))左右。所以我們的目標(biāo)就是盡可能得把網(wǎng)絡(luò)轉(zhuǎn)存的速度往這個(gè)速度靠攏。
2 效率優(yōu)化過程
最初的網(wǎng)絡(luò)轉(zhuǎn)存速度大概在1.04MB/s左右,僅僅通過軟件優(yōu)化,最終速度達(dá)到6.12MB/s。
下文中描述的軟件優(yōu)化過程,不僅僅對本系統(tǒng)有效,其通用性可以推廣到任何嵌入式系統(tǒng)軟件開發(fā)。
2.1 去掉網(wǎng)絡(luò)報(bào)文中校驗(yàn)和檢查
記錄儀和上位機(jī)之間的網(wǎng)絡(luò)通信有固定的網(wǎng)絡(luò)協(xié)議,回波數(shù)據(jù)也是用同樣的網(wǎng)絡(luò)協(xié)議發(fā)給上位機(jī)的。這個(gè)協(xié)議包含諸如報(bào)文頭、報(bào)文長度、命令索引、報(bào)文內(nèi)容、校驗(yàn)和等。
對于回波數(shù)據(jù)報(bào)文來說,計(jì)算校驗(yàn)和耗費(fèi)很大。所以對于單獨(dú)這個(gè)協(xié)議,和上位機(jī)協(xié)議去掉校驗(yàn)和檢查。畢竟對于這么海量的回波數(shù)據(jù)而言,少量的數(shù)據(jù)錯(cuò)誤并不會對后續(xù)的數(shù)據(jù)分析造成太大的影響。
2.2 去掉嵌入式軟件內(nèi)部的調(diào)試信息打印
對于回波數(shù)據(jù)報(bào)文而言,每秒會有幾千個(gè),如果每條報(bào)文都在內(nèi)部打印調(diào)試信息,對系統(tǒng)資源和效率而言,是很大的浪費(fèi)。
2.3 UDP包大小從1KB增大到48KB
由于記錄儀的內(nèi)部邏輯中最小的記錄單元是48KB,因此記錄儀每次傳輸給上位機(jī)的數(shù)據(jù)包(Data package)大小也定義為48KB。而默認(rèn)的網(wǎng)絡(luò)UDP協(xié)議(UDP package)只能支持最大1KB的包,這樣每個(gè)數(shù)據(jù)包需要發(fā)送48個(gè)UDP包才能傳輸完成。頻繁的網(wǎng)絡(luò)及中斷調(diào)用極大地影響了網(wǎng)絡(luò)傳輸效率。
如果能實(shí)現(xiàn)48KB大小的 UDP包,這樣每個(gè)數(shù)據(jù)包只需要對應(yīng)的一個(gè)UDP包就能傳輸完成,從而大大減少頻繁復(fù)雜的UDP調(diào)用。
對于Altera Nios,通過修改系統(tǒng)配置bsp目錄下system.h,加上IP_FRAGMENTS支持,這個(gè)宏使網(wǎng)絡(luò)內(nèi)部驅(qū)動支持UDP大包。
#define INCLUDE_TCP
#define INICHE_DEFAULT_IF \"tse_mac\"
#define IP_FRAGMENTS
#define HEAPBUFS
2.4 memcpy用到的源地址和目的地址改為4字節(jié)對齊
memcpy在不同的平臺上會有不一樣的編譯器實(shí)現(xiàn)。
可惜的是在我們使用的Nios平臺下,memcpy對源地址和目的地址沒有做到很好的處理。在地址沒有4字節(jié)對齊的情況下,是按照字節(jié)賦值的方式來完成內(nèi)存拷貝操作的;在有4字節(jié)對齊的情況下,是通過雙字賦值的方式來完成拷貝的。
而一般情況下CPU對4字節(jié)對齊的地址訪問和雙字賦值效率是最高的。所以為了安全起見,使代碼與編譯器實(shí)現(xiàn)無關(guān),使用頻度非常高的memcpy調(diào)用最好都做到源地址和目的地址4字節(jié)對齊。
下面是Nios的memcpy實(shí)現(xiàn)源碼:
(nios2eds\bin\nios2-gnutools\src\newlib\newlib\libc\string\memcpy.c)
_PTR
_DEFUN (memcpy, (dst0, src0, len0),
_PTR dst0 _AND
_CONST _PTR src0 _AND
size_t len0)
{
…
/* If the size is small, or either SRC or DST is unaligned,
then punt into the byte copy loop. This should be rare. */
if (!TOO_SMALL(len) !UNALIGNED (src, dst))
{
aligned_dst = (long*)dst;
aligned_src = (long*)src;
/* Copy 4X long words at a time if possible. */
while (len >= BIGBLOCKSIZE)
{
*aligned_dst++ = *aligned_src++;
*aligned_dst++ = *aligned_src++;
*aligned_dst++ = *aligned_src++;
*aligned_dst++ = *aligned_src++;
len -= BIGBLOCKSIZE;
}
/* Copy one long word at a time if possible. */
while (len >= LITTLEBLOCKSIZE)
{
*aligned_dst++ = *aligned_src++;
len -= LITTLEBLOCKSIZE;
}
/* Pick up any residual with a byte copier. */
dst = (char*)aligned_dst;
src = (char*)aligned_src;
}
while (len--)
*dst++ = *src++;
…
}
2.5 善用const指針
const指針有時(shí)非常重要,它會告訴編譯器當(dāng)前指針指向一個(gè)常量,編譯器會對它做大量的匯編優(yōu)化,從而使代碼執(zhí)行效率大大提升。
在網(wǎng)絡(luò)轉(zhuǎn)存的數(shù)據(jù)包中,有個(gè)數(shù)據(jù)重組的過程,3個(gè)16KB的板卡數(shù)據(jù)需要按照8字節(jié)的單位重新組合成一個(gè)48KB的數(shù)據(jù)包。const優(yōu)化對大量數(shù)據(jù)的重組來說,效率提升非常顯著。見圖2。
板1A1(8字節(jié)) 數(shù)據(jù)包A3(8字節(jié))
B1(8字節(jié)) A2(8字節(jié))
16KB A1(8字節(jié))
… B3(8字節(jié))
B2(8字節(jié))
板2A2(8字節(jié)) B1(8字節(jié))
B2(8字節(jié))
16KB 48KB
…
板3A3(8字節(jié))
B3(8字節(jié)) …
16KB
…
圖2 數(shù)據(jù)重組過程
對于這個(gè)過程,代碼實(shí)現(xiàn)如下:
VOID FS_NetStore(VOID *pvNetStoreInfo)
{
…
INT32 i4Len =16*1024*3/4;
UINT32 *pu4Dst = (UINT32 *)prNetStoreInfo->rProtocol.u1PageData;
const UINT32 *pu4PageBufferA = …;
const UINT32 *pu4PageBufferB = …;
const UINT32 *pu4PageBufferC = …;
…
// 每個(gè)存儲板中字節(jié)數(shù)據(jù)重組成字節(jié)數(shù)據(jù)。
while (i4Len)
{
*pu4Dst++ = *pu4PageBufferC++;
*pu4Dst++ = *pu4PageBufferC++;
*pu4Dst++ = *pu4PageBufferB++;
*pu4Dst++ = *pu4PageBufferB++;
*pu4Dst++ = *pu4PageBufferA++;
*pu4Dst++ = *pu4PageBufferA++;
i4Len -= 6;
}
}
2.6 編譯器的代碼優(yōu)化等級設(shè)定
Alter Nios使用GCC編譯器,在代碼實(shí)現(xiàn)和調(diào)試階段,建議使用-O0的優(yōu)化等級,在代碼發(fā)布的時(shí)候使用-O2。
其中-O2優(yōu)化等級會優(yōu)化掉無用的調(diào)試信息以及使代碼執(zhí)行非序列化,從而使代碼執(zhí)行效率顯著提高。當(dāng)然-O2會使代碼單步調(diào)試失效。
Altera下會有兩個(gè)編譯優(yōu)化選項(xiàng),一個(gè)針對應(yīng)用層代碼,一個(gè)針對板級代碼。
APP_CFLAGS_OPTIMIZATION := -O2
BSP_CFLAGS_OPTIMIZATION = -O2
3 結(jié)束語
效率對于嵌入式系統(tǒng)來說非常重要,當(dāng)然影響嵌入式系統(tǒng)效率的主要瓶頸是在硬件開發(fā)。像本文所述的網(wǎng)絡(luò)轉(zhuǎn)存來說,如果硬件從百兆網(wǎng)卡改為千兆網(wǎng)卡的話,效率就不可同日而語了。
但是在硬件定型后,軟件優(yōu)化也可以大顯身手,顯著提高效率。本文最終網(wǎng)絡(luò)轉(zhuǎn)存速度達(dá)到6.12MB/s,基本快達(dá)到百兆網(wǎng)絡(luò)的上限了。
參考文獻(xiàn):
[1]Stanley B Lippman,Josee Lajoie.C++ Primer 3rd Edition[M].北京:中國電力出版社,2005.
[2]Andrew Koenig Andrew Koenig.C陷阱與缺陷[M].北京:人民郵電出版社,2008.
作者簡介:王軍(1979-),男,安徽肥東人,碩士研究生,助理工程師,主要從事嵌入式底層軟件、實(shí)時(shí)控制等軟件開發(fā)設(shè)計(jì)。
作者單位:中國電子科技集團(tuán)第三十八研究所,合肥 230031