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