德尔福;传递const字符串与传递var字符串的性能

时间:2009-12-23 07:27:32

标签: delphi string

快一点;我是否正确认为将字符串传递给方法'作为CONST'涉及比将字符串作为'VAR'传递更多的开销?如果字符串参数声明为CONST,编译器会让Delphi复制字符串然后传递副本吗?

问题的原因有点乏味;我们有一个传统的 Delphi 5 实用程序,其日期确实有编号(替换正在开发中)。它执行大量的字符串处理,经常在各种函数和过程之间传递1-2Kb字符串。在整个代码中,已经遵守了使用CONST或VAR传递参数(取决于手头的工作)的“正确”观察。我们只是在寻找一些“快速获胜”,可能会缩短执行时间几微秒,让我们度过难关,直到新版本准备就绪。我们想过将内存管理器从默认的Delphi 5更改为FastMM,我们也想知道是否值得改变字符串传递的方式 - 因为代码工作正常,字符串传递为const,我们不如果我们将这些声明更改为var,则会发现问题 - 该方法中的代码不会更改字符串。

但实际上它真的会有什么不同吗? (该程序实际上只对这些1kb + ish字符串进行了大量处理;在高峰时间每分钟有数百个字符串)。在重写时,这些字符串被保存在对象/类变量中,因此它们实际上并没有以相同的方式被复制/传递,但在遗留代码中,它非常“老派”帕斯卡。

当然,我们会分析程序的整体运行情况,看看我们已经做了哪些不同但是如果我们在第一次实例中字符串传递的工作原理完全错误的话,实际尝试这一点毫无意义!

5 个答案:

答案 0 :(得分:12)

不,在您的案例中使用constvar之间不应有任何性能差异。在这两种情况下,都会将指向字符串的指针作为参数传递。如果参数为const,则编译器只是不允许对其进行任何修改。请注意,如果您遇到棘手问题,这并不排除对字符串的修改:

procedure TForm1.Button1Click(Sender: TObject);
var
  s: string;
begin
  s := 'foo bar baz';
  UniqueString(s);
  SetConstCaption(s);
  Caption := s;
end;

procedure TForm1.SetConstCaption(const AValue: string);
var
  P: PChar;
begin
  P := PChar(AValue);
  P[3] := '?';
  Caption := AValue;
end;

这实际上会改变调用方法中的本地字符串变量,证明只传递指向它的指针。

但绝对使用FastMM4,它应该会产生更大的性能影响。

答案 1 :(得分:9)

对于Delphi中的参数,

const实质上意味着“我不会改变它,我也不在乎这是通过值还是通过引用传递 - 无论哪个是最有效率的我很好“。粗体部分很重要,因为它实际上是可观察的。请考虑以下代码:

type TFoo =
  record
    x: integer;
    //dummy: array[1..10] of integer;
  end;

procedure Foo(var x1: TFoo; const x2: TFoo);
begin
  WriteLn(x1.x);
  WriteLn(x2.x);

  Inc(x1.x);
  WriteLn;

  WriteLn(x1.x);
  WriteLn(x2.x);
end;

var
  x: TFoo;
begin
  Foo(x, x);
  ReadLn;
end.

这里的技巧是我们将varconst都传递给同一个变量,这样我们的函数就可以通过一个参数进行变异,看看这是否会影响另一个变量。如果您使用上面的代码进行尝试,则会看到x1.x内的Foo递增不会更改x2.x,因此x2按值传递。但是尝试取消注释TFoo中的数组声明,以便它的大小变大,并再次运行它 - 你会看到x2.x现在如何别名x1.x,所以我们有传递现在引用x2

总而言之,const始终是传递任何类型参数的最有效方式,但您不应对是否拥有调用者传递的值的副本做出任何假设,或者对某些人的引用(可能由您可能调用的其他代码发生变异)的位置。

答案 2 :(得分:4)

这真是一个评论,但是很长一段时间,请耐心等待。

关于'所谓的'字符串传递值

Delphi 始终通过引用传递stringansistring (不包括WideStrings和ShortStrings)作为指针。
所以字符串永远不会通过值传递 这可以通过传递100MB字符串轻松测试。

只要你没有在被调用的例程字符串体内更改它们,就需要花费O(1)时间(并且在那里有一个小常量)

但是,当传递没有varconst子句的字符串时,Delphi会做三件事。

  1. 增加字符串的引用次数。
  2. 在过程周围放置一个隐式的try-finally块,因此字符串参数的引用计数会减少。
  3. 当字符串被更改时(并且只有那时)Delphi会复制字符串,减少传递字符串的引用计数,并在例程的其余部分使用副本。
    这样做会假装pass by value
  4. 关于通过引用传递(指针)

    当字符串作为constvar传递时,Delphi也会传递引用(指针),但是:

    1. 字符串的引用计数不会增加。 (微小,极小的速度增加)
    2. 没有隐含的try / finally被放在例程中,因为它不需要。这是为什么const/var字符串参数执行得更快的第1部分。
    3. 在例程中更改字符串时,不复制实际字符串已更改。对于const参数,编译器禁止字符串替换。这是var/const字符串参数工作速度更快的原因的第2部分。
    4. 如果但是你需要创建一个本地var来分配字符串; Delphi复制字符串:-)并放置一个隐式try / finally块,消除const字符串参数的99%以上的速度增益。
    5. 希望这能为这个问题提供一些启示 免责声明:大部分信息来自hereherehere

答案 3 :(得分:3)

使用 const afaik时,编译器不会复制该字符串。使用 const 可以节省为您使用的字符串递增/递减refcounter的开销。

通过将memorymanager升级到FastMM,您将获得更大的性能提升,并且,因为您对字符串做了很多工作,请考虑使用FastCode库。

答案 4 :(得分:2)

Const已经是将参数传递给函数的最有效方法。它避免了创建副本(默认值,按值)或甚至传递指针(var,通过引用) 对于字符串尤其如此,当计算能力有限且不被浪费时(因此称为“旧学校”标签),确实是要走的路。

IMO,const应该是默认约定,由程序员根据值或var真正需要时更改它。这可能更符合Pascal的整体安全性(如限制自己在脚下射击的机会)。

我的2¢......