今天測試人員向我抱怨,說我們提交的API代碼性能比較差,解析一個90萬筆業務的報文居然用了9個小時,這當然是不能接受的。我自己簡單測試下,發現在我的T43/1.5G機器上,API的解析速度只能達到每秒30筆,直觀覺得代碼應存在性能問題。我仔細查閱了程序,沒有發現明顯的問題。到底是什么導致性能損失的呢?我逐步跟蹤每步代碼的執行,終于找到了問題所在,簡單地講此性能問題是頻繁調用函數strlen()導致的。各位可能非常驚訝,我當時也很驚訝,但情況確實如此。90萬筆業務的報文長度約50M,解析報文時我們調用了自己編寫的底層代碼StrStrExt()替換系統的strstr()以便支持的GB18030漢字,而性能正是StrStrExt()底層函數導致的。
先看看原始的StrStrExt()函數的代碼:
// 查找含漢字串的位置
char* StrStrExt(LPCSTR sSearch,LPCSTR sFind)
{
if (IsEmptyStr(sSearch)) return NULL;
if (IsEmptyStr(sFind)) return (char*)sSearch;
ULONG nOffset =0;
ULONG nIndex =0;
ULONG nMaxLen = strlen(sSearch);
ULONG nLen = strlen(sFind);
while(nOffset < nMaxLen)
{
for(nIndex=0; nIndex<nLen; nIndex ++)
{
if(sSearch[nOffset + nIndex] != sFind[nIndex]) break;
}
if(nIndex == nLen) return (char*)(sSearch + nOffset);
int n = IsChineseChar(sSearch + nOffset);
if(n > 0)
{
// 漢字處理
nOffset += n;
}
else
{
nOffset++;
}
}
return NULL;
}
發現strlen()對大字符串可能導致性能問題后,我將該函數修改如下:char* StrStrExt(LPCSTR sSearch,LPCSTR sFind)
{
if (IsEmptyStr(sSearch)) return NULL;
if (IsEmptyStr(sFind)) return (char*)sSearch;
ULONG nOffset =0;
ULONG nIndex =0;
ULONG nMaxLen = strlen(sSearch);
ULONG nLen = strlen(sFind);
while(nOffset < nMaxLen)
{
for(nIndex=0; nIndex<nLen; nIndex ++)
{
if(sSearch[nOffset + nIndex] != sFind[nIndex]) break;
}
if(nIndex == nLen) return (char*)(sSearch + nOffset);
int n = IsChineseChar(sSearch + nOffset);
if(n > 0)
{
// 漢字處理
nOffset += n;
}
else
{
nOffset++;
}
}
return NULL;
}
char* StrStrExt(LPCSTR sSearch,LPCSTR sFind)
{
if (IsEmptyStr(sSearch)) return NULL;
if (IsEmptyStr(sFind)) return (char*)sSearch;
ULONG nOffset =0;
ULONG nIndex =0;
// ULONG nMaxLen = strlen(sSearch);
ULONG nLen = strlen(sFind);
// while(nOffset < nMaxLen)
while(sSearch[nOffset] != 0)
{
for(nIndex=0; nIndex<nLen; nIndex ++)
{
if(sSearch[nOffset + nIndex] != sFind[nIndex]) break;
}
if(nIndex == nLen) return (char*)(sSearch + nOffset);
int n = IsChineseChar(sSearch + nOffset);
if(n > 0)
{
// 漢字處理
// nOffset += n;
for(int ii=0; ii<n; ii++)
{
++nOffset;
if(sSearch[nOffset] == 0) break;
}
}
else
{
nOffset++;
}
}
return NULL;
}
新函數與原函數的唯一差別是不再使用strlen()去獲取串的結束位置,而是直接通過\0結束符判斷串的結束位置。修改后的代碼在我T43的機器上,解析速度提高到每秒3000筆的速度,是原代碼的近100倍,達到了系統性能指標的要求。{
if (IsEmptyStr(sSearch)) return NULL;
if (IsEmptyStr(sFind)) return (char*)sSearch;
ULONG nOffset =0;
ULONG nIndex =0;
// ULONG nMaxLen = strlen(sSearch);
ULONG nLen = strlen(sFind);
// while(nOffset < nMaxLen)
while(sSearch[nOffset] != 0)
{
for(nIndex=0; nIndex<nLen; nIndex ++)
{
if(sSearch[nOffset + nIndex] != sFind[nIndex]) break;
}
if(nIndex == nLen) return (char*)(sSearch + nOffset);
int n = IsChineseChar(sSearch + nOffset);
if(n > 0)
{
// 漢字處理
// nOffset += n;
for(int ii=0; ii<n; ii++)
{
++nOffset;
if(sSearch[nOffset] == 0) break;
}
}
else
{
nOffset++;
}
}
return NULL;
}
這個問題提示我們,程序員應時刻關注代碼的性能問題,對核心代碼編寫者更是如此。