performance - Delphi:为什么二进制字符串比较运算符 (=) 不使用 SameStr?

标签 performance delphi string compiler-construction

众所周知,SameStr(S1, S2)S1 = S2 更快,其中 var S1, S2: string德尔福。

(并且,当然SameText(S1, S2)AnsiLowerCase(S1) = AnsiLowerCase(S2) 快得多。 )

但是,据我了解,SameStr(S1, S2)S1 = S2 的作用完全相同,所以我不禁想知道为什么事实上,Delphi 编译器在使用 = 运算符测试字符串相等性时不使用 SameStr 代码。这肯定是有原因的吧?

一些基准测试

一个简单的程序,

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  RejbrandCommon;

const
  N = 1000000;

var
  Strings1, Strings2: StringArray;
  i: integer;
  b: {dummy }boolean;

procedure CreateRandomStringArrays;
var
  i: integer;
begin
  SetLength(Strings1, N);
  SetLength(Strings2, N);
  for i := 0 to N - 1 do
  begin
    Strings1[i] := RandomString(0, 40);
    Strings2[i] := RandomString(0, 40);
  end;
end;

begin

  CreateRandomStringArrays;

  StartClock;
  for i := 0 to N - 1 do
    if Strings1[i] = Strings2[i] then
      b := not b;
  StopClock;
  OutputClock;

  StartClock;
  for i := 0 to N - 1 do
    if SameStr(Strings1[i], Strings2[i]) then
      b := not b;
  StopClock;
  OutputClock;

  Pause;

end.

哪里

function RandomString(const LowerLimit: integer = 2; const UpperLimit: integer = 20): string;
var
  N, i: integer;
begin
  N := RandomRange(LowerLimit, UpperLimit);
  SetLength(result, N);
  for i := 1 to N do
    result[i] := RandomChar;
end;

和内联

function RandomChar: char;
begin
  result := chr(RandomRange(ord('A'), ord('Z')));
end;

“时钟”函数只是包装 QueryPerformanceCounterQueryPerformanceFrequencyWriteln,生成输出

2.56599325762716E-0002
1.24310093156453E-0002
ratio ~ 2.06

如果我们比较的两个字符串的长度差异很大,那么差异就更大。我们尝试

Strings1[i] := RandomString(0, 0); // = '';
Strings2[i] := RandomString(0, 40);

并获取

1.81630411160156E-0002
4.44662043198641E-0003
ratio ~ 4.08

那么为什么编译器在为 S1 = S2 编写汇编代码时不使用 SameStr 代码呢?

更新

读完 Cosmin Prund 的精彩回答后,我忍不住设置了

Strings1[i] := RandomString(40, 40);
Strings2[i] := RandomString(40, 40);

产生长度相等的字符串。

2.74783364614126E-0002
1.96818773095322E-0002
ratio ~ 1.40

嗯... SameStr 仍然获胜...

我的规范

CPU Brand String: Intel(R) Core(TM) i7 CPU         870  @ 2.93GHz
Memory: 6 GB
OS: Windows 7 Home Premium (64-bit)
Compiler/RTL: Delphi 2009

更新

看起来(请参阅 Cosmin Prund 优秀答案下面的评论)= 运算符在 D2009 和 D2010 之间发生了更改。谁能证实这一点?

最佳答案

回答

这完全取决于您如何构建随机字符串。我使用了代码的修改版本,因为我们很少有人拥有 RejbrandCommon 单元,而且因为我想使用 Excel 来完成我的分析(并制作漂亮的图片)。

代码(跳过代码查看一些结论):

计划项目3;

{$APPTYPE CONSOLE}

uses
  SysUtils, Windows;

const
  StringsNumber = 2000000;

var
  Strings1, Strings2: array of string;
  StrLen: integer;
  b: {dummy }boolean;

function RandomString(MinLen, MaxLen:Integer):string;
var N, i:Integer;
begin
  N := MinLen + Random(MaxLen-MinLen);
  Assert(N >= MinLen); Assert(N <= MaxLen);
  SetLength(Result, N);
  for i:=1 to N do
    Result[i] := Char(32 + Random(1024)); // Random Unicode Char
end;

procedure CreateRandomStringArrays(StrLen:Integer);
var
  i: integer;
  StrLen2:Integer;
begin
  SetLength(Strings1, StringsNumber);
  SetLength(Strings2, StringsNumber);
  for i := 0 to StringsNumber - 1 do
  begin
    StrLen2 := StrLen + Random(StrLen div 2);
    Strings1[i] := RandomString(StrLen, StrLen2);
    StrLen2 := StrLen + Random(StrLen div 2);
    Strings2[i] := RandomString(StrLen, StrLen2);
  end;
end;

var C1, C2, C3, C4:Int64;

procedure RunTest(StrLen:Integer);
var i:Integer;
begin
  CreateRandomStringArrays(StrLen);

  // Test 1: using equality operator
  QueryPerformanceCounter(C1);
  for i := 0 to StringsNumber - 1 do
    if Strings1[i] = Strings2[i] then
      b := not b;
  QueryPerformanceCounter(C2);

  // Test 2: using SameStr
  QueryPerformanceCounter(C3);
  for i := 0 to StringsNumber - 1 do
    if SameStr(Strings1[i], Strings2[i]) then
      b := not b;
  QueryPerformanceCounter(C4);

  // Results:
  C2 := C2 - C1;
  C4 := C4 - C3;
  WriteLn(IntToStr(StrLen) + #9 + IntToStr(C2) + #9 + IntToStr(C4));
end;

begin

  WriteLn('Count'#9'='#9'SameStr');
  for StrLen := 1 to 50 do
    RunTest(StrLen);

end.

我使 CreateRandomStringArrays 例程采用 StrLen 参数,以便我可以在循环中运行多个类似的测试。我使代码直接使用 QueryPerformanceCounter 并使用 WriteLn 制表符分隔格式的结果,以便我可以将其复制/粘贴到 Excel 中。在 Excel 中,我得到以下形式的结果:

StrLen  =   SameStr
1   61527   69364
2   60188   69450
3   72130   68891
4   78847   85779
5   77852   78286
6   83612   88670
7   93936   96773

然后我将事情标准化了一点。每行将最大值设置为“1”,其他值设置为 1 的百分比。结果如下所示:

StrLen  =   SameStr
1   0,88    1
2   0,86    1
3   1   0,95
4   0,91    1
5   0,99    1
6   0,94    1
7   0,97    1

然后我开始使用 CreateRandomStringArrays 例程来运行多个测试。

这就是原始情况下的绘图(CreateRandomStringArrays 生成随机长度的字符串,长度为 1 到 X 轴上的任何值)。蓝色是“=”运算符的结果,红色是“SameStr”的结果,越低越好。很明显,SameStr() 对于长度超过 10 个字符的字符串有优势。

alt text http://fisiere.sediu.ro/PentruForumuri/V1_1_to_maxlen.png

下一个测试,使CreateRandomStringArrays返回相等长度的字符串。字符串的内容仍然是完全随机的,但字符串的长度等于 X 轴上的长度。这次“=”运算符显然更有效:

alt text http://fisiere.sediu.ro/PentruForumuri/V1_equal_strings.png

现在真正的问题是,对于 REAL 代码,字符串相等的概率是多少? SameStr() 的差异必须有多大才能开始获得地形?接下来的文本,我将构建两个字符串,第一个字符串的长度为 StrLen(X 轴上的数字),第二个字符串的长度为 StrLen + Random(4)。同样,“=”运算符更好:

alt text http://fisiere.sediu.ro/PentruForumuri/V1_rnd_p4.png

接下来的测试,我有两个字符串,每个字符串的长度为:StrLen + Random(StrLen div 10)。 “=”运算符更好。

alt text http://fisiere.sediu.ro/PentruForumuri/V1_rnd_pm_10p.png

...以及我的最终测试,长度为 +/- 50% 的字符串。公式:StrLen + Random(StrLen div 2)。 SameStr() 赢得了这一轮:

alt text http://fisiere.sediu.ro/PentruForumuri/V1_rnd_pm_50p.png

结论

我不确定。我没想到这与字符串长度有关!我希望这两个函数都能快速处理不同长度的字符串,但这并没有发生。

关于performance - Delphi:为什么二进制字符串比较运算符 (=) 不使用 SameStr?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3230296/

相关文章:

c# - 快速视频显示 WPF

java - 哪些编码问题可能会严重损害游戏的性能?

Windows 7 中的 Delphi VCL "TaskDialog"问题

delphi - Delphi中如何将对象保存到文件中

mysql - 任何适用于 Windows 的 MySQLTuner 等效/替代品?

delphi - 如何将模态对话框保持在动态创建的表单之上? (CreateParams - 覆盖)

c# - 用于字符串比较的可忽略字符列表

c - 字符串比较逻辑错误

c - 获取变量的当前日期

c# - UWP 应用程序更快地加载图像?