2013-01-08 40 views
3

我是那些所謂的開發人員之一,他們沒有真正理解甚至沒有考慮過基本知識,就與Delphi一起獲得了方向。在這種情況下,我說的是字符串Delphi中的字符串:在簡單情況下預分配內存以提高性能?

雖然我明白預分配內存可以導致顯着的速度增益。我不明白如何在簡單的現實世界中使用它(這在TStringBuilder中更是如此)。

例如,假設我有這樣的代碼,遞歸搜索文件夾&添加結果到散列表:

var 
    FilesList : TDictionary<String, Byte>; // Byte = (file = 0, folder = 1) 

// ------------------------------------------------------------------------------ // 
procedure AddFolder(const AFolderName : String); 
var 
    FileName : String; 
    AHandle : THandle; 
    FindData : TWin32FindData; 
begin 
    AHandle := FindFirstFile(PChar(AFolderName + '*'), FindData); 
    if (AHandle = INVALID_HANDLE_VALUE) then 
     Exit; 

    repeat 
      if (FindData.dwFileAttributes And FILE_ATTRIBUTE_DIRECTORY = 0) then 
      begin 
       { Add a file. } 
       FileName := FindData.cFileName; 
       FilesList.Add(AFolderName + FileName, 0); 
      end 
      else if ((FindData.cFileName[0] <> '.') OR Not ((FindData.cFileName[1] = #0) OR (FindData.cFileName[1] = '.') And (FindData.cFileName[2] = #0))) then 
      begin 
       FileName := AFolderName + FindData.cFileName + '\'; 
       FilesList.Add(FileName, 1); 
       AddFolder(FileName); 
      end; 
    until Not FindNextFile(AHandle, FindData); 

    Windows.FindClose(AHandle); 
end; 

我不知道這是否是一個很好的例子,但在這種情況下,它的我不清楚如何預先分配內存到變量FileName將有助於提高執行速度,特別是我對它的長度一無所知。 假設這是可能的,怎麼樣?

是隻在連接/建立字符串時有用的預分配技術嗎?


說明我的問題:

  1. 的問題主要是爲XE2,但隨時引用其他版本的Delphi,因爲我敢肯定,其他開發人員從共享中受益智慧(也就是說,假設mods不會將它刪除爲聊天或主觀)

  2. 我更感興趣的是簡單的日常案例其中一個需要通過優化字符串內存預分配在非常大的循環/大量數據中進行微優化。

+2

由於500內部服務器錯誤狀態,大部分字符串問題都是因爲編譯器總是希望創建新內存,將字符串複製到新數據中,然後釋放舊內存。開始整合FastMM的Delphi版本對此做了一些改進,但只要您能夠在時間密集型的過程中遠離字符串,這是件好事。總之,你的示例代碼可能不會從任何技巧中受益。如果您對性能感興趣,請始終對您的應用進行配置,以查明您的程序在哪裏執行其執行時間。 – Glenn1234

+0

@ Glenn1234 +1「如果您對性能感興趣,請始終配置您的應用,以查明您的程序在哪裏花費其執行時間。」 – RobertFrank

+1

我恐怕你們錯過了我的觀點,我**使用我的分析器,並且我完全知道我的應用程序中的薄弱點(並且仍然在發現),我正在/正在嘗試查看是否有空間對於字符串分配方面的改進(通常來說,不僅僅是我發佈的示例代碼),希望這可以闡明我的意圖 – TheDude

回答

4

當然,你不理解字符串是如何工作的:Delphi在這方面非常重要,它的字符串操作非常有效,而且它的內存管理器對於小內存塊也非常有效。你可以用Delphi來做ALOT,而且不會有字符串操作的問題。

有一些類型的問題需要注意,特別是如果您正在查看的例程要重用(庫代碼)。

例如,這應該總是提出一個標誌:

Result := ''; 
for i:=1 to N do 
    Result := Result + Something; // <- Recursively builds the string, one-char-at-a-time 

即使可能與德爾福飛,如果不是經常使用或使用,時間並不重要。儘管如此那種代碼應該因此字符串的整個(可能)長度進行優化是預分配的,則在然後結束脩整:

SetLength(Result, Whatever); 
for i:=1 to N do 
    Result[i] := SomeChar; 
SetLength(Result, INowKnowTheLength); 

現在對於其中TStringBuilder照射的例子。如果你有這樣的事情:

var Msg: string; 
begin 
    Msg := 'Mr ' + MrName + #13#10; 
    if SomeCondition then 
    Msg := Msg + 'We were unable to reach you' 
    else 
    Msg := Msg + 'We need to let you know'; 
    Msg := Msg + #13#10 
end; 

即:即建立消息的一個複雜的(也可能是大)位,則代碼,您可以輕鬆使用TStringBuilder優化它:

var Msg: TStringBuilder; 
begin 
    Msg := TStringBuilder.Create; 
    try 
    Msg.Append('Mr '); 
    Msg.Append(MrName); 
    Msg.Append(#13#10); 
    if SomeCondition then 
     Msg.Append('We were unable to reach you') 
    else 
     Msg.Append('We need to let you know'); 
    Msg.Append(#13#10); 
    ShowMessage(Msg.ToString); // <- Gets the whole string 
    finally Msg.Free; 
    end; 
end; 

任何方式總是平衡寫作的難易程度,易於維護和表現的真正好處。不要超出編寫代碼的自然限制:優化字符串生成例程的速度要快於硬盤寫入的速度,這是浪費時間。優化一些GUI代碼以1ms(而不是20ms)生成一條消息也是浪費精力 - 用戶永遠不會知道你的代碼快了20倍,它就像瞬間一樣。

9

直連字符串連接(例如)可能會很慢,因爲字符串的內存是爲每個附加的塊重新分配的。有時新的尺寸實際上可以容納在適當的位置,但有時候數據必須被複制到新的位置,舊的緩衝區被釋放,等等。這需要時間。

儘管如此,除非您使用性能分析器或明確的時序聲明來驗證事實上確實存在性能問題,否則這應該與您無關。

+0

是的我知道,我相信[這澄清](http://stackoverflow.com/questions/14207000/strings-in-delphi-pre-allocate-memory-to-increase-performance-in-simple-cases#comment19707738_14207000 ) 我的問題。我想我正在嘗試改進一些無法改進的東西...... – TheDude

3

基本上就是這些串連你正在談論:

AFolderName + '*' 
AFolderName + FindData.cFileName 
AFolderName + FindData.cFileName + '\' 

第一個是做一次,循環執行或者第二和第三。

在System.pas這些方法可以用於內部3行:

procedure _UStrCat3(var Dest: UnicodeString; const Source1, Source2: UnicodeString); 
procedure _UStrCat3(var Dest: UnicodeString; const Source1, Source2: UnicodeString); 
procedure _UStrCatN(var Dest: UnicodeString; ArgCnt: Integer; const Strs: UnicodeString); varargs; 

由於3倍的值是不同的,則可以不使用僅一個表達優化它。

所有函數都會預先計算最終長度,並在需要時進行適當的分配工作。

在循環中,你可以嘗試做AFolderName + FindData.cFileName + '\'自己的預分配,並扣了AFolderName + FindData.cFileName部分,但你需要2個分配爲then情況。

所以我認爲你的代碼不能進一步優化(即你不能讓它執行更好的數量級)。

相關問題