Delphi compiler difference between IntToStr() and Integer.ToString()?
Asked Answered
V

2

6

What is the basic difference between IntToStr() and Integer.ToString() when converting an Integer to a string. Which one is faster?

var
  VarInt: integer;
  VarStr: string;
begin
  VarInt := 5;
  VarStr := IntToStr(VarInt); 
  VarStr := VarInt.ToString;
end;
Viperine answered 27/11, 2017 at 18:51 Comment(5)
There's no difference. ToString is inlined hence the compiler produces essentially the same result.Morley
It's same result and speed when i combile with 'VarStr := (VarInt + 1).ToString'Viperine
Yes. In both cases must be first evaluated expression VarInt + 1.Morley
If you look at the documentation for .ToString(); it actually tells you to read the documentation for IntToStr() for more information. So I assume no difference at all, other than how you actually accomplish it.Escarp
TIntegerHelper.ToString() simply calls SysUtils.IntToStr(), and ToString() is inlined, so whether you call VarInt.ToString or IntToStr(VarInt) doesn't really matter since they will produce identical code.Ursal
G
11

Disclaimer: The following text contains details that only apply to Delphi 10.2.1 (and also 10.2.2) which seems to have made something worse about inlining and RVO:

The code produced by the compiler indeed differs (regardless the compiler version) as you can easily see when looking into the disassembly window.

Lets take this routine:

procedure Main;
var
  i: Integer;
  s: string;
begin
  i := 0;
  s := IntToStr(i);
  s := i.ToString;
end;

Now let's run this and look into the disassembly window to check the code the compiler produced:

This is what you got with Delphi 10.1:

Project1.dpr.14: s := IntToStr(i);
00419810 8D55F8           lea edx,[ebp-$08]
00419813 8B45FC           mov eax,[ebp-$04]
00419816 E80DA4FFFF       call IntToStr
Project1.dpr.15: s := i.ToString;
0041981B 8D55F4           lea edx,[ebp-$0c]
0041981E 8B45FC           mov eax,[ebp-$04]
00419821 E802A4FFFF       call IntToStr
00419826 8D45F8           lea eax,[ebp-$08]
00419829 8B55F4           mov edx,[ebp-$0c]
0041982C E843D2FEFF       call @UStrLAsg

And this is what you get with 10.2.1 (and also 10.2.2):

Project1.dpr.14: s := IntToStr(i);
00419B04 8D55F8           lea edx,[ebp-$08]
00419B07 8B45FC           mov eax,[ebp-$04]
00419B0A E8C5A2FFFF       call IntToStr
Project1.dpr.15: s := i.ToString;
00419B0F 33C0             xor eax,eax
00419B11 55               push ebp
00419B12 68499B4100       push $00419b49
00419B17 64FF30           push dword ptr fs:[eax]
00419B1A 648920           mov fs:[eax],esp
00419B1D 8D55F4           lea edx,[ebp-$0c]
00419B20 8B45FC           mov eax,[ebp-$04]
00419B23 E8ACA2FFFF       call IntToStr
00419B28 8D45F8           lea eax,[ebp-$08]
00419B2B 8B55F4           mov edx,[ebp-$0c]
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 $00419b50
00419B40 8D45F4           lea eax,[ebp-$0c]
00419B43 E8D4CCFEFF       call @UStrClr
00419B48 C3               ret 
00419B49 E9CEC3FEFF       jmp @HandleFinally
00419B4E EBF0             jmp $00419b40

Now the million dollar question is, what is all that extra instructions there?!

The extra instructions that you can see in both compilers is the result of missing so called return value optimization. As you might know the compiler treats results of functions that are of a managed type (like string) as hidden var parameter. Now when the compiler does the inlining it does not eliminate this parameter and directly passes the s variable to the IntToStr as it does when directly calling it. It rather reserves a temporary variable that it uses to pass to the IntToStr and then after that assigns that variable to s (that's the call @UStrLAsg you see there 3 lines after the IntToStr call).

As I mentioned above there seems to be a regression in 10.2 or 10.2.1 where they changed something about temporary variable cleanup right after the inlined call (that's the extra instructions before and after that).

Reported as RSP-19439.

To be continued ...

Granthem answered 28/11, 2017 at 13:17 Comment(4)
Interestingly enough, in Seattle, if I link with runtime package, both instructions become 100% identical. Otherwise, I get same results as 10.1Achates
No, they don't, look at the addresses of the two calls. The second calls into the non inlined helper function.Granthem
Indeed, I missed that. But still remove the UStrLAsg part.Achates
Sure because it does not need the temp variable because there is no inlined method being called so it passes s as result parameter.Granthem
B
3

There is no difference.

Int.ToString is defined like so:

function TIntHelper.ToString: string; inline;
begin
  Result := IntToStr(Self);
end;

Because it is inlined, it will just translate to IntToStr(Int).

The var.action methods were added to bring make the Runtime Library (RTL) more hospitable to Java and C# programmers. Which is especially relevant on the mobile platforms where people are likely to have been exposed to Java.

A major benefit of doing it this way is that the functions are much more discoverable. You can just type VarInt. and the auto-completion will show you all available options. If you don't know about IntToStr already it's kind of hard to find it.

Beg answered 28/11, 2017 at 10:7 Comment(1)
Even more so with string, .IndexOf, .SubString etc are much easier to find than Pos() and Copy()Melendez

© 2022 - 2024 — McMap. All rights reserved.