15

一年前,我将我的程序从 Delphi 4 转换为 Delphi 2009,主要是为了跳转到 Unicode,同时也是为了获得这些年来 Delphi 改进的好处。

因此,我的代码当然都是遗留代码。它使用的短字符串现在都方便地变成了长 Unicode 字符串,并且我已经将所有旧的 ANSI 函数更改为新的等效函数。

但是在 Delphi 2009 中,他们引入了 TStringBuilder 类,大概是仿照 .NET 的 StringBuilder 类。

我的程序进行了大量的字符串处理和操作,并且可以一次将数百兆字节的大字符串加载到内存中以供使用。

我对Delphi对TStringBuilder的实现了解不多,但听说它的一些操作比使用默认的字符串操作要快。

我的问题是是否值得我努力并转换我的标准字符串以使用 TStringBuilder 类。这样做我会得到什么,失去什么?


感谢您的回答并让我得出结论,除非需要 .NET 兼容性,否则不要打扰。

Jolyon Smith在他关于 Delphi 2009 String Performance 的博客中指出:

但在我看来,TStringBuilder 似乎主要是作为 .NET 兼容性夹具存在的,而不是为 Win32 应用程序的开发人员提供任何真正的好处,希望或需要单一来源 Win32/.NET 代码库的开发人员可能例外字符串处理性能不是问题。

4

6 回答 6

13

据我所知,引入 TStringBuilder 只是为了与 .NET 和 Java 相提并论,它似乎比任何重大进步都更像是一种勾选框类型功能。

共识似乎是 TStringBuilder 在某些操作中更快,但在其他操作中更慢。

您的程序听起来很有趣,可以与 TStringBuilder 进行前后比较,但除了作为学术练习之外,我不会这样做。

于 2009-10-18T19:28:58.543 回答
11

基本上,我使用这些习语来构建字符串。最重要的区别是:

对于复杂的构建模式,第一个使我的代码更清晰,第二个只有当我添加行并且经常包含许多Format调用时。

当格式模式很重要时,第三个使我的代码更清晰。

我只在表达式很简单时才使用最后一个。

前两个成语之间的更多区别:

  • TStringBuilder有很多重载,如果你想添加像can 这样的行Append,还有AppendLine (只有两个重载)TStringList.Add
  • TStringBuilder使用超容量方案重新分配底层缓冲区,这意味着对于大缓冲区和频繁追加,它可能比TStringList
  • 要获取TStringBuilder内容,您必须调用可以减慢速度的ToString方法。

所以:速度不是选择你的字符串附加习语的最重要的问题。可读的代码是。

于 2009-10-19T09:29:02.857 回答
10

我试图改进一个解析文本文件(1.5GB)的旧例程。该例程非常愚蠢,它正在构建这样的字符串:Result:= Result+ buff[i];

因此,我认为 TStringBuilder 将显着提高速度。事实证明,“哑”代码实际上比使用 TStringBuilder 的“改进”版本快 114%。

因此,从字符构建字符串并不是一个可以使用 TStringBuilder 提高速度的地方。


我的 StringBuilder(如下)比经典的 s:= s+ chr快 184.82 倍(是的 184 !!!!!!) 。(4MB 字符串的实验)

经典 s:= s + c
时间:8502 ms

procedure TfrmTester.btnClassicClick(Sender: TObject);
VAR
   s: string;
   FileBody: string;
   c: Cardinal;
   i: Integer;
begin
 FileBody:= ReadFile(File4MB);
 c:= GetTickCount;
 for i:= 1 to Length(FileBody) DO
  s:= s+ FileBody[i];
 Log.Lines.Add('Time: '+ IntToStr(GetTickCount-c) + 'ms');     // 8502 ms
end;

预缓冲

Time:  
     BuffSize= 10000;       // 10k  buffer = 406ms
     BuffSize= 100000;      // 100k buffer = 140ms
     BuffSize= 1000000;     // 1M   buffer = 46ms

代码:

procedure TfrmTester.btnBufferedClick(Sender: TObject);
VAR
   s: string;
   FileBody: string;
   c: Cardinal;
   CurBuffLen, marker, i: Integer;
begin
 FileBody:= ReadFile(File4MB);
 c:= GetTickCount;

 marker:= 1;
 CurBuffLen:= 0;
 for i:= 1 to Length(FileBody) DO
  begin
   if i > CurBuffLen then
    begin
     SetLength(s, CurBuffLen+ BuffSize);
     CurBuffLen:= Length(s)
    end;
   s[marker]:= FileBody[i];
   Inc(marker);
  end;

 SetLength(s, marker-1); { Cut down the prealocated buffer that we haven't used }  
 Log.Lines.Add('Time: '+ IntToStr(GetTickCount-c) + 'ms');
 if s <> FileBody
 then Log.Lines.Add('FAILED!');
end;

预缓冲,作为类

Time:    
 BuffSize= 10000;       // 10k  buffer = 437ms       
 BuffSize= 100000;      // 100k buffer = 187ms        
 BuffSize= 1000000;     // 1M buffer = 78ms     

代码:

procedure TfrmTester.btnBuffClassClick(Sender: TObject);
VAR
   StringBuff: TCStringBuff;
   s: string;
   FileBody: string;
   c: Cardinal;
   i: Integer;
begin
 FileBody:= ReadFile(File4MB);
 c:= GetTickCount;

 StringBuff:= TCStringBuff.Create(BuffSize);
 TRY
   for i:= 1 to Length(FileBody) DO
    StringBuff.AddChar(filebody[i]);
   s:= StringBuff.GetResult;
 FINALLY
  FreeAndNil(StringBuff);
 END;

 Log.Lines.Add('Time: '+ IntToStr(GetTickCount-c) + 'ms');
 if s <> FileBody
 then Log.Lines.Add('FAILED!');
end;

这是课程:

{ TCStringBuff }

constructor TCStringBuff.Create(aBuffSize: Integer= 10000);
begin
 BuffSize:= aBuffSize;
 marker:= 1;
 CurBuffLen:= 0;
 inp:= 1;
end;

function TCStringBuff.GetResult: string;
begin
 SetLength(s, marker-1);                    { Cut down the prealocated buffer that we haven't used }
 Result:= s;
 s:= '';         { Free memory }
end;

procedure TCStringBuff.AddChar(Ch: Char);
begin
 if inp > CurBuffLen then
  begin
   SetLength(s, CurBuffLen+ BuffSize);
   CurBuffLen:= Length(s)
  end;

 s[marker]:= Ch;
 Inc(marker);
 Inc(inp);
end;

结论: 如果您有大(超过 10K)字符串,请停止使用 s:= s + c 。即使您有小字符串但您经常这样做也可能是真的(例如,您有一个函数正在对小字符串进行一些字符串处理,但您经常调用它)。

_

PS:您可能还想看看这个:https ://www.delphitools.info/2013/10/30/efficient-string-building-in-delphi/2/

于 2015-08-07T12:56:23.983 回答
8

引入TStringBuilder仅仅是为了为应用程序在DelphiDelphi.NET中执行字符串处理提供源代码兼容机制。你在 Delphi 中牺牲了一些速度来换取Delphi.NET中的一些潜在的显着好处

.NET中的StringBuilder概念解决了该平台上字符串实现的性能问题,而Delphi(本机代码)平台根本没有这些问题。

如果您不编写需要为本地代码和Delphi.NET编译的代码,那么根本没有理由使用TStringBuilder

于 2009-10-18T19:54:06.627 回答
7

根据Marco Cantu的说法,不是为了速度,但您可能会获得更清晰的代码以及与 .Net 更好的代码兼容性。这里(以及这里的一些更正)另一个速度测试 TStringBuilder 没有更快。

于 2009-10-18T19:28:21.957 回答
6

TStringBuilder 基本上只是一个模仿功能,就像 LachlanG 所说的那样。它在 .NET 中是必需的,因为 CLR 字符串是不可变的,但 Delphi 没有这个问题,因此它并不需要字符串生成器作为解决方法。

于 2009-10-18T19:52:34.290 回答