rtl/inc/objpas.inc procedure InitInterfacePointers observation lazarus / 64-FPC development ---------------------------------------------------------------------- lagprogramming · 2023-03-20 13:45 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       procedure InitInterfacePointers(objclass: tclass;instance : pointer);         var          ovmt: PVmt;          i: longint;        intftable: pinterfacetable;          Res: pinterfaceentry;        begin          ovmt := PVmt(objclass);          while assigned(ovmt) and {$ifdef VER3_0}(ovmt^.vIntfTable <> @emptyintf){$else}assigned(ovmt^.vIntfTable){$endif} do            begin              intftable:=ovmt^.vIntfTable;              {$ifdef VER3_0}              if assigned(intftable) then              {$endif VER3_0}              begin                i:=intftable^.EntryCount;              Res:=@intftable^.Entries[0];                while i>0 do begin                  if Res^.IType = etStandard then                  ppointer(@(pbyte(instance)[Res^.IOffset]))^:=                      pointer(Res^.VTable);                  inc(Res);                  dec(i);                end;              end;              ovmt:=ovmt^.vParent;            end;        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 · 2023-03-20 14:33 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program pgmLongVsSizeUI; var  i: longint;  sui : sizeuint;  c: sizeuint;  a: array [0..9] of pointer;begin  c := 0;  WriteLn('Using i');  for i := 0 to c - 1 do    writeln(i : 6, HexStr(a[i]):18);  Write('Press enter'); ReadLn;   WriteLn('Using sui');  for sui := 0 to c - 1 do begin    writeln(sui : 6, HexStr(a[sui]):18);    if sui>= 99 then    break;  end;  Write('Press enter'); ReadLn;end. ---------------------------------------------------------------------- lagprogramming · 2023-03-20 15:13 It's not mandatory to keep the for loop. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   WriteLn('Using sui');  for sui := 0 to c - 1 do begin    writeln(sui : 6, HexStr(a[sui]):18);    if sui>= 99 then      break;  end;    can be replaced with something like   Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   WriteLn('Using sui');  if c>0 then  begin    sui:=0;    repeat      writeln(sui : 6, HexStr(a[sui]):18);      inc(sui);    until sui=c;  end; Edit: Or you can still keep the for loop just by adding "if c>0 then" before "for" Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   WriteLn('Using sui');  if c>0 then    for sui := 0 to c - 1 do begin    writeln(sui : 6, HexStr(a[sui]):18);    end; ---------------------------------------------------------------------- BrunoK · 2023-03-20 15:25 Quote from: lagprogramming on March 20, 2023, 04:13:51 pmIt'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 · 2023-03-20 15:32 Quote from: BrunoK on March 20, 2023, 04:25:08 pmEdit above message  : I meant in procedure TObject.InitInstance(instance : pointer) : tobject; the for loop Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i                      for i:=0 to mopinittable^.Count-1 do                       TRTTIRecVarOp(mopinittable^.Entries[i].ManagmentOperator)(PByte(Insta nce)+mopinittable^.Entries[i].FieldOffset); ---------------------------------------------------------------------- lagprogramming · 2023-03-21 11:24 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i diff --git a/rtl/inc/objpas.inc b/rtl/inc/objpas.incindex 5479d3698a..ae836317fc 100644--- a/rtl/inc/objpas.inc+++ b/rtl/inc/objpas.inc@@ -351,7 +351,7 @@          var           ovmt: PVmt;-          i: longint;+          i: sizeuint;           intftable: pinterfacetable;           Res: pinterfaceentry;         begin@@ -365,13 +365,14 @@               begin                 i:=intftable^.EntryCount;                 Res:=@intftable^.Entries[0];-                while i>0 do begin-                if Res^.IType = etStandard then-                    ppointer(@(pbyte(instance)[Res^.IOffset]))^:=-                      pointer(Res^.VTable);-                  inc(Res);-                  dec(i);-                end;+                while i>0 do+                begin+                    if Res^.IType = etStandard then+                    ppointer(@(pbyte(instance)[Res^.IOffset]))^:=+                      pointer(Res^.VTable);+                    inc(Res);+                  dec(i);+                  end;               end;             ovmt:=ovmt^.vParent;             end;@@ -385,8 +386,9 @@          inittable : pointer; {$ifdef FPC_HAS_FEATURE_RTTI}            mopinittable : PRTTIRecordOpOffsetTable;+           Res : ^TRTTIRecordOpOffsetEntry; {$endif def FPC_HAS_FEATURE_RTTI}-         i : longint;+           i : longword; {$endif VER3_0}         begin          { the size is saved at offset 0 }@@ -409,12 +411,14 @@                  mopinittable:=RTTIRecordMopInitTable(inittable);                  if assigned(mopinittable) then                      begin-                     {$push}-                       { ensure that no range check errors pop up with the [0..0] array }-                     {$R-}-                       for i:=0 to mopinittable^.Count-1 do-                       TRTTIRecVarOp(mopinittable^.Entries[i].Managment Operator)(PByte(Instance)+mopinittable^.Entries[i].FieldOffset);-                     {$pop}+                       i:=mopinittable^.Count;+                       Res:=@mopinittable^.Entries[0];+                       while i>0 do+                       begin+                           TRTTIRecVarOp(R es^.ManagmentOperator)(PByte(Instance)+Res^.FieldOffset);+                         inc(Res);+                           dec(i);+                       end;                      end;                  end;            end;@@ -930,7 +934,7 @@            TMsgInt = record        class function TObject.GetInterfaceEntry(const iid : tguid) : pinterfaceentry;         var-          i: longint;+          i: sizeuint;           intftable: pinterfacetable;           ovmt: PVmt;         begin@@ -942,11 +946,14 @@            TMsgInt = record           if assigned(intftable) then             {$endif VER3_0}             begin-              for i:=0 to intftable^.EntryCount-1 do+            i:=intftable^.EntryCount;+              result:=@intftable^.Entries[0];+              while i>0 do             begin-                result:=@intftable^.Entries[i];               if assigned(Result^.iid) and IsGUIDEqual(Result^.iid^,iid) then                 Exit;+                inc(Result);+                dec(i);               end;             end;             ovmt := ovmt^.vParent;@@ -956,7 +963,7 @@            TMsgInt = record        class function TObject.GetInterfaceEntryByStr(const iidstr : shortstring) : pinterfaceentry;         var-          i: longint;+        i: sizeuint;           intftable: pinterfacetable;           ovmt: PVmt;         begin@@ -968,11 +975,14 @@            TMsgInt = record             if assigned(intftable) then             {$endif VER3_0}             begin-              for i:=0 to intftable^.EntryCount-1 do+              i:=intftable^.EntryCount;+            result:=@intftable^.Entries[0];+              while i>0 do             begin-                result:=@intftable^.Entries[i];               if assigned(result^.iidstr) and (result^.iidstr^ = iidstr) then                   Exit;+                inc(Result);+              dec(i);               end;             end;             ovmt := ovmt^.vParent; ---------------------------------------------------------------------- runewalsh · 2023-05-04 18:19 No, from my observations, for loop often generates worse code. For now, Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i for i := 0 to N - 1 dobegin        // ...end;  is usually worse than Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i i := 0;// N := precalculate if nontrivialwhile i < N dobegin        // ...        inc(i);end;  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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       procedure InitInterfacePointers(objclass: tclass;instance : pointer);         var          ovmt: PVmt;          intftable: pinterfacetable;          Res,ResEnd: pinterfaceentry;        begin        ovmt := PVmt(objclass);          while assigned(ovmt) and {$ifdef VER3_0}(ovmt^.vIntfTable <> @emptyintf){$else}assigned(ovmt^.vIntfTable){$endif} do            begin              intftable:=ovmt^.vIntfTable;              {$ifdef VER3_0}              if assigned(intftable) then              {$endif VER3_0}              begin                Res:=@intftable^.Entries[0];                ResEnd:=Res+intftable^.EntryCount;                while Res