Recent

Author Topic: rtl/inc/objpas.inc procedure InitInterfacePointers observation  (Read 1183 times)

lagprogramming

  • Sr. Member
  • ****
  • Posts: 405
Looking at procedure InitInterfacePointers(objclass: tclass;instance : pointer); I've noticed that variable "i" is of type longint and intftable^.EntryCount is of type sizeuint. I was thinking that maybe it would be better to make i of sizeuint, too.
Code: Pascal  [Select][+][-]
  1.       procedure InitInterfacePointers(objclass: tclass;instance : pointer);
  2.  
  3.         var
  4.           ovmt: PVmt;
  5.           i: longint;
  6.           intftable: pinterfacetable;
  7.           Res: pinterfaceentry;
  8.         begin
  9.           ovmt := PVmt(objclass);
  10.           while assigned(ovmt) and {$ifdef VER3_0}(ovmt^.vIntfTable <> @emptyintf){$else}assigned(ovmt^.vIntfTable){$endif} do
  11.             begin
  12.               intftable:=ovmt^.vIntfTable;
  13.               {$ifdef VER3_0}
  14.               if assigned(intftable) then
  15.               {$endif VER3_0}
  16.               begin
  17.                 i:=intftable^.EntryCount;
  18.                 Res:=@intftable^.Entries[0];
  19.                 while i>0 do begin
  20.                   if Res^.IType = etStandard then
  21.                     ppointer(@(pbyte(instance)[Res^.IOffset]))^:=
  22.                       pointer(Res^.VTable);
  23.                   inc(Res);
  24.                   dec(i);
  25.                 end;
  26.               end;
  27.               ovmt:=ovmt^.vParent;
  28.             end;
  29.         end;

Also, in the same file, immediately after the above procedure comes class function TObject.InitInstance(instance : pointer) : tobject; This function has variable "i" of type longint, too. Somebody might check the variable type because the function has the line "for i:=0 to mopinittable^.Count-1 do" and mopinittable^.Count is of type longword.

BrunoK

  • Sr. Member
  • ****
  • Posts: 452
  • Retired programmer
Re: rtl/inc/objpas.inc procedure InitInterfacePointers observation
« Reply #1 on: March 20, 2023, 03:33:22 pm »
Test this little program to see what happens if 'i' is defined as sizeuint.
One loop uses i : longint and the next one uses sui : sizeuint as indexes.
In case i is declared as longint, FPC takes appropriate steps to adjust types for the loop (probably casting to longint).
Code: Pascal  [Select][+][-]
  1. program pgmLongVsSizeUI;
  2.  
  3. var
  4.   i: longint;
  5.   sui : sizeuint;
  6.   c: sizeuint;
  7.   a: array [0..9] of pointer;
  8. begin
  9.   c := 0;
  10.   WriteLn('Using i');
  11.   for i := 0 to c - 1 do
  12.     writeln(i : 6, HexStr(a[i]):18);
  13.   Write('Press enter'); ReadLn;
  14.  
  15.   WriteLn('Using sui');
  16.   for sui := 0 to c - 1 do begin
  17.     writeln(sui : 6, HexStr(a[sui]):18);
  18.     if sui>= 99 then
  19.       break;
  20.   end;
  21.   Write('Press enter'); ReadLn;
  22. end.

lagprogramming

  • Sr. Member
  • ****
  • Posts: 405
Re: rtl/inc/objpas.inc procedure InitInterfacePointers observation
« Reply #2 on: March 20, 2023, 04:13:51 pm »
It's not mandatory to keep the for loop.

Code: Pascal  [Select][+][-]
  1.   WriteLn('Using sui');
  2.   for sui := 0 to c - 1 do begin
  3.     writeln(sui : 6, HexStr(a[sui]):18);
  4.     if sui>= 99 then
  5.       break;
  6.   end;
 
  can be replaced with something like
 
Code: Pascal  [Select][+][-]
  1.   WriteLn('Using sui');
  2.   if c>0 then
  3.   begin
  4.     sui:=0;
  5.     repeat
  6.       writeln(sui : 6, HexStr(a[sui]):18);
  7.       inc(sui);
  8.     until sui=c;
  9.   end;

Edit:
Or you can still keep the for loop just by adding "if c>0 then" before "for"

Code: Pascal  [Select][+][-]
  1.   WriteLn('Using sui');
  2.   if c>0 then
  3.     for sui := 0 to c - 1 do begin
  4.       writeln(sui : 6, HexStr(a[sui]):18);
  5.     end;
« Last Edit: March 20, 2023, 04:23:33 pm by lagprogramming »

BrunoK

  • Sr. Member
  • ****
  • Posts: 452
  • Retired programmer
Re: rtl/inc/objpas.inc procedure InitInterfacePointers observation
« Reply #3 on: March 20, 2023, 04:25:08 pm »
It's not mandatory to keep the for loop.
So how would you rewrite procedure InitInterfacePointers(objclass: tclass;instance : pointer); using i : SizeUInt and be sure it works in every case ?

BrunoK

  • Sr. Member
  • ****
  • Posts: 452
  • Retired programmer
Re: rtl/inc/objpas.inc procedure InitInterfacePointers observation
« Reply #4 on: March 20, 2023, 04:32:17 pm »
Edit above message  : I meant in procedure TObject.InitInstance(instance : pointer) : tobject; the for loop
Code: Pascal  [Select][+][-]
  1.                        for i:=0 to mopinittable^.Count-1 do
  2.                          TRTTIRecVarOp(mopinittable^.Entries[i].ManagmentOperator)(PByte(Instance)+mopinittable^.Entries[i].FieldOffset);
  3.  

lagprogramming

  • Sr. Member
  • ****
  • Posts: 405
Re: rtl/inc/objpas.inc procedure InitInterfacePointers observation
« Reply #5 on: March 21, 2023, 12:24:09 pm »
The patch at the end of the message updates
class function TObject.InitInstance(instance : pointer) : tobject;
class function TObject.GetInterfaceEntry(const iid : tguid) : pinterfaceentry;
class function TObject.GetInterfaceEntryByStr(const iidstr : shortstring) : pinterfaceentry;
in order to mimic the coding style of
procedure InitInterfacePointers(objclass: tclass;instance : pointer);

Now, a further improvement might be to replace the "while" loops with "for" loops because, as far as I know, during the "for" loops "i" will be kept in a CPU register, which increases speed most of the times. There are two things to consider and both of them need the experience of a rtl compiler developer:
1)class function TObject.MethodAddress(const name : shortstring) : codepointer; and class function TObject.MethodName(address : codepointer) : shortstring;, both of them have "for i:=0 to methodtable^.count-1 do" without an "if methodtable^.count>0 then" check, even though variable "i : dword;". This is either a bug, either if assigned(methodtable) then methodtable^.count is automatically greater than 0(which would allow the rtl compiler developer to avoid a prior "if methodtable^.count>0 then" check in the code).
Now the questions similar to the situation presented in the above paragraph. If assigned(mopinittable) can mopinittable^.Count be zero? If assigned(intftable) can intftable^.EntryCount be zero? Maybe automatically they are greater than zero. I consider this situation not just because of what I've written in the previous paragraph, but also because of the comment "{ ensure that no range check errors pop up with the [0..0] array }" at class function TObject.InitInstance, comment associated with compiler directives that I've removed in the patch.

2)If the answer is yes to the questions presented above, would it be better to replace in the mentioned routines "i:=... while i>0 do...begin ... inc(i);end;" with "if i>0 then for i:=i downto 1 do begin...end;"? The value of "i" during the loop would be stored in a CPU register, most likely making the loop faster.
By the way, I notice that "for...to -1 do" and "for ...downto 1 do" loops are not optimized for near zero comparisons. The code shows a cmp instruction which can be avoided by using a test one, removing at conditional jumps the "or equal".

Here is a patch that uses "while" loops. Changing "while" loops into "for" loops should also consider updating the coding style in the other mentioned functions.
Code: Pascal  [Select][+][-]
  1. diff --git a/rtl/inc/objpas.inc b/rtl/inc/objpas.inc
  2. index 5479d3698a..ae836317fc 100644
  3. --- a/rtl/inc/objpas.inc
  4. +++ b/rtl/inc/objpas.inc
  5. @@ -351,7 +351,7 @@
  6.  
  7.          var
  8.            ovmt: PVmt;
  9. -          i: longint;
  10. +          i: sizeuint;
  11.            intftable: pinterfacetable;
  12.            Res: pinterfaceentry;
  13.          begin
  14. @@ -365,13 +365,14 @@
  15.                begin
  16.                  i:=intftable^.EntryCount;
  17.                  Res:=@intftable^.Entries[0];
  18. -                while i>0 do begin
  19. -                  if Res^.IType = etStandard then
  20. -                    ppointer(@(pbyte(instance)[Res^.IOffset]))^:=
  21. -                      pointer(Res^.VTable);
  22. -                  inc(Res);
  23. -                  dec(i);
  24. -                end;
  25. +                while i>0 do
  26. +                  begin
  27. +                    if Res^.IType = etStandard then
  28. +                      ppointer(@(pbyte(instance)[Res^.IOffset]))^:=
  29. +                        pointer(Res^.VTable);
  30. +                    inc(Res);
  31. +                    dec(i);
  32. +                  end;
  33.                end;
  34.                ovmt:=ovmt^.vParent;
  35.              end;
  36. @@ -385,8 +386,9 @@
  37.             inittable : pointer;
  38.  {$ifdef FPC_HAS_FEATURE_RTTI}
  39.             mopinittable : PRTTIRecordOpOffsetTable;
  40. +           Res : ^TRTTIRecordOpOffsetEntry;
  41.  {$endif def FPC_HAS_FEATURE_RTTI}
  42. -           i : longint;
  43. +           i : longword;
  44.  {$endif VER3_0}
  45.          begin
  46.             { the size is saved at offset 0 }
  47. @@ -409,12 +411,14 @@
  48.                     mopinittable:=RTTIRecordMopInitTable(inittable);
  49.                     if assigned(mopinittable) then
  50.                       begin
  51. -                       {$push}
  52. -                       { ensure that no range check errors pop up with the [0..0] array }
  53. -                       {$R-}
  54. -                       for i:=0 to mopinittable^.Count-1 do
  55. -                         TRTTIRecVarOp(mopinittable^.Entries[i].ManagmentOperator)(PByte(Instance)+mopinittable^.Entries[i].FieldOffset);
  56. -                       {$pop}
  57. +                       i:=mopinittable^.Count;
  58. +                       Res:=@mopinittable^.Entries[0];
  59. +                       while i>0 do
  60. +                         begin
  61. +                           TRTTIRecVarOp(Res^.ManagmentOperator)(PByte(Instance)+Res^.FieldOffset);
  62. +                           inc(Res);
  63. +                           dec(i);
  64. +                         end;
  65.                       end;
  66.                   end;
  67.               end;
  68. @@ -930,7 +934,7 @@            TMsgInt = record
  69.  
  70.        class function TObject.GetInterfaceEntry(const iid : tguid) : pinterfaceentry;
  71.          var
  72. -          i: longint;
  73. +          i: sizeuint;
  74.            intftable: pinterfacetable;
  75.            ovmt: PVmt;
  76.          begin
  77. @@ -942,11 +946,14 @@            TMsgInt = record
  78.              if assigned(intftable) then
  79.              {$endif VER3_0}
  80.              begin
  81. -              for i:=0 to intftable^.EntryCount-1 do
  82. +              i:=intftable^.EntryCount;
  83. +              result:=@intftable^.Entries[0];
  84. +              while i>0 do
  85.                begin
  86. -                result:=@intftable^.Entries[i];
  87.                  if assigned(Result^.iid) and IsGUIDEqual(Result^.iid^,iid) then
  88.                    Exit;
  89. +                inc(Result);
  90. +                dec(i);
  91.                end;
  92.              end;
  93.              ovmt := ovmt^.vParent;
  94. @@ -956,7 +963,7 @@            TMsgInt = record
  95.  
  96.        class function TObject.GetInterfaceEntryByStr(const iidstr : shortstring) : pinterfaceentry;
  97.          var
  98. -          i: longint;
  99. +          i: sizeuint;
  100.            intftable: pinterfacetable;
  101.            ovmt: PVmt;
  102.          begin
  103. @@ -968,11 +975,14 @@            TMsgInt = record
  104.              if assigned(intftable) then
  105.              {$endif VER3_0}
  106.              begin
  107. -              for i:=0 to intftable^.EntryCount-1 do
  108. +              i:=intftable^.EntryCount;
  109. +              result:=@intftable^.Entries[0];
  110. +              while i>0 do
  111.                begin
  112. -                result:=@intftable^.Entries[i];
  113.                  if assigned(result^.iidstr) and (result^.iidstr^ = iidstr) then
  114.                    Exit;
  115. +                inc(Result);
  116. +                dec(i);
  117.                end;
  118.              end;
  119.              ovmt := ovmt^.vParent;

runewalsh

  • Jr. Member
  • **
  • Posts: 78
Re: rtl/inc/objpas.inc procedure InitInterfacePointers observation
« Reply #6 on: May 04, 2023, 08:19:54 pm »
No, from my observations, for loop often generates worse code. For now,

Code: Pascal  [Select][+][-]
  1. for i := 0 to N - 1 do
  2. begin
  3.         // ...
  4. end;
  5.  

is usually worse than

Code: Pascal  [Select][+][-]
  1. i := 0;
  2. // N := precalculate if nontrivial
  3. while i < N do
  4. begin
  5.         // ...
  6.         inc(i);
  7. end;
  8.  

in everything but convenience. In future this might (but unlikely ever will) change.

Certain other tricks that while form makes available exist, for example, if you know there will always be at least one iteration, you can use repeat .. until, or you can do the following (which, honestly, in this particular case generates slightly worse code than the current version anyway, but also saves two (!) LoC):

Code: Pascal  [Select][+][-]
  1.       procedure InitInterfacePointers(objclass: tclass;instance : pointer);
  2.  
  3.         var
  4.           ovmt: PVmt;
  5.           intftable: pinterfacetable;
  6.           Res,ResEnd: pinterfaceentry;
  7.         begin
  8.           ovmt := PVmt(objclass);
  9.           while assigned(ovmt) and {$ifdef VER3_0}(ovmt^.vIntfTable <> @emptyintf){$else}assigned(ovmt^.vIntfTable){$endif} do
  10.             begin
  11.               intftable:=ovmt^.vIntfTable;
  12.               {$ifdef VER3_0}
  13.               if assigned(intftable) then
  14.               {$endif VER3_0}
  15.               begin
  16.                 Res:=@intftable^.Entries[0];
  17.                 ResEnd:=Res+intftable^.EntryCount;
  18.                 while Res<ResEnd do begin
  19.                   if Res^.IType = etStandard then
  20.                     ppointer(instance+Res^.IOffset)^:=
  21.                       Res^.VTable;
  22.                   inc(Res);
  23.                 end;
  24.               end;
  25.               ovmt:=ovmt^.vParent;
  26.             end;
  27.         end;

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: rtl/inc/objpas.inc procedure InitInterfacePointers observation
« Reply #7 on: May 04, 2023, 08:29:17 pm »
for in do <sigh>
Specialize a type, not a var.

 

TinyPortal © 2005-2018