有很多性能問題,在系統使用的初期,不大能看出來,因為使用量的很小。隨著系統的不斷深入使用,性能問題才出現,尤其是那種24*7都要不停運行的程序。
下面的一個例子,是經常在項目中都會用到的.ini配置文件生成和解析的過程
比如
[section1]
key1 = value1 ;here is comment
key2 = value2
[section2]
key3 = value3
key4 = value4
當然WinAPI也提供了WritePrivateProfileString,GetPrivateProfileString兩個API來讀寫這種文件格式,但是
每次使用都會打開文件來讀寫,性能非常低,只適用于小規模的數據讀寫。
看我們的代碼:
這個是解析類的聲明,重要的是它的數據結構,它采用了兩級鏈表的結構,第一級是所有section的list,第二級是section下面的key-value的list.
下面是其中的實現代碼片斷
上面的一個方法是添加一個值到配置中去,它的算法是首先查找它所在的section,然后查找所在的key,
最后決定是insert還是update.
這樣性能問題就來了,當數據不斷增加,SetKeyValue所需要的時間呈N*N的方式增長。很可怕。CPU的占用率也會跑到很高。
最后,我們不得不進行優化,因為在我們的項目中,不存在相同的section,也沒有相同的key,所以我們使用map,使得查找時間變成常數級別。(即使有相同的key§ion,也可以使用multimap)
優化后的數據結構是這樣的
SetKeyValue那個方法的實現是這樣:
兩者的性能差距有多大?超過你的想象
下面的一個例子,是經常在項目中都會用到的.ini配置文件生成和解析的過程
比如
[section1]
key1 = value1 ;here is comment
key2 = value2
[section2]
key3 = value3
key4 = value4
當然WinAPI也提供了WritePrivateProfileString,GetPrivateProfileString兩個API來讀寫這種文件格式,但是
每次使用都會打開文件來讀寫,性能非常低,只適用于小規模的數據讀寫。
看我們的代碼:
#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_)
#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_)
這個是解析類的聲明,重要的是它的數據結構,它采用了兩級鏈表的結構,第一級是所有section的list,第二級是section下面的key-value的list.
下面是其中的實現代碼片斷
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;
}
{
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;
}
上面的一個方法是添加一個值到配置中去,它的算法是首先查找它所在的section,然后查找所在的key,
最后決定是insert還是update.
這樣性能問題就來了,當數據不斷增加,SetKeyValue所需要的時間呈N*N的方式增長。很可怕。CPU的占用率也會跑到很高。
最后,我們不得不進行優化,因為在我們的項目中,不存在相同的section,也沒有相同的key,所以我們使用map,使得查找時間變成常數級別。(即使有相同的key§ion,也可以使用multimap)
優化后的數據結構是這樣的
#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 );
};
#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那個方法的實現是這樣:
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;
}
{
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;
}
兩者的性能差距有多大?超過你的想象