Delphi IntToStr() 和 Integer.ToString() 之间的编译器区别?
Delphi compiler difference between IntToStr() and Integer.ToString()?
将 Integer
转换为 string
时,IntToStr()
和 Integer.ToString()
之间的基本区别是什么?哪个更快?
var
VarInt: integer;
VarStr: string;
begin
VarInt := 5;
VarStr := IntToStr(VarInt);
VarStr := VarInt.ToString;
end;
没有区别。
Int.ToString
定义如下:
function TIntHelper.ToString: string; inline;
begin
Result := IntToStr(Self);
end;
因为它是内联的,所以它只会转换为 IntToStr(Int)
。
添加了 var.action
方法,使运行时库 (RTL) 更适合 Java 和 C# 程序员。这在人们可能接触过的移动平台上尤为重要 Java。
这样做的一个主要好处是函数更容易被发现。您只需键入 VarInt.
,自动完成功能将向您显示所有可用选项。如果您还不知道 IntToStr
,则很难找到它。
免责声明:以下文本包含仅适用于 Delphi 10.2.1(以及 10.2.2)的详细信息,这似乎使内联变得更糟和 RVO:
编译器生成的代码确实不同(无论编译器版本如何),您在查看反汇编时可以很容易地看到 window。
让我们开始这个例程:
procedure Main;
var
i: Integer;
s: string;
begin
i := 0;
s := IntToStr(i);
s := i.ToString;
end;
现在让我们运行查看反汇编window以检查编译器生成的代码:
这就是你用 Delphi 10.1 得到的:
Project1.dpr.14: s := IntToStr(i);
00419810 8D55F8 lea edx,[ebp-]
00419813 8B45FC mov eax,[ebp-]
00419816 E80DA4FFFF call IntToStr
Project1.dpr.15: s := i.ToString;
0041981B 8D55F4 lea edx,[ebp-[=11=]c]
0041981E 8B45FC mov eax,[ebp-]
00419821 E802A4FFFF call IntToStr
00419826 8D45F8 lea eax,[ebp-]
00419829 8B55F4 mov edx,[ebp-[=11=]c]
0041982C E843D2FEFF call @UStrLAsg
这就是您使用 10.2.1(以及 10.2.2)获得的结果:
Project1.dpr.14: s := IntToStr(i);
00419B04 8D55F8 lea edx,[ebp-]
00419B07 8B45FC mov eax,[ebp-]
00419B0A E8C5A2FFFF call IntToStr
Project1.dpr.15: s := i.ToString;
00419B0F 33C0 xor eax,eax
00419B11 55 push ebp
00419B12 68499B4100 push [=12=]419b49
00419B17 64FF30 push dword ptr fs:[eax]
00419B1A 648920 mov fs:[eax],esp
00419B1D 8D55F4 lea edx,[ebp-[=12=]c]
00419B20 8B45FC mov eax,[ebp-]
00419B23 E8ACA2FFFF call IntToStr
00419B28 8D45F8 lea eax,[ebp-]
00419B2B 8B55F4 mov edx,[ebp-[=12=]c]
00419B2E E805D0FEFF call @UStrLAsg
00419B33 33C0 xor eax,eax
00419B35 5A pop edx
00419B36 59 pop ecx
00419B37 59 pop ecx
00419B38 648910 mov fs:[eax],edx
00419B3B 68509B4100 push [=12=]419b50
00419B40 8D45F4 lea eax,[ebp-[=12=]c]
00419B43 E8D4CCFEFF call @UStrClr
00419B48 C3 ret
00419B49 E9CEC3FEFF jmp @HandleFinally
00419B4E EBF0 jmp [=12=]419b40
现在最重要的问题是,那些额外的指令是什么?!
您可以在两个编译器中看到的额外指令是缺少所谓的 return 值优化的结果。正如您可能知道的那样,编译器将托管类型(如字符串)的函数结果视为隐藏的 var 参数。现在,当编译器进行内联时,它不会消除此参数,而是直接将 s
变量传递给 IntToStr
,就像直接调用它时一样。它保留了一个临时变量,用于传递给 IntToStr
,然后将该变量分配给 s
(即 call @UStrLAsg
,您在 [=14= 之后看到 3 行] 呼叫)。
正如我上面提到的,10.2 或 10.2.1 中似乎出现了回归,他们在内联调用之后立即更改了有关临时变量清理的内容(这是之前和之后的额外说明)。
报告为 RSP-19439。
待续...
将 Integer
转换为 string
时,IntToStr()
和 Integer.ToString()
之间的基本区别是什么?哪个更快?
var
VarInt: integer;
VarStr: string;
begin
VarInt := 5;
VarStr := IntToStr(VarInt);
VarStr := VarInt.ToString;
end;
没有区别。
Int.ToString
定义如下:
function TIntHelper.ToString: string; inline;
begin
Result := IntToStr(Self);
end;
因为它是内联的,所以它只会转换为 IntToStr(Int)
。
添加了 var.action
方法,使运行时库 (RTL) 更适合 Java 和 C# 程序员。这在人们可能接触过的移动平台上尤为重要 Java。
这样做的一个主要好处是函数更容易被发现。您只需键入 VarInt.
,自动完成功能将向您显示所有可用选项。如果您还不知道 IntToStr
,则很难找到它。
免责声明:以下文本包含仅适用于 Delphi 10.2.1(以及 10.2.2)的详细信息,这似乎使内联变得更糟和 RVO:
编译器生成的代码确实不同(无论编译器版本如何),您在查看反汇编时可以很容易地看到 window。
让我们开始这个例程:
procedure Main;
var
i: Integer;
s: string;
begin
i := 0;
s := IntToStr(i);
s := i.ToString;
end;
现在让我们运行查看反汇编window以检查编译器生成的代码:
这就是你用 Delphi 10.1 得到的:
Project1.dpr.14: s := IntToStr(i);
00419810 8D55F8 lea edx,[ebp-]
00419813 8B45FC mov eax,[ebp-]
00419816 E80DA4FFFF call IntToStr
Project1.dpr.15: s := i.ToString;
0041981B 8D55F4 lea edx,[ebp-[=11=]c]
0041981E 8B45FC mov eax,[ebp-]
00419821 E802A4FFFF call IntToStr
00419826 8D45F8 lea eax,[ebp-]
00419829 8B55F4 mov edx,[ebp-[=11=]c]
0041982C E843D2FEFF call @UStrLAsg
这就是您使用 10.2.1(以及 10.2.2)获得的结果:
Project1.dpr.14: s := IntToStr(i);
00419B04 8D55F8 lea edx,[ebp-]
00419B07 8B45FC mov eax,[ebp-]
00419B0A E8C5A2FFFF call IntToStr
Project1.dpr.15: s := i.ToString;
00419B0F 33C0 xor eax,eax
00419B11 55 push ebp
00419B12 68499B4100 push [=12=]419b49
00419B17 64FF30 push dword ptr fs:[eax]
00419B1A 648920 mov fs:[eax],esp
00419B1D 8D55F4 lea edx,[ebp-[=12=]c]
00419B20 8B45FC mov eax,[ebp-]
00419B23 E8ACA2FFFF call IntToStr
00419B28 8D45F8 lea eax,[ebp-]
00419B2B 8B55F4 mov edx,[ebp-[=12=]c]
00419B2E E805D0FEFF call @UStrLAsg
00419B33 33C0 xor eax,eax
00419B35 5A pop edx
00419B36 59 pop ecx
00419B37 59 pop ecx
00419B38 648910 mov fs:[eax],edx
00419B3B 68509B4100 push [=12=]419b50
00419B40 8D45F4 lea eax,[ebp-[=12=]c]
00419B43 E8D4CCFEFF call @UStrClr
00419B48 C3 ret
00419B49 E9CEC3FEFF jmp @HandleFinally
00419B4E EBF0 jmp [=12=]419b40
现在最重要的问题是,那些额外的指令是什么?!
您可以在两个编译器中看到的额外指令是缺少所谓的 return 值优化的结果。正如您可能知道的那样,编译器将托管类型(如字符串)的函数结果视为隐藏的 var 参数。现在,当编译器进行内联时,它不会消除此参数,而是直接将 s
变量传递给 IntToStr
,就像直接调用它时一样。它保留了一个临时变量,用于传递给 IntToStr
,然后将该变量分配给 s
(即 call @UStrLAsg
,您在 [=14= 之后看到 3 行] 呼叫)。
正如我上面提到的,10.2 或 10.2.1 中似乎出现了回归,他们在内联调用之后立即更改了有关临时变量清理的内容(这是之前和之后的额外说明)。
报告为 RSP-19439。
待续...