有很多性能问题,在系统使用的初期,不大能看出来,因为使用量的很小。随着系统的不断深入使用,性能问题才出现,尤其是那种24*7都要不停运行的程序。
下面的一个例子,是经常在项目中都会用到的.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_)
这个是解析类的声明,重要的是它的数据结构,它采用了两级链表的结构,第一级是所有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;
}
上面的一个方法是添加一个值到配置中去,它的算法是首先查找它所在的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 );
};
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;
}
两者的性能差距有多大?超过你的想象