【經驗總結】xml
解析到底能多快?
----by abllen
近來,因為工作的關係,需要對大xml檔案進行解析使用,就專門對解析效率問題作了下研究。
場景:
在我們的場景中,會將大量的業務配置資訊存放在db, 便於前端修改, 後端使用時,會先將db匯出到本地xml格式檔案,再各自載入。 這裡之所以不直連資料庫一是考慮後端高併發時的效能問題,二是資料一般變動不頻繁,存放xml也合適。
起初也曾參考一些現有的xml解析庫,但由於我們的xml檔案都是從db匯出的,對有些情況不很合適,因而考慮手寫。
為什麼會手寫:
1) 允許xml的key為純數字,因為要從資料庫匯出到xml,而資料庫的內容往往難以避免有純數字的出現
2) 允許空key,資料庫裡往往使用空欄位表示預設,也比較方便
3) 相容xml格式,更好的出錯說明
我們希望使用完全的自左向右掃瞄分析,如對
能提示a/as
key缺少
>更好些, 而不是某一行缺少閉合的》不完整
tips:曾經對乙個800k的上萬行xml檔案進行排錯,包括notepad++/chrome/ie 等瀏覽器的出錯資訊讓人崩潰,往往只有 某一行缺少;等
4) 更好的把握
5) 學習,只有親手寫並和其他解析器對比,才能知道真正的瓶頸和問題在哪,希望能挖掘效能極限,看解析究竟能有多快
經過一段時間的調教,得到如下結果:
1) 俺的滿足系統要求的xml解析器核心**只有50行,純模板,外圍功能還支援增量解析合併,易用
2) 解析效能可以對比 strlen ,媲美rapidxml,是一般的tinyxml的數十倍。
關於rapidxml:
1) 號稱比tinyxml
快30~60倍,是boost.propertytree
的預設xml解析器
2) 對乙個約1.5m,utf-8編碼包含中/英文,有一定層次深度,大約3.3萬行的範例檔案解析 tinyxml: 54ms 而 rapidxml: 4ms!
(資料參考
)3) 可對比strlen
ps:為什麼拿strlen對比,
由於解析一般都是對檔案字元進行的判斷處理,而strlen最簡單,且由於對0值判斷的特點,多數系統都採用了特別的優化,可以按系統位長(如32bit就是4位元組)來進行比較,效率很高並且是穩定的,可以作為基礎標尺。
測試及結論:
機器環境
系統win2003server + mingw gcc4.6.1
amd單核機器
1.8ghz
記憶體ddr333 1g
(為了能明顯看出差異,特別動用了一台老爺機, 當然不用過於擔心現在機器的差異,拜amd不進取所致和頻率牆限制,intel那邊現在也是縫縫補補又三年,單核的效能這幾年幾乎沒有多少比例提高)
系統記憶體頻寬:
2.7gb/s aida64
記憶體讀取效能約
1200mb/s
(現在一般ddr3 1600的記憶體頻寬 12gb/s, aida64 檢測單通道讀取約5.8gb/s)
基準資料
範例檔案
1.5m
資料重複
1000
次,memcpy 3s
,strlen 2.8s
,自己寫的按位元組
mystrlen 12.1s
基本上自測的資料是上述aida64測試效能的再對半折,這裡面也可以看出x86的位元組不對齊影響不大。
自測結果
仍舊是範例檔案,
重複1000
次,rapidxml 14.2s
,myparse 14.1s
,與按位元組寫的
mystrlen
基本相當!!!
多出的主要是些邏輯判斷等,這應該是目前語言層面所能達到的基本極限了,更深的挖掘,可能需要考慮cpu擴充套件指令如sse的使用了,不過這類解析都是乙個前後關聯的過程,並行化並不容易。
如何做到的:
1) 只做一遍掃瞄分析,cpu的內部運算及頻寬相對記憶體還是快很多的,乙個字串多次比較和每次多個值比較會慢些,所以不要做多次子串的掃瞄,在測試中這減少了近2s
而一般的解析器為了簡單等原因,可能會對源串做多次子串操作,譬如當分析了乙個合規範的段後,剩餘內容子段化, a –
> a
』 + b(a substr or erase a
』), 這裡面都可能包含大量的記憶體複製操作,會導致效能的迅速低下,如我優化前的解析器,效能就差50倍還多,快兩個量級。
2) 使用查表法換時間,當只做乙個自左至右的分析時,不可避免會對字串多次比較,
如』***
』 id2=
』yyy
』 />, 對key
abc每乙個字元的判斷,便不能為 \n \r \t space = / >
使用查表法便可以解決這個速度問題,現代的cpu快取一般每核心都有二級256k, **2m,足夠快取錶值,見rapidxml
file: rapidxml.hpp version: 1.13 2423行
3) 模板程式設計可趨於減少執行時間,編譯器會優化掉中間的多層轉換及呼叫問題
4) 減少記憶體的分配,特別是小記憶體的頻繁分配釋放
在測試中只將每個key、id、value轉換成string,時間就已拉公升到了40ms左右,轉換到簡單dom樹要50ms,rapidxml則使用了自己特別的mempool(rapidxml只從記憶體解析,使用時會修改給定的記憶體段並複製),幾乎避免了記憶體的多次分配,時間也就幾乎保持了。
最後談一下文字與二進位制協議的看法:
1) 二進位制協議其實也要有拷貝及判斷等,極限情況下,只算一次比較及複製(二進位制也難以避免多次讀取判斷, 特別是字段複雜和多的情況下),按字元比較佔1/3算,需要
4×1/3 + 1×2/3= 2,即2倍strlen,加上更複雜的邏輯運算,僅比xml解析快一倍不到
2) 解析往往不是問題,儲存才是。rapidxml之所以快,還在於mempool的應用, 基本沒有記憶體的來回拷貝和小物件問題, 而一般的應用,包括我們的解析庫,最後都回到了對map類
stl的使用,stl不可避免會有多次的記憶體拷貝,加上中間小物件的頻繁分配釋放,效能會立馬降低幾倍,在這個比較上,純粹解析的差別就微乎其微。在實際的測試中,儲存往往會是乙個更大的影響, 而非解析。測試中, 將對映關係存入map,立即就到 ~50ms
3) 選擇自己喜歡的協議來吧,這真的不是太大問題, 建議不非常渴求的情況下, 使用字串或xml可讀性及可維護性好的協議
4) 對於簡單又短小的協議,可用二進位制
5) 一家之言,歡迎pk討論j
本文原創自無線技術運營空間:
及 (專注無線技術運營
——無線技術
(作業系統
/資料庫
/web前端/
負載均衡
/系統容災
/系統安全
/簡訊接入
/wap
接入/3g等)
、無線業務運營、無線開放平台、統計分析
(使用者行為分析
/資料探勘)、
fseek讀,到底有多快!
常聽人說,fseek很快 到底多快?如果不知道多快,就乙個勁的爭論很快!好像不必擔心的快一樣。這種常識性的東西不知道,妄談系統設計,就會出笑話的。下面做個實驗,測測fseek的量級。測試的方法 1.乙個檔案512m 2.乙個執行緒對應乙個檔案,然後在這個檔案裡邊進行rand fseek,然後讀1k位...
CAN FD的波特率到底能跑多快?
摘要 canfd是基於can20的公升級版協議,為了滿足汽車電子日益增長的高頻寬和高傳輸速率的要求,canfd主要公升級了以下幾個方面 眾所周知,can fd是基於can 2.0的公升級版協議,為了滿足汽車電子日益增長的高頻寬和高傳輸速率的要求,can fd主要公升級了以下幾個方面 一 更高的傳輸波...
記憶體資料庫到底有多快
併發量太高的應用中 比如10分鐘內插入300w條記錄 資料庫往往難堪重負,在沒有銀子實現伺服器集群 負載均衡 分布式儲存的情況下,可以嘗試一下把資料庫做乙個臨時副本全部放在記憶體中處理,完成操作後,再同步到硬碟的物理資料庫中。那麼,把資料庫放在記憶體中到底有多快?晚上抽空試了一下 步驟1 先用ram...