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

rob*_*oft 11 delphi string

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

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

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

当然,我们将分析程序的整体运行情况以了解我们已经做出了哪些不同,但是如果我们在第一个实例中对字符串传递的工作方式存在严重错误的话,那么实际尝试这一点毫无意义!

mgh*_*hie 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;
Run Code Online (Sandbox Code Playgroud)

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

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

  • var字符串参数实际上传递指向字符串变量的指针,而const字符串参数只传递字符串值.因此Var有一个额外的间接层,所以它应该比const慢. (7认同)

Pav*_*aev 9

const对于Delphi中的参数本质上意味着"我不会改变这一点,我也不在乎这是通过值还是通过引用传递 - 无论哪种效率最高都是我的".粗体部分很重要,因为它实际上是可观察的.考虑以下代码:

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.
Run Code Online (Sandbox Code Playgroud)

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

总结一下,const始终是传递任何类型参数的最有效方法,但是您不应该假设您是否拥有调用者传递的值的副本,或者对某些类型的引用(可能是由您可以调用的其他代码)位置.


Joh*_*ica 5

这确实是一条评论,但很长,所以请耐心等待。

关于按值传递的“所谓”字符串

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

只要您不在被调用的例程字符串传递的主体内更改它们,就需要 O(1) 时间(并且有一个小常量)

然而,当传递一个不带varorconst子句的字符串时,Delphi 做了三件事。

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

关于通过引用(指针)传递

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

  1. 字符串的引用计数不会增加。(微小的,微小的速度增加)
  2. 没有隐含的 try/finally 围绕例程放置,因为它不需要。这是const/var字符串参数执行速度更快的第 1 部分。
  3. 在例程中更改字符串时,不会复制实际字符串。对于const参数,编译器禁止字符串交替。这是为什么var/const字符串参数工作得更快的第 2 部分。
  4. 但是,如果您需要创建一个本地变量来分配字符串;Delphi 复制字符串 :-) 并放置一个隐式 try/finally 块,消除了const字符串参数99% 以上的速度增益。

希望这对这个问题有所启发。
免责声明:大部分信息来自这里这里这里