Eine eigene Implementation:
Code: Alles auswählen
function isIntLoop(s: String): Boolean; inline;
var
i: Integer;
begin
Result := Length(s) > 0;
for i:=1 to Length(s) do
if not (s[i] in ['0'..'9']) then
exit(False);
end;
Braucht auf O3 etwa nur die hälfte (und 60% auf O1) der Zeit von Val:
Code: Alles auswählen
function isIntVal(s: String): Boolean; inline;
var dummy: Integer;
code: Word;
begin
val(s, dummy, code);
Result := code = 0;
end;
Also deutlich effizienter. Der Grund dafür ist das val effektiv mehr macht. Val konvertiert strings zu Integers und gibt nur zurück ob es gefailed hat. Die konvertierung ist natürlich deutlich komplizierter, da sie schlicht weg mehr macht:
Code: Alles auswählen
function strToIntExample(s: String): Integer;
var
i: Integer;
begin
if Length(s) = 0 then error;
Result := 0;
for i:=1 to Length(s) do
begin
Result *= 10;
if (s[i] in ['0'..'9']) then
Result += ord(s[i]) - ord('0')
else
error
end;
end;
Dementsprechend sind Funktionen wie TryStrToInt oder auch StrToIntDef langsamer als eine hand geschriebene funktion, denn sie führen code aus den man schlicht weg nicht braucht.
Würde man LLVM als backend benutzen könnte der Optimizer wahrscheinlich bei Whole-Program optimization feststellen das das ergebnis von val nicht verwendet wird und dieses wegoptimieren (sodass man dann bei der selben funktionaltiät wie die handgeschriebene funktion endet), allerdings ist der FPC Optimizer nicht so gut wie LLVM, weshalb dieser unterschied zu stande kommt.
Was mir dabei auch aufgefallen ist, ein For-in loop ist deutlich langsamer als ein For-i loop. also mein beispiel von oben
Code: Alles auswählen
function IsInteger(s: string): boolean; inline;
var
c: char;
begin
Result := s.length > 0;
for c in s do
if not (c in ['0'..'9']) then
exit(False); // oder Result := False; und break;
end;
braucht etwa 40% länger als die selbe funktion mit nem For-I loop. Das ist äußerst interresant, weil in C++ ein For-Each mit Iterator nach dem Optimieren (mit LLVM oder auch mit GCC) tatsächlich den selben assembly erzeugt wie ein For-I loop nach dem optimieren. Daran erkennt man auch mal wieder das der FPC nicht so gut im optimieren ist wie vergleichbare C++ compiler.
Da die meisten Pascal programme allerdings nicht auf optimale performance aus sind, ist das meist kein Problem, sollte man nur im Hinterkopf behalten, das sich der FPC nicht so sehr für performance kritische anwendungen eignet
Programm:
Code: Alles auswählen
program Project1;
{$mode objfpc}{$H+}
uses
LCLIntf;
type TStringArray = array of String;
type TBooleanArray = array of Boolean;
function GenRandomString: String;
var
len, i: integer;
begin
len := Random(4) + 2;
SetLength(Result, len);
for i:=1 to len do
Result[i] := chr(Random(15) + ord('0'));
end;
function GenRandomStrings(n: Integer): TStringArray;
var
i: Integer;
begin
SetLength(Result, n);
for i:=0 to n-1 do
Result[i] := GenRandomString;
end;
function isIntVal(s: String): Boolean; inline;
var dummy: Integer;
code: Word;
begin
val(s, dummy, code);
Result := code = 0;
end;
function isIntLoop(s: String): Boolean; inline;
var
i: Integer;
begin
Result := Length(s) > 0;
for i:=1 to Length(s) do
if not (s[i] in ['0'..'9']) then
exit(False);
end;
var
arr: TStringArray;
valResults: TBooleanArray;
loopResults: TBooleanArray;
start: Cardinal;
valTime, loopTime: Cardinal;
i: Integer;
begin
Randomize;
arr := GenRandomStrings(10000000);
SetLength(valResults, Length(Arr));
SetLength(loopResults, Length(Arr));
start := GetTickCount;
for i:=0 to Length(arr) -1 do
loopResults[i] := isIntLoop(arr[i]);
loopTime := GetTickCount-start;
start := GetTickCount;
for i:=0 to Length(arr) -1 do
valResults[i] := isIntVal(arr[i]);
valTime := GetTickCount-start;
WriteLn('Val: ', valTime);
WriteLn('loop: ', loopTime);
end.