就任何其他不同环境下这个简单的本地化性能测试得出的结果的有效性寻求第二意见

发布于 2024-12-25 05:08:14 字数 2301 浏览 0 评论 0原文

我的设置

  • 操作系统:Windows 7 SP1(32 位)
  • RAM:4 Go
  • 处理器:Intel Pentium D 3.00 GHz
  • Delphi XE

我的简单测试

我执行了运行以下命令的测试程序:

program TestAssign;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Diagnostics;

type
  TTestClazz = class
  private
    FIntProp: Integer;
    FStringProp: string;
  protected
    procedure SetIntProp(const Value: Integer);
    procedure SetStringProp(const Value: string);
  public
    property  IntProp: Integer   read FIntProp write SetIntProp;
    property  StringProp: string read FStringProp write SetStringProp;
  end;

{ TTestClazz }

procedure TTestClazz.SetIntProp(const Value: Integer);
begin
  if FIntProp <> Value then
    FIntProp := Value;
end;

procedure TTestClazz.SetStringProp(const Value: string);
begin
  if FStringProp <> Value then
    FStringProp := Value;
end;

var
  i, j: Integer;
  stopw1, stopw2 : TStopwatch;
  TestObj: TTestClazz;

begin
  ReportMemoryLeaksOnShutdown := True;
  //
  try
    TestObj := TTestClazz.Create;
    //
    try
      j := 10000;

      while j <= 100000 do
      begin
        ///
        /// assignement
        ///
        stopw1 :=  TStopwatch.StartNew;
        for i := 0 to j do
        begin
          TestObj.FIntProp := 666;
          TestObj.FStringProp := 'Hello';
        end;
        stopw1.Stop;

        ///
        /// property assignement using Setter
        ///
        stopw2 := TStopwatch.StartNew;
        for i := 0 to j do
        begin
          TestObj.IntProp := 666;
          TestObj.StringProp := 'Hello';
        end;
        stopw2.Stop;

        ///
        /// Log results
        ///

        Writeln(Format('Ellapsed time for %6.d loops: %5.d %5.d', [j, stopw1.ElapsedMilliseconds, stopw2.ElapsedMilliseconds]));

        //
        Inc(j, 5000);
      end;
      //
      Writeln('');
      Write('Press Return to Quit...');

      Readln;
    finally
      TestObj.Free
    end
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

我的(临时)结论

看来:

  • 在某些条件下使用具有属性的 Setter 是值得的
  • 调用方法和执行条件测试的开销比赋值花费的时间更少。

我的问题

这些发现在任何其他不同环境下有效还是仅在本地环境下有效(例外)?

My setting:

  • OS: Windows 7 SP1 (32 bits)
  • Ram: 4 Go
  • Processor: Intel Pentium D 3.00 GHz
  • Delphi XE

My simple test:

I performed a test running the following program:

program TestAssign;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Diagnostics;

type
  TTestClazz = class
  private
    FIntProp: Integer;
    FStringProp: string;
  protected
    procedure SetIntProp(const Value: Integer);
    procedure SetStringProp(const Value: string);
  public
    property  IntProp: Integer   read FIntProp write SetIntProp;
    property  StringProp: string read FStringProp write SetStringProp;
  end;

{ TTestClazz }

procedure TTestClazz.SetIntProp(const Value: Integer);
begin
  if FIntProp <> Value then
    FIntProp := Value;
end;

procedure TTestClazz.SetStringProp(const Value: string);
begin
  if FStringProp <> Value then
    FStringProp := Value;
end;

var
  i, j: Integer;
  stopw1, stopw2 : TStopwatch;
  TestObj: TTestClazz;

begin
  ReportMemoryLeaksOnShutdown := True;
  //
  try
    TestObj := TTestClazz.Create;
    //
    try
      j := 10000;

      while j <= 100000 do
      begin
        ///
        /// assignement
        ///
        stopw1 :=  TStopwatch.StartNew;
        for i := 0 to j do
        begin
          TestObj.FIntProp := 666;
          TestObj.FStringProp := 'Hello';
        end;
        stopw1.Stop;

        ///
        /// property assignement using Setter
        ///
        stopw2 := TStopwatch.StartNew;
        for i := 0 to j do
        begin
          TestObj.IntProp := 666;
          TestObj.StringProp := 'Hello';
        end;
        stopw2.Stop;

        ///
        /// Log results
        ///

        Writeln(Format('Ellapsed time for %6.d loops: %5.d %5.d', [j, stopw1.ElapsedMilliseconds, stopw2.ElapsedMilliseconds]));

        //
        Inc(j, 5000);
      end;
      //
      Writeln('');
      Write('Press Return to Quit...');

      Readln;
    finally
      TestObj.Free
    end
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

My (provisionnal) conclusion:

It seems that:

  • It's worth using Setter with property under some condition
  • The overhead of calling a method and performing a conditional test take less time than an assignement.

My question:

Are those findings valid under any other diffrent setting or just localized ones (exception)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

同展鸳鸯锦 2025-01-01 05:08:14

我会提出以下意见:

  1. 是否使用 setter 的决定应该基于代码维护、正确性、可读性等因素,而不是性能。
  2. 您的基准测试完全不合理,因为 if 语句每次都计算为 False。设置属性的现实世界代码可能会在设置器运行的合理时间内修改属性。
  3. 我希望对于许多现实世界的例子,设置器在没有相等测试的情况下会运行得更快。如果该测试每次都评估为True,那么显然,如果没有它,代码会更快。
  4. 整数设置器实际上是免费的,并且实际上设置器比直接字段访问慢。
  5. 时间花在了字符串属性上。由于 if 测试的优化,如果可能的话,可以避免字符串赋值代码,从而带来一些真正的性能优势。
  6. 如果将 setter 内联,它们会更快,但速度不会太快。

我相信任何现实世界的代码永远无法检测到任何这些性能差异。实际上,瓶颈将是获取传递给设置器的值,而不是在设置器中花费的时间。

这种if保护有价值的主要情况是属性修改成本高昂。例如,可能涉及发送 Windows 消息或访问数据库。对于由字段支持的财产,您可能可以接受或离开它。


在评论中的讨论中,过早优化想知道为什么比较 if FStringProp <> Value 比赋值 FStringProp := Value 更快。我进一步调查了一下,发现情况并不像我最初想象的那样。

事实证明 if FStringProp <> Value 由对 System._UStrEqual 的调用控制。传递的两个字符串实际上不是相同的引用,因此必须比较每个字符。然而,这段代码经过了高度优化,最重要的是只有 5 个字符可供比较。

FStringProp := Value 的调用会转到 System._UStrAsg,并且由于 Value 是一个具有负引用计数的文字,因此必须使用一个全新的字符串被制作。 Pascal 版本的代码如下所示:

procedure _UStrAsg(var Dest: UnicodeString; const Source: UnicodeString); // globals (need copy)
var
  S, D: Pointer;
  P: PStrRec;
  Len: LongInt;
begin
  S := Pointer(Source);
  if S <> nil then
  begin
    if __StringRefCnt(Source) < 0 then   // make copy of string literal
    begin
      Len := __StringLength(Source);
      S := _NewUnicodeString(Len);
      Move(Pointer(Source)^, S^, Len * SizeOf(WideChar));
    end else
    begin
      P := PStrRec(PByte(S) - SizeOf(StrRec));
      InterlockedIncrement(P.refCnt);
    end;
  end;
  D := Pointer(Dest);
  Pointer(Dest) := S;
  _UStrClr(D);
end;

其中的关键部分是对 _NewUnicodeString 的调用,它当然会调用 GetMem。对于堆分配比 5 个字符的比较慢得多,我一点也不感到惊讶。

I would make the following observations:

  1. The decision as to whether or not to use a setter should be based on factors like code maintenance, correctness, readability rather than performance.
  2. Your benchmark is wholly unreasonable since the if statements evaluate to False every time. Real world code that sets properties would be likely to modify the properties a reasonable proportion of the time that the setter runs.
  3. I would expect that for many real world examples, the setter would run faster without the equality test. If that test were to evaluate to True every time then clearly the code would be quicker without it.
  4. The integer setter is practically free and in fact the setter is slower than the direct field access.
  5. The time is spent in the string property. Here there is some real performance benefit due to the optimisation of the if test which avoids string assignment code if possible.
  6. The setters would be faster if you inlined them, but not by a significant amount.

My belief is that any real world code would never be able to detect any of these performance differences. In reality the bottleneck will be obtaining the values passed to the setters rather than time spent in the setters.

The main situation where such if protection is valuable is where the property modification is expensive. For example, perhaps it involves sending a Windows message, or hitting a database. For a property backed by a field you can probably take it or leave it.


In the chatter in the comments Premature Optimization wonders why the comparison if FStringProp <> Value is quicker than the assignment FStringProp := Value. I investigated a little further and it wasn't quite as I had originally thought.

It turns out that if FStringProp <> Value is dominated by a call to System._UStrEqual. The two strings passed are not in fact the same reference and so each character has to be compared. However, this code is highly optimised and crucially there are only 5 characters to compare.

The call to FStringProp := Value goes to System._UStrAsg and since Value is a literal with negative reference count, a brand new string has to be made. The Pascal version of the code looks like this:

procedure _UStrAsg(var Dest: UnicodeString; const Source: UnicodeString); // globals (need copy)
var
  S, D: Pointer;
  P: PStrRec;
  Len: LongInt;
begin
  S := Pointer(Source);
  if S <> nil then
  begin
    if __StringRefCnt(Source) < 0 then   // make copy of string literal
    begin
      Len := __StringLength(Source);
      S := _NewUnicodeString(Len);
      Move(Pointer(Source)^, S^, Len * SizeOf(WideChar));
    end else
    begin
      P := PStrRec(PByte(S) - SizeOf(StrRec));
      InterlockedIncrement(P.refCnt);
    end;
  end;
  D := Pointer(Dest);
  Pointer(Dest) := S;
  _UStrClr(D);
end;

The key part of this is the call to _NewUnicodeString which of course calls GetMem. I am not at all surprised that heap allocation is significantly slower than comparison of 5 characters.

末蓝 2025-01-01 05:08:14

将 'Hello' const 放入变量中并使用它进行设置,然后再次进行测试

Put 'Hello' const into a variable and use it for setting then do a test again

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文