Delphi中的字符串:在简单情况下预分配内存以提高性能?
我是那些所谓的开发人员之一,他们在没有真正理解或思考基础知识的情况下使用了Delphi。在本例中,我指的是字符串 虽然我确实理解预分配内存如何能显著提高速度。我不明白如何在简单、真实的情况下使用它(对于TStringBuilder更是如此) 例如,假设我有递归搜索文件夹并将结果添加到哈希列表的代码:Delphi中的字符串:在简单情况下预分配内存以提高性能?,delphi,optimization,delphi-xe2,string-concatenation,Delphi,Optimization,Delphi Xe2,String Concatenation,我是那些所谓的开发人员之一,他们在没有真正理解或思考基础知识的情况下使用了Delphi。在本例中,我指的是字符串 虽然我确实理解预分配内存如何能显著提高速度。我不明白如何在简单、真实的情况下使用它(对于TStringBuilder更是如此) 例如,假设我有递归搜索文件夹并将结果添加到哈希列表的代码: var FilesList : TDictionary<String, Byte>; // Byte = (file = 0, folder = 1) // ---------
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;
var
文件列表:t字典;//字节=(文件=0,文件夹=1)
// ------------------------------------------------------------------------------ //
过程AddFolder(const-AFolderName:String);
变量
文件名:字符串;
阿汉德尔:坦德尔;
FindData:TWin32FindData;
开始
AHandle:=FindFirstFile(PChar(一个文件夹名+“*”),FindData);
如果(AHandle=无效的句柄值),则
出口
重复
如果(FindData.dwFileAttributes和FILE_ATTRIBUTE_DIRECTORY=0),则
开始
{添加一个文件。}
文件名:=FindData.cFileName;
添加(文件夹名+文件名,0);
结束
否则,如果((FindData.cFileName[0]'.')或不((FindData.cFileName[1]=#0)或(FindData.cFileName[1]='.')和(FindData.cFileName[2]=#0)),则
开始
文件名:=AFolderName+FindData.cFileName+'\';
添加(文件名,1);
AddFolder(文件名);
结束;
直到没有FindNextFile(AHandle,FindData);
Windows.FindClose(AHandle);
结束;
我不确定这是否是一个很好的例子,但在这种情况下,我不清楚如何将内存预先分配给变量FileName
来帮助提高执行速度,特别是我对其长度一无所知假设这是可能的,如何实现?
或者预分配技术是否仅在连接/构建字符串时有用?
关于我的问题的注释:
例如,直接连接字符串可能会很慢,因为字符串的内存会重新分配给所附加的每个片段。有时新的大小实际上可以就地容纳,但有时必须将数据复制到新位置,释放旧的缓冲区,等等。这需要时间
但是,通常情况下,除非您使用性能分析器或明确的计时语句验证了您确实存在性能问题,否则您不必担心这一点。基本上,您谈论的是这些连接:
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;
由于这三个值不同,因此不能仅使用一个表达式对其进行优化
所有功能预先计算最终长度,并在需要时进行适当的分配工作
在循环内部,您可以尝试自己进行AFolderName+FindData.cFileName+'\'
的预分配,并弹出AFolderName+FindData.cFileName
部分,但随后需要为然后
案例分配两次
因此,我认为您的代码无法得到进一步优化(即,您无法让它更好地执行一个数量级)。当然,您没有理解字符串的实际工作原理就逃脱了惩罚:Delphi在这方面非常优秀,它的字符串操作非常有效,它的内存管理器对于小内存块也非常有效。您可以使用Delphi进行很多操作,而不会出现字符串操作问题 有一些类的问题需要注意,特别是如果要重用正在查看的例程(库代码) 例如,这应始终升起一面旗帜:
Result := '';
for i:=1 to N do
Result := Result + Something; // <- Recursively builds the string, one-char-at-a-time
现在以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;
i、 e:构建一个复杂(可能很大)消息位的代码,然后您可以使用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;
var-Msg:TStringBuilder;
开始
Msg:=TStringBuilder.Create;
尝试
Msg.Append('Mr');
Msg.Append(MrName);
Msg.Append(#13#10);
如果有条件的话
Msg.Append('我们无法联系到您')
其他的
Msg.Append('我们需要让您知道');
Msg.Append(#13#10);
ShowMessage(Msg.ToString);//正如500个内部服务器错误所指出的那样,字符串的大部分问题都来自这样一个事实:编译器总是希望创建新内存,用新数据复制字符串,然后释放旧内存。开始合并FastMM的Delphi版本在这方面做得更好一些,但是在时间密集的过程中,任何时候都可以远离字符串,这是一件好事。简而言之,您的示例代码可能不会从任何技巧中受益。如果您对性能感兴趣,请始终分析应用程序以了解程序的执行时间。@Glen1234+1表示“如果您对性能感兴趣,请始终分析应用程序以了解程序的执行时间。”恐怕你们没有领会我的意思,我确实使用了我的探查器,我非常清楚我的应用程序中的弱点在哪里(并且仍在发现),我一直/正在尝试查看是否有改进的余地
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;