C Xml解析的效率比較QtTinyXml2RapidXml問題背景測試環境Qt - QDomDocumentTinyXml-2RapidXml總結
通常我們在一些軟件的初始化或者保存配置時都會遇到對XML文件的操作,包括讀寫xml文件,解析內容等等。在我的工作中就遇到了這么一個問題,就是在ARM平臺下Qt解析xml文件非常的慢,最初懷疑是我的操作有問題或者是ARM平臺下的文件操作本身就很慢,于是就開始調查到底是哪里的效率問題,下面是一些測試分享給大家。
下面一段代碼是前面提到的運行效率低的一段代碼:
QString filename = "...";QFile file( filename );//< step1 open fileif( !file.open(QIODevice::ReadOnly) ){ qDebug() << "failed in opening file!"; return false;}//< step2 read file contentQDomDocument doc; //< #include <qdom.h>if( !doc.setContent( &file ) ){ qDebug() << "failed in setting content!"; file.close(); return false;}file.close();... //< Operations on the content of file!起初以為是文件打開和關閉耗時太多,所以在文件open和close函數前后都獲取了系統時間來測試了函數消耗時間,結果是耗時很短,反而是 doc.setContent
耗費了非常長的時間,這才發現原來是Qt獲取XML文件內容且Dom模型結構花費了太多時間,所以我們開始尋求效率更高的解決方案。
Windows: system:windows 10 cpu: intel core-i5-5200u @2.2GHz IDE: visual studio 2010 compiler: VC10
linux: system: Debian 4.4.5-8 cpu: intel core-i5-3450 @3.3GHz IDE: VIM compiler: gcc version 4.4.5
Qt版本: 4.8.4 用來測試的文件名為 DriverConfig.xml,大小為245Kb,共1561行,大部分內容為中文 比較項有 TinyXml2, QDomDocument,因為從接口來看這兩者的操作方式很類似,后面我會加入其它的xml解析庫的比較,如 xmlbooster 等。下面是利用Qt中的xml支持來讀取文件內容的源代碼:
#include <QtCore/QCoreapplication>#include <qdom.h>#include <QFile>#include <QIODevice>#include <iostream>#ifdef Q_OS_WIN# include <Windows.h>#else# include <sys/time.h>#endifusing std::cout;using std::endl;#define TEST_TIMES 10int main(int argc, char *argv[]){ QCoreApplication a(argc, argv);#ifdef Q_OS_WIN //< windows long tStart = 0; long tEnd = 0; LARGE_INTEGER nFreq; LARGE_INTEGER nStartTime; LARGE_INTEGER nEndTime; double time = 0.; QueryPerformanceFrequency(&nFreq); QFile file( "D:/DriverConfig.xml" ); QDomDocument doc; for( int i = 0; i < TEST_TIMES; ++i ) { doc.clear(); //< step1 open file if( !file.open(QIODevice::ReadOnly) ) { cout << "failed to open file!" << endl; continue; } Sleep( 100 ); QueryPerformanceCounter(&nStartTime); //< step2 set content if( !doc.setContent(&file) ) { cout << "Failed to read xml file!" << endl; } QueryPerformanceCounter(&nEndTime); time = (double)(nEndTime.QuadPart-nStartTime.QuadPart) / (double)nFreq.QuadPart * 1000.; //< ms cout << " seting content costs " << time << "ms" << endl; file.close(); Sleep( 100 ); }#else //< LINUX timeval starttime, endtime; QFile file( "/home/liuyc/DriverConfig.xml" ); QDomDocument doc; double timeuse = 0.; double timeAverage = 0.; for( int i = 0; i < TEST_TIMES; ++i ) { doc.clear(); //< step1 open file if( !file.open(QIODevice::ReadOnly) ) { cout << "failed to open file!" << endl; continue; } sleep( 1 ); //< delay for 1s gettimeofday( &starttime, 0 ); //< step2 set content if( !doc.setContent(&file) ) { cout << "Failed to read xml file!" << endl; continue; } gettimeofday( &endtime, 0 ); timeuse = 1000000. * (endtime.tv_sec - starttime.tv_sec) + endtime.tv_usec - starttime.tv_usec; timeuse *= 0.001 ; timeAverage += timeuse; cout << " reading files costs : " << timeuse << "ms" << endl; file.close(); sleep( 1 ); //< delay for 1s } timeAverage /= TEST_TIMES; cout << " The End *****************/n average costs = " << timeAverage << "ms" << endl; #endif return a.exec();}下面來看看windows下的運行結果: 當時我的反應是 WTF?? 為什么同一個函數讀同一個文件十次會有這么大的差異所以我才會在文件打開和關閉時分別都加了延時,希望避免文件開關的過程對這個函數產生的影響,結果依然沒有解決這個問題,這個問題希望有大神幫我解答一下!
那下面我們來看linux下的運行結果: 顯然,linux下這個時間相對的穩定可信,所以我們后面的測試也只要以linux下的時間作為參考。
下面我們來看利用tinyxml2實現讀取的源代碼:
#include <iostream>#include "tinyxml2.h"#ifdef _WIN32#include <Windows.h>#else#include <sys/time.h>#endifusing namespace tinyxml2;using std::cout;using std::endl;#define TEST_TIMES 10int main(){#ifndef _WIN32 //< linux ------------------------------------------------ tinyxml2::XMLDocument doc; timeval starttime, endtime; double timeuse = 0.; double timeAverage = 0.; for( int i = 0; i < TEST_TIMES; ++i ) { gettimeofday( &starttime, 0 ); if( XML_SUCCESS != doc.LoadFile( "/home/liuyc/DriverConfig.xml" ) ) { cout << "failed in load xml file! _ " << i << endl; continue; } gettimeofday( &endtime, 0 ); timeuse = 1000000. * (endtime.tv_sec - starttime.tv_sec) + endtime.tv_usec - starttime.tv_usec; timeuse *= 0.001 ; cout << " reading files costs : " << timeuse << "ms" << endl; timeAverage += timeuse; } timeAverage /= TEST_TIMES; cout << " /n** The end *******************/n the average costs = " << timeAverage << "ms" << endl;#else //< windows --------------------------------------------------- LARGE_INTEGER nFreq; LARGE_INTEGER nStartTime; LARGE_INTEGER nEndTime; double time = 0.; QueryPerformanceFrequency(&nFreq); tinyxml2::XMLDocument doc; for( int i = 0; i < TEST_TIMES; ++i ) { QueryPerformanceCounter(&nStartTime); if( XML_SUCCESS != doc.LoadFile( "D:/DriverConfig.xml" ) ) { cout << "failed in load xml file! _ " << i << endl; continue; } QueryPerformanceCounter(&nEndTime); time = (double)(nEndTime.QuadPart-nStartTime.QuadPart) / (double)nFreq.QuadPart * 1000.; //< ms cout << " reading files costs : " << time << "ms" << endl; } cout << endl; system("pause");#endif //< end of windows --------------------------------------------------- return 0;}接下來先看linux下的運行結果(windows下的運行結果已經沒有太多參考價值了): linux下的表現依然很穩定,這里我們可以得出一個很明顯的結論,tinyxml的處理效率要比QDomDocument的處理效率高很多(這里的數據大致是4倍,但不包括對于xml文件內部信息的處理的其他函數接口的調用)。
雖然沒什么參考價值,但還是看一下windows下的測試結果: 這里效率也明顯的比Windows Qt提升很多,而且執行時間也相對穩定了一,所以前一個測試中運行時間十分不穩定的情況暫定為Qt本身實現的問題,具體是什么問題或者在高版本的Qt里面是否已解決尚無答案。
注:RapidXml版本: 1.13 在RapidXml Manual的介紹中可以看到它和TinyXml以及其他的一些xml解析庫做了對比(這里面tinyXml是最慢的),原文中介紹這是目前Xml解析最快的
As a rule of thumb, parsing speed is about 50-100x faster than Xerces DOM, 30-60x faster than TinyXml, 3-12x faster than pugxml, and about 5% - 30% faster than pugixml, the fastest XML parser I know of.
所以這里我也想要試試看RapidXml在內容解析時的效率表現,下面是源代碼:
#include <iostream>#include "rapidxml.hpp"#include "rapidxml_同樣,先看linux下運行的結果:再來看一下windows下的運行結果: 依然不是很穩定,所以只做參考。
統計的時間如下(LINUX):
解析器 | 消耗時間(ms) | 效率倍數(相對Qt) |
---|---|---|
Qt-QDomDocument | 25.85 | 1 |
TinyXml2 | 6.64 | 3.89 |
RapidXml | 2.71 | 9.54 |
工作以來基本上都是在Qt下開發,深切體會到Qt的接口封裝很完善易用,但不可避免的犧牲了一些效率(雖然沒想到效率降低了這么多),相對的,tinyxml2在接口非常相似的同時,效率也有明顯的提升(接近4倍),所以目前xml的解析還是推薦TinyXml2。后面我會繼續仔細研究TinyXml2和RapidXml的使用方法,看是否可以真正將RapidXml的性能發揮出來!
新聞熱點
疑難解答
圖片精選