有很多性能问题,在系统使用的初期,不大能看出来,因为使用量的很小。随着系统的不断深入使用,性能问题才出现,尤其是那种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;
}
两者的性能差距有多大?超过你的想象