首页 文章

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

提问于
浏览
10

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

这个问题的原因有点乏味;我们有一个遗留的 Delphi 5 实用程序,其日期确实已编号(替换正在开发中) . 它执行大量的字符串处理,经常在各种函数和过程之间传递1-2Kb字符串 . 在整个代码中,已经遵守了使用CONST或VAR传递参数(取决于手头的工作)的观察结果 . 我们're just looking for a few '快速获胜' that might shave a few microseconds off the execution time, to tide us over until the new version is ready. We thought of changing the memory manager from the default Delphi 5 one to FastMM, and we also wondered if it was worth altering the way the strings are passed around - because the code is working fine with the strings passed as const, we don'如果我们将这些声明更改为var,则会看到问题 - 该方法中的代码不会更改字符串 .

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

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

5 回答

  • 4

    不,在您的情况下使用 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,它应该会产生更大的性能影响 .

  • 9

    对于Delphi中的参数来说 const 本质上意味着“我不会改变这个, and I also don't care if this is passed by value or by reference - 无论哪种效率最高,我都可以” . 粗体部分很重要,因为它实际上是可观察的 . 考虑以下代码:

    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 ,这样我们的函数可以通过一个参数进行变异,看看这是否会影响另一个变量 . 如果您使用上面的代码尝试它,您会看到 Foo 内的 x1.x 递增不会改变 x2.x ,因此 x2 按值传递 . 但是尝试取消注释 TFoo 中的数组声明,以便它的大小变大,并再次运行它 - 你会看到 x2.x 现在如何别名 x1.x ,所以我们现在为 x2 传递参考!

    总而言之, const 始终是传递任何类型参数的最有效方法,但是您不应该对是否拥有调用者传递的值的副本或对某些类型的引用(可能是变异的)做出任何假设 . 通过其他代码,你可以称之为)位置 .

  • 3

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

    About 'so called' string passing by value

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

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

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

    • 增加字符串的引用计数 .

    • 在过程周围放置了一个隐式的try-finally块,因此字符串参数的引用计数减少了 .

    • 当字符串发生变化(并且只有这样)时,Delphi会复制字符串,减少传递字符串的引用计数,并在例程的其余部分中使用该副本 .
      这样做会伪造一个 pass by value .

    About passing by reference (pointer)

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

    • 字符串的引用计数不会增加 . (微小,极小的速度增加)

    • 没有隐含的try / finally被放在例程中,因为它不需要 . 这是为什么 const/var 字符串参数执行的第1部分快点 .

    • 在例程中更改字符串时, no copy is make 将更改实际字符串 . 对于 const 参数,编译器禁止字符串替换 . 这是为什么 var/const 字符串参数工作得更快的第2部分 .

    • 但是,如果您需要创建一个本地var来分配字符串; Delphi复制字符串:-)并放置一个隐式的try / finally块,消除了 const 字符串参数的99%的速度增益 .

    希望这能为这个问题带来一些启示 .
    免责声明:大部分信息来自hereherehere

  • 12

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

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

  • 2

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

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

    我的2¢......

相关问题