Skip to content

Optimize Low and High for Integer

Summary

Looking for a weird slowness in a code, I think I find a weird problem in optimizer.

System Information

Lazarus 2.3.0 (rev main-2_3-1804-g43e385a90b) FPC 3.3.1 x86_64-win64-win32/win64

Example Project

program project1;

uses
  SysUtils;

  function Check1Integer(Index: Integer; const Value: TCharArray; const A: TCharArray): Integer; inline;
  var
    I, J: Integer;
  begin
    for I := Index to High(Value) do
      for J := Low(A) to High(A) do
        if Value[I] = A[J] then
          Exit(I);
    Result := -1;
  end;

  function Check2Integer(Index: Integer; const Value: TCharArray; const A: TCharArray): Integer; inline;
  var
    I, J, L, H: Integer;
  begin
    L := Low(A);
    H := High(A);
    for I := Index to High(Value) do
      for J := L to H do
        if Value[I] = A[J] then
          Exit(I);
    Result := -1;
  end;

  function Test1Check1Integer(const Value: TCharArray; const A: TCharArray): Integer;
  var
    I: Integer;
  begin
    Result := 0;
    I := 0;
    repeat
      I := Check1Integer(I, Value, A);
      I += 1;
      Result += 1;
    until I = 0;
    Result -= 1;
  end;

  function Test1Check2Integer(const Value: TCharArray; const A: TCharArray): Integer;
  var
    I: Integer;
  begin
    Result := 0;
    I := 0;
    repeat
      I := Check2Integer(I, Value, A);
      I += 1;
      Result += 1;
    until I = 0;
    Result -= 1;
  end;

  procedure Test2Check1Integer(const Value: TCharArray; const A: TCharArray);
  var
    I: Integer;
  begin
    I := 0;
    repeat
      I := Check1Integer(I, Value, A);
      I += 1;
    until I = 0;
  end;

  procedure Test2Check2Integer(const Value: TCharArray; const A: TCharArray);
  var
    I: Integer;
  begin
    I := 0;
    repeat
      I := Check2Integer(I, Value, A);
      I += 1;
    until I = 0;
  end;

  procedure Test3Check1Integer(const Value: TCharArray; const A: TCharArray);
  var
    I: Integer;
  begin
    for I := 0 to High(Value) do
      Check1Integer(I, Value, A);
  end;

  procedure Test3Check2Integer(const Value: TCharArray; const A: TCharArray);
  var
    I: Integer;
  begin
    for I := 0 to High(Value) do
      Check2Integer(I, Value, A);
  end;

  function Check1Int64(Index: Int64; const Value: TCharArray; const A: TCharArray): Int64; inline;
  var
    I, J: Int64;
  begin
    for I := Index to High(Value) do
      for J := Low(A) to High(A) do
        if Value[I] = A[J] then
          Exit(I);
    Result := -1;
  end;

  function Check2Int64(Index: Int64; const Value: TCharArray; const A: TCharArray): Int64; inline;
  var
    I, J, L, H: Int64;
  begin
    L := Low(A);
    H := High(A);
    for I := Index to High(Value) do
      for J := L to H do
        if Value[I] = A[J] then
          Exit(I);
    Result := -1;
  end;

  function Test1Check1Int64(const Value: TCharArray; const A: TCharArray): Int64;
  var
    I: Int64;
  begin
    Result := 0;
    I := 0;
    repeat
      I := Check1Int64(I, Value, A);
      I += 1;
      Result += 1;
    until I = 0;
    Result -= 1;
  end;

  function Test1Check2Int64(const Value: TCharArray; const A: TCharArray): Int64;
  var
    I: Int64;
  begin
    Result := 0;
    I := 0;
    repeat
      I := Check2Int64(I, Value, A);
      I += 1;
      Result += 1;
    until I = 0;
    Result -= 1;
  end;

  procedure Test2Check1Int64(const Value: TCharArray; const A: TCharArray);
  var
    I: Int64;
  begin
    I := 0;
    repeat
      I := Check1Int64(I, Value, A);
      I += 1;
    until I = 0;
  end;

  procedure Test2Check2Int64(const Value: TCharArray; const A: TCharArray);
  var
    I: Int64;
  begin
    I := 0;
    repeat
      I := Check2Int64(I, Value, A);
      I += 1;
    until I = 0;
  end;

  procedure Test3Check1Int64(const Value: TCharArray; const A: TCharArray);
  var
    I: Int64;
  begin
    for I := 0 to High(Value) do
      Check1Int64(I, Value, A);
  end;

  procedure Test3Check2Int64(const Value: TCharArray; const A: TCharArray);
  var
    I: Int64;
  begin
    for I := 0 to High(Value) do
      Check2Int64(I, Value, A);
  end;

var
  A: TCharArray = (#0);
  Tick: QWord;
  Value: TCharArray;
begin
  SetLength(Value, 1000000000);

  Tick := GetTickCount64;
  Test1Check1Integer(Value, A);
  WriteLn('Test1Check1Integer: ', GetTickCount64 - Tick);

  Tick := GetTickCount64;
  Test1Check2Integer(Value, A);
  WriteLn('Test1Check2Integer: ', GetTickCount64 - Tick);

  Tick := GetTickCount64;
  Test2Check1Integer(Value, A);
  WriteLn('Test2Check1Integer: ', GetTickCount64 - Tick);

  Tick := GetTickCount64;
  Test2Check2Integer(Value, A);
  WriteLn('Test2Check2Integer: ', GetTickCount64 - Tick);

  Tick := GetTickCount64;
  Test3Check1Integer(Value, A);
  WriteLn('Test3Check1Integer: ', GetTickCount64 - Tick);

  Tick := GetTickCount64;
  Test3Check2Integer(Value, A);
  WriteLn('Test3Check2Integer: ', GetTickCount64 - Tick);


  Tick := GetTickCount64;
  Test1Check1Int64(Value, A);
  WriteLn('Test1Check1Int64: ', GetTickCount64 - Tick);

  Tick := GetTickCount64;
  Test1Check2Int64(Value, A);
  WriteLn('Test1Check2Int64: ', GetTickCount64 - Tick);

  Tick := GetTickCount64;
  Test2Check1Int64(Value, A);
  WriteLn('Test2Check1Int64: ', GetTickCount64 - Tick);

  Tick := GetTickCount64;
  Test2Check2Int64(Value, A);
  WriteLn('Test2Check2Int64: ', GetTickCount64 - Tick);

  Tick := GetTickCount64;
  Test3Check1Int64(Value, A);
  WriteLn('Test3Check1Int64: ', GetTickCount64 - Tick);

  Tick := GetTickCount64;
  Test3Check2Int64(Value, A);
  WriteLn('Test3Check2Int64: ', GetTickCount64 - Tick);

  ReadLn;
end.

What is the current bug behavior?

These are the times, In all cases the Check1 and Check2 versions should take the same time or Check2 be slower. But it is not the case and even different results for Integer and Int64. This issue happen for three cases I marked in this log.

Test1Check1Integer: 1750[1]
Test1Check2Integer: 1406[1]
Test2Check1Integer: 1953
Test2Check2Integer: 1953
Test3Check1Integer: 1735[2]
Test3Check2Integer: 1515[2]
Test1Check1Int64: 1297
Test1Check2Int64: 1734
Test2Check1Int64: 1938[3]
Test2Check2Int64: 1734[3]
Test3Check1Int64: 1313
Test3Check2Int64: 1297

Here is the assembly output: project1.s

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information