1. 程式人生 > >淺談MFC CSting類拼接效率

淺談MFC CSting類拼接效率

暮鼓集    行走集

2004年11月10日

在使用MFC開發時,字串類CString大概是使用最多的類之一,它使得對字串的處理變得很方便。但是,在一些場合下,CString存在一些明顯的效率問題。

如下面的程式碼:

CString a;

CString b = "Hello world!";

INT n = b.GetLength();
   
for( INT i = 0; i < n; i++ )
{
    a += b[i];
    a += TEXT("\r\n");
}    

這段程式碼將字串b中的每個字元拼接到字串a,並在後面插入0x0D 0x0A。

當b為”Hello, world!”這樣簡單的字串時,程式的執行起來尚無問題。但是若b的字串很長的時候,就會出現明顯的效率問題。我使用自己的計算器做了一系列測試,得出如下的結果

字串b的大小 執行時間

-------------─ 

  001k    0.01s

  010k    0.41s

  020k    1.68s

  040k    6.02s

  080k    33.56s

  100k    80.46s

可以說,當b的大小超過20K時,已經會出現明顯的延遲,當超過40k,使用者就會難以忍受了。

為甚麼會出現這樣的情況?我們會想到,當被拼接的字串b越長,要複製和插入到字串a的字就越多,迴圈的次數就越多,導致時間的增加。但是這只是一個方面,更為重要的原因在於--

CString的拼接操作符”+”看似簡單,卻要完成相當多的操作。首先會對CString物件(例子中字串a)的資料緩衝區進行重新分配,接著找到字串的結束位置,將要拼接的串複製到這個位置開始的緩衝區中。隨著拼接的進行,CString物件的字串隨之變長,這些操作所需要的時間開銷也會逐步增加。

如何解決這個問題呢,可以考慮,如果字串a的資料緩衝區一開始就固定下來,那麼此後在每次拼接前就不必去重新分配了;同時,用一個指標來指向已有字串的結束位置,就不必去尋找這個位置了。因此,將程式改寫如下。

CString a;
PTCHAR    buf, p;

CString b = "Hello, world!";
INT n = b.GetLength();
PTCHAR  q = b.GetBuffer(n);

p = buf = (PTCHAR)malloc( sizeof(TCHAR) * (n*3+1) );

while( *q )
{
    *p++ = *q++;
    *p++ = '\r';
    *p++ = '\n';
}

*p = '\0';
   
a = buf;
free( buf );

再次測試後的結果是

字串b的大小 執行時間

-------------─ 

  040k    0.01s

  100k    0.01s

  500k    0.06s

效率的提升是多麼明顯。

題外話,這個例子的一個啟示是,當遇到程式執行出現延遲的情形,可以首先從程式本身找原因,想辦法替換掉那些效率不高的元操作。如果確認已經優化過了,還可以考慮其它的方法如多執行緒,來完成任務。