有很多性能問(wèn)題,在系統(tǒng)使用的初期,不大能看出來(lái),因?yàn)槭褂昧康暮苄 kS著系統(tǒng)的不斷深入使用,性能問(wèn)題才出現(xiàn),尤其是那種24*7都要不停運(yùn)行的程序。
下面的一個(gè)例子,是經(jīng)常在項(xiàng)目中都會(huì)用到的.ini配置文件生成和解析的過(guò)程
比如
[section1]
key1 = value1 ;here is comment
key2 = value2
[section2]
key3 = value3
key4 = value4
當(dāng)然WinAPI也提供了
WritePrivateProfileString,
GetPrivateProfileString兩個(gè)API來(lái)讀寫(xiě)這種文件格式,但是
每次使用都會(huì)打開(kāi)文件來(lái)讀寫(xiě),性能非常低,只適用于小規(guī)模的數(shù)據(jù)讀寫(xiě)。
看我們的代碼:
#define _TD_INI_H_
#include <list>
#include <fstream>
using namespace std;
class KEV_VALUE
{
public:
KEV_VALUE(){m_bIsGarbage=FALSE;m_bSingleLine=FALSE;};
virtual ~KEV_VALUE(){};
string m_key;
string m_value;
BOOL m_bIsGarbage;
BOOL m_bSingleLine;
};
typedef list<KEV_VALUE> LIST_KEY_VELUE;
class SECTION
{
public:
SECTION(){};
virtual ~SECTION(){};
string m_section;
LIST_KEY_VELUE m_listKeyValue;
};
typedef list<SECTION> LIST_SECTION;
class CTDIni
{
public:
CTDIni( void );
virtual ~CTDIni( void );
BOOL UpdateData( BOOL bSave=true );
void SetFileName( const CHAR *lpstrFileName );
BOOL GetLastSectionName( string& str );
BOOL AddSection( const CHAR *lpstrSection );
BOOL DeleteSection( const CHAR *lpstrSection);
BOOL ReadSection( const CHAR *lpszSection, string& str );
BOOL SetKeyValue( const CHAR *lpstrSection, const CHAR *lpstrKey, const CHAR *lpstrValue );
BOOL SetKeyValue( const CHAR *lpstrSection, const CHAR *lpstrKey, const INT32 nValue );
BOOL GetKeyValue( const CHAR *lpstrSection, const CHAR *lpstrKey, CHAR *lpstrValue );
BOOL DeleteKeyValue( const CHAR *lpstrSection, const CHAR *lpstrKey );
BOOL ChangeKeyName( const CHAR *lpstrSection, const CHAR *lpstrKeyOld, const CHAR *lpstrKeyNew );
BOOL ChangeSectionName( const CHAR *lpstrSectionOld, const CHAR *lpstrSectionNew );
private:
LIST_SECTION m_sections;
string m_strFileName;
BOOL AddGarbage( const CHAR *lpstrSection, const CHAR *lpstrGarbage, BOOL bSingleLine=FALSE );
};
#endif // !defined(_TD_INI_H_)
這個(gè)是解析類(lèi)的聲明,重要的是它的數(shù)據(jù)結(jié)構(gòu),它采用了兩級(jí)鏈表的結(jié)構(gòu),第一級(jí)是所有section的list,第二級(jí)是section下面的key-value的list.
下面是其中的實(shí)現(xiàn)代碼片斷
BOOL CTDIni::SetKeyValue( const CHAR *lpstrSection, const CHAR *lpstrKey, const CHAR *lpstrValue )
{
LIST_SECTION::iterator i;
for( i = m_sections.begin(); i != m_sections.end(); i++ )
{
if( (*i).m_section == lpstrSection )
{
LIST_KEY_VELUE::iterator j;
for( j = (*i).m_listKeyValue.begin(); j != (*i).m_listKeyValue.end(); j++ )
{
if( (*j).m_key == lpstrKey )
{
(*j).m_value = lpstrValue;
return TRUE;
}
}
KEV_VALUE tmp;
tmp.m_key = lpstrKey;
tmp.m_value = lpstrValue;
(*i).m_listKeyValue.push_back( tmp );
return TRUE;
}
}
return FALSE;
}
上面的一個(gè)方法是添加一個(gè)值到配置中去,它的算法是首先查找它所在的section,然后查找所在的key,
最后決定是insert還是update.
這樣性能問(wèn)題就來(lái)了,當(dāng)數(shù)據(jù)不斷增加,SetKeyValue所需要的時(shí)間呈N*N的方式增長(zhǎng)。很可怕。CPU的占用率也會(huì)跑到很高。
最后,我們不得不進(jìn)行優(yōu)化,因?yàn)樵谖覀兊捻?xiàng)目中,不存在相同的section,也沒(méi)有相同的key,所以我們使用map,使得查找時(shí)間變成常數(shù)級(jí)別。(即使有相同的key§ion,也可以使用multimap)
優(yōu)化后的數(shù)據(jù)結(jié)構(gòu)是這樣的
#include <string>
#include <stdio.h>
#include <list>
#include <map>
#include <fstream>
using namespace std;
struct VELUE
{
string m_value;
BOOL m_bIsGarbage;
BOOL m_bSingleLine;
};
typedef std::map<std::string,VELUE> MAP_KEY_VELUE;
typedef std::map<std::string,MAP_KEY_VELUE> MAP_SECTION;
class CTDIni
{
public:
CTDIni( void );
virtual ~CTDIni( void );
BOOL UpdateData( BOOL bSave=true );
void SetFileName( const CHAR *lpstrFileName );
BOOL GetLastSectionName( string& str );
BOOL AddSection( const CHAR *lpstrSection );
BOOL DeleteSection( const CHAR *lpstrSection);
BOOL ReadSection( const CHAR *lpszSection, string& str );
BOOL IsExistSection( const CHAR *lpstrSection);
BOOL SetKeyValue( const CHAR *lpstrSection, const CHAR *lpstrKey, const CHAR *lpstrValue );
BOOL SetKeyValue( const CHAR *lpstrSection, const CHAR *lpstrKey, const INT32 nValue );
BOOL GetKeyValue( const CHAR *lpstrSection, const CHAR *lpstrKey, CHAR *lpstrValue );
BOOL DeleteKeyValue( const CHAR *lpstrSection, const CHAR *lpstrKey );
BOOL ChangeKeyName( const CHAR *lpstrSection, const CHAR *lpstrKeyOld, const CHAR *lpstrKeyNew );
BOOL ChangeSectionName( const CHAR *lpstrSectionOld, const CHAR *lpstrSectionNew );
private:
MAP_SECTION m_sections;
string m_strFileName;
BOOL AddGarbage( const CHAR *lpstrSection, const CHAR *lpstrGarbage, BOOL bSingleLine=FALSE );
};
SetKeyValue那個(gè)方法的實(shí)現(xiàn)是這樣:
BOOL CTDIni::SetKeyValue( const CHAR *lpstrSection, const CHAR *lpstrKey, const CHAR *lpstrValue )
{
MAP_SECTION::iterator i;
MAP_KEY_VELUE::iterator j;
i = m_sections.find(lpstrSection);
if ( i == m_sections.end())
{
return FALSE;
}
j = i->second.find(lpstrKey);
if( j != i->second.end()) //update
{
j->second.m_value = lpstrValue;
}
else //insert
{
VELUE tmp;
tmp.m_value = lpstrValue;
tmp.m_bSingleLine = false;
tmp.m_bIsGarbage = false;
i->second[lpstrKey] = tmp;
}
return TRUE;
}
兩者的性能差距有多大?超過(guò)你的想象