【问题标题】:Are there any advantages in using const parameters with an ordinal type?将 const 参数与序数类型一起使用有什么好处吗?
【发布时间】:2009-10-21 13:57:11
【问题描述】:

我知道将字符串参数标记为const 可以产生巨大的性能差异,但是序数类型呢?让他们const 有什么好处吗?

我在处理字符串时一直使用const 参数,但从不用于IntegerPointer、类实例等。

在使用const 时,我经常需要创建额外的临时变量来替换现在写保护的参数,所以我想知道:将序数参数标记为const 有什么好处吗?

【问题讨论】:

    标签: delphi


    【解决方案1】:

    您需要了解原因,以避免“货物崇拜编程”。将字符串标记为 const 会产生性能差异,因为您不再需要在字符串上使用互锁的递增和递减引用计数,随着时间的推移,这种操作实际上会变得更昂贵,而不是更少,因为更多的核心意味着必须做更多的工作来保持原子操作的同步。这是安全的,因为编译器强制执行“此变量不会被更改”约束。

    对于通常为 4 字节或更小的序数,没有性能提升。仅当您使用大于 4 个字节的值类型(例如数组或记录)或引用计数类型(例如字符串和接口)时,才使用 const 作为优化。

    不过,还有另一个重要优势:代码可读性。如果您将某些内容作为 const 传递并且它对编译器没有任何影响,它仍然可以对 you 产生影响,因为您可以阅读代码并了解其意图最重要的是不要修改它。如果您之前没有看过代码(其他人编写的),或者您在很长一段时间后重新使用它并且不记得您最初编写它时的确切想法,那么这可能很重要。

    【讨论】:

    • 您的第一段关于字符串是错误的。字符串始终作为其四字节指针表示形式传递。 Const 禁止函数的序言和尾声中更新字符串引用计数的代码。接口参数和动态数组也是如此。同样,记录作为指针传递。 Const 仅抑制将记录复制到函数的本地堆栈的序言代码。换句话说,const 在函数的 调用者没有效果。它只影响呼叫的接收者。
    • @Rob:无法编辑 cmets 真是太可惜了。很容易误读您的评论,就好像您在说记录总是作为指针传递一样。
    • 如果你认为我是这么说的,那么你没有误读任何东西,Wouter。大于指针的记录总是作为指针传递。调用者在堆栈上放置一个指针。如果参数不是 const,则调用的 receiver 会复制记录。这与字符串相同。调用者传递一个指针,接收者复制一份。不过,在字符串的情况下,复制只是简单地更新引用计数。
    • const 与字符串一起使用不会阻止复制!!使用const 只能防止增加引用计数以及隐式try..finally 块。 try..finally 有点贵,但远没有副本那么贵。
    • 哇,真的是我写的吗? Derp...好的,现在修好了。
    【解决方案2】:

    您不能不小心将它们视为var 参数并编译您的代码。这样你的意图就很清楚了。

    【讨论】:

    • 好点,但不指定参数 var 是否足以说明意图?
    • Smasher,编译器不会区分 var 和默认约定,只要您可以对方法中的参数执行什么操作。
    • 即使目前看起来很清楚这是一个您不打算更改的值,但可能不会在六个月后重新使用它,或者当维护程序员必须查看时在您的代码中。
    • 梅森,这正是我的意思。让维护者的思想与创建者的思想相同。维护者可以根据需要更改约定,但她应该了解创建者的意图。
    • 是的。我可能应该在最后一条评论前面放一个@Smasher。 :p
    【解决方案3】:

    声明序数类型 const 没有任何区别,因为它们无论如何都会被复制(按值调用),因此对变量的任何更改都不会影响原始变量。

    procedure Foo (Val : Integer)
    begin
    Val := 2;
    end;
    ...
    SomeVar := 3;
    Foo (SomeVar);
    Assert (SomeVar = 3);
    

    恕我直言,声明序数类型 const 没有任何意义,正如您所说,需要您经常引入局部变量。

    【讨论】:

    • 克雷格有一个有效的观点。有充分的理由将序数声明为 const;只是不是出于性能原因。
    【解决方案4】:

    这取决于您的日常活动有多复杂以及如何使用它。如果它在很多地方使用并且要求值保持不变,请将其声明为“const”以使其清晰且安全。对于字符串类型,如果声明为“const”,则存在一个错误(对于 Delphi 7,因为我对此感到困惑)会导致内存损坏。下面是示例代码

    type
      TFoo = class 
      private
         FStr: string;
      public
         procedure DoFoo(const AStr: string);
         begin
            FStr := AStr; //the trouble code 1
            ......
         end;
         procedure DoFoo2;
         begin
            .....
            DoFoo(FStr);  //the trouble code 2
         end;
      end;
    

    【讨论】:

      【解决方案5】:

      使用带字符串的 Const 可以显着提高速度:

      function test(k: string): string;
      begin
        Result := k;
      end;
      
      function test2(Const k: string): string;
      begin
        Result := k;
      end;
      
      function test3(Var k: string): string;
      begin
        Result := k;
      end;
      
      procedure TForm1.Button1Click(Sender: TObject);
      Var a: Integer;
          s,b: string;
          x: Int64;
      begin
        s := 'jkdfjklf lkjj3i2ej39ijkl  jkl2eje23 io3je32 e832 eu283 89389e3jio3 j938j 839 d983j9';
      
        PerfTimerInit;
        for a := 1 to 10000000 do
         b := test(s);
        x := PerfTimerStopMS;
        Memo1.Lines.Add('default: '+x.ToString);
      
        PerfTimerInit;
        for a := 1 to 10000000 do
         b := test2(s);
        x := PerfTimerStopMS;
        Memo1.Lines.Add('const: '+x.ToString);
      
        PerfTimerInit;
        for a := 1 to 10000000 do
         b := test3(s);
        x := PerfTimerStopMS;
        Memo1.Lines.Add('var: '+x.ToString);
      end;
      

      默认值:443 常量:320 变量:325

      默认值:444 常量:303 变量:310

      默认值:444 常量:302 变量:305

      整数也一样:

      默认值:142 常量:13 变量:14

      有趣的是,在 64 位中,字符串似乎几乎没有区别(默认模式只比 Const 慢一点):

      默认值:352 常量:313 变量:314

      【讨论】:

        猜你喜欢
        • 2016-02-10
        • 2020-11-05
        • 1970-01-01
        • 1970-01-01
        • 2012-01-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-16
        相关资源
        最近更新 更多