Wednesday, 10 June 2009

The performance of the "as" operator

Some people wonder, if the "as" operator is slow or not. A programmer well known in the Danish Delphi community, Thomas Vedel, has made some investigations into this, using Delphi 2009, and I have got his permission to publish them.

The blue instructions below are those that are only executed if the type does not match, and they are therefore not relevant for normal execution. The conclusion seems to be, that the "as" operator does not slow down your application in any amount that is worth spending time on. So keep using the "as" operator!

Example 1

Main1.pas.28: TButton(Sender).Caption := 'Klik her!';
00464808 8BC2 mov eax,edx
0046480A BA24484600 mov edx,$00464824
0046480F E8E0CFFDFF call TControl.SetText


Example 2

Main1.pas.28: (Sender as TButton).Caption := 'Klik her!';
0046480B 8BC3 mov eax,ebx
0046480D 8B15CC014300 mov edx,[$004301cc]
00464813 E8A0F4F9FF call @AsClass
00464818 BA30484600 mov edx,$00464830
0046481D E8D2CFFDFF call TControl.SetText

00403CB8 85C0 test eax,eax
00403CBA 7416 jz $00403cd2
00403CBC 89C1 mov ecx,eax
00403CBE 8B09 mov ecx,[ecx]
00403CC0 39D1 cmp ecx,edx
00403CC2 740E jz $00403cd2
00403CC4 8B49D0 mov ecx,[ecx-$30]
00403CC7 85C9 test ecx,ecx
00403CC9 75F3 jnz $00403cbe
00403CCB B00A mov al,$0a
00403CCD E93EF4FFFF jmp Error

00403CD2 C3 ret


Example 3

Main1.pas.28: if (Sender is TButton) then
0046480B 8BC3 mov eax,ebx
0046480D 8B15CC014300 mov edx,[$004301cc]
00464813 E87CF4F9FF call @IsClass
00464818 84C0 test al,al
0046481A 740C jz $00464828

Main1.pas.29: TButton(Sender).Caption := 'Klik her!';
0046481C BA38484600 mov edx,$00464838
00464821 8BC3 mov eax,ebx
00464823 E8CCCFFDFF call TControl.SetText

00403C94 53 push ebx
00403C95 56 push esi
00403C96 8BF2 mov esi,edx
00403C98 8BD8 mov ebx,eax
00403C9A 85DB test ebx,ebx
00403C9C 740D jz $00403cab
00403C9E 8BD6 mov edx,esi
00403CA0 8B03 mov eax,[ebx]
00403CA2 E875000000 call TObject.InheritsFrom
00403CA7 84C0 test al,al
00403CA9 7505 jnz $00403cb0
00403CAB 33C0 xor eax,eax
00403CAD 5E pop esi
00403CAE 5B pop ebx
00403CAF C3 ret

00403CB0 B001 mov al,$01
00403CB2 5E pop esi
00403CB3 5B pop ebx
00403CB4 C3 ret


omonien said...

Imho the examples are missing a context, thus the results are saying not much.

Technically Example 1 is just wrong - you *have* to use the IS operator there, as you don't know what class Sender will be. Without using "if sender is TButton" you would get weird exceptions if sender is something else.

Lars D said...

Sorry - this post was meant to cover the "as" operator, and I wrote "is" everywhere. I corrected the post, and I hope it makes more sense, now - thanks :-)

Moritz Beutel said...

Thanks to single inheritance, "as" can be implemented pretty straightforward. If you wonder what a polymorphic cast may look like in an MI language (and if you have C++Builder installed), look at $(BDS)\source\cpprtl\Source\except\xxtype.cpp, function __DynamicCast(). But beware, you'll need a whiskey after that ;)

Anyway, even dynamic_cast isn't that slow, unless you decide to implement it as Microsoft did for x64.

omonien said...

ok, makes more sense now :)

Eric said...

Actually things aren't so nice and fluffy, as both AsClass and InheritsFrom (for 'is') involve a loop that walks up the class hierarchy.

If you're only testing against a direct ancestor, you'll go only once through the loop, and that's not an issue, but if you're not, then branch mispredictions will make an appearance, and then things can go awfully wrong from that point on.

This is one of those case where looking at generated code doesn't provide much insight as to what the real-world performance will be :)