Please stop coming up with these badly thought out ideas.
Please stop coming up with these badly thought out ideas.I think the intent of @Alextp was to highlight the use of copying (sub)strings when it may not be necessary (e.g. when the copied string is not modified), not to suggest yet more functionality. The following post clearly illustrates that there are existing alternatives that avoids making copies of strings:
It only aids in adding more useless redundant junk to an already over whelmed RTL..
These 3 can be opt'ed already:
In fphttpclient.pp: LowerCase(Copy(S,1,Length(SetCookie)))=SetCookie
In fphttpclient.pp: ((LowerCase(Copy(HTTPHeaders[Result],1,l)))<>h)
In fpreport.pp: SameText(Copy(BaseName,1,9),'TFPReport')
Using StrLComp() and StrLIComp().
Typically, every function that deals with strings should have a counterpart that takes pchar and length to allow for same usage even when a String type is used.The problem will arise when the string has a legal #0 inside. If not done properly the PChar version can stop parsing here and miss the rest of the string.
The problem will arise when the string has a legal #0 inside. If not done properly the PChar version can stop parsing here and miss the rest of the string.
Length should always be respected instead of being lazy and use Copy with MaxInt.Note that recent FPCs allow you to use Copy() with only two parameters when you want the copy to be from Index to Length inclusive -- so no Maxint is needed.
Dealing with strings, pchar is the natural type to use, while String type is more of a user-friendly type to hide address, length, codepage and reference count.
Length should always be respected instead of being lazy and use Copy with MaxInt.Note that recent FPCs allow you to use Copy() with only two parameters when you want the copy to be from Index to Length inclusive -- so no Maxint is needed.
Yes, and if length were always respected we would not have all these buffer underruns in C. As I said: "if done properly"... "When things can go wrong they will go wrong." Or: "Never change a winning team".
I did a lot of these micro-optimizations myself with arguments like "it looks better when I do it this way", "ah, this is a bit faster" etc., and I almost always ended up in introducing a bug somewhere. This is different from fixing a bug. A bug MUST be fixed, micro-optimizations gains nothing.
If a function is not used in your program then it will be smartlinked out, especially if you did indeed enable the smartlink options. If it is not left out then it is indeed used either directly or indirectly.
Dealing with strings, pchar is the natural type to use, while String type is more of a user-friendly type to hide address, length, codepage and reference count.
Hi!
The natural Pascal type is string, not pchar. Turbo Pascal lived years without pchar. I think pchar was first used in version 7 but only with extended syntax switch on. pchar is a concession to the Operating systems but not original Pascal syntax.
Winni
I think pchar was first used in version 7 but only with extended syntax switch on. pchar is a concession to the Operating systems but not original Pascal syntax.
[...] Glad you pointed this out, it shows when pchar was perceived as a needed type.
Delphi1 introduced strpas and strPcopy to convert string <--> pchar
Delphi1 introduced strpas and strPcopy to convert string <--> pchar
Not quite: those two (among others) were already in TP7, in the Strings unit ;D
Don't struggle so useless.
The introduction of the AnsiString and the UTF8 codepage has nothing to do with the pchar.
Ok, let's forget pchar for a second. Can I assume you have no problem with another string type like SubString?
Like it or not you seem to keep defending bugs and claimed features that just don't work.
Also bad code generation with blanket statements in return.
.Ll7:
movl TC_$P$PROGRAM$_$SOMEPROC_$$_defaultB,%edx
leal -36(%ebp),%eax
call fpc_ansistr_assign
// this optimization is weird, since this snapshot has no avx2 ?
vmovdqu TC_$P$PROGRAM$_$SOMEPROC_$$_defaultA,%ymm0
vmovdqu %ymm0,-32(%ebp)
.Ll8:
leal -32(%ebp),%edx
movl $1,%ecx
call P$PROGRAM$_$SOMEPROC_$$_PROC1$array_of_LONGINT
.Ll9:
movl -36(%ebp),%edx
movl $4,%ecx
call P$PROGRAM$_$SOMEPROC_$$_PROC2$array_of_CHAR
.Ll10:
But it looks like the called function makes a copy with getmem and move:
procedure Proc1(XS: array of Integer); begin writeln(length(XS)); end;
1. A subrange of a shortstring is probably an array of char, so proc2 probably needs to be array of char not string1. It indeed seems to be and in the case of an overloaded function with the option of either string or array of char, the compiler chooses the one with array of char.
2. the arguments of proc1 (XS and StR) should probably be const.
What is often forgetten is the ability of fpc to use only a slice of an array as parameter.I didn't know about it up until yesterday :-[.
FPC already seems to have the concept of partial arrays and partial strings, in other languages they're called slices.Those are substrings and they are standardized in ISO 10206 “Extended Pascal”. It’s an FPC extension that this syntax works on other arrays too.
Unfortunately I can't seem to find much documentation about partial strings. […]
I'll have a read, thank you.FPC already seems to have the concept of partial arrays and partial strings, in other languages they're called slices.Those are substrings and they are standardized in ISO 10206 “Extended Pascal”. It’s an FPC extension that this syntax works on other arrays too.
Unfortunately I can't seem to find much documentation about partial strings. […]
Running benchmarks 1000000000 times per function...That passing a string with copy is the slowest is not really a surprise, but the overhead of a partial string passed to a function with const string compared to an array is rather unexpected when you've never used it before. I have expected it to perform more like passing a pointer.
FuncAcceptingString(Copy(InputString, 2, 4))) finished in 18.967 seconds
FuncAcceptingString(InputString[2..5])) finished in 14.641 seconds
FuncAcceptingArray(InputArray[1..4])) finished in 1.776 seconds
FuncAcceptingArray(InputString[2..5])) finished in 1.751 seconds
FuncAcceptingPChar(PChar(InputString), 1, 4)) finished in 1.743 seconds
All accumulators are equal to: 212000000000
[...] but the overhead of a partial string passed to a function with const string compared to an array is rather unexpected when you've never used it before. I have expected it to perform more like passing a pointer.
FPC already seems to have the concept of partial arrays and partial strings, in other languages they're called slices.
[...] but the overhead of a partial string passed to a function with const string compared to an array is rather unexpected when you've never used it before. I have expected it to perform more like passing a pointer.
It's not so surprising; one just has to remember that despite the sintactic similarities the internal structure of strings is different from that of arrays: they always have that more or less complex record before the "char array" itself and the compiler has to build that (so in fact a whole new string) before using it as a parameter, it can't just pass an "array of char" where the function/procedure expects a full blown "String"
That is one of the first lessons in Pascal:
The parameter of a procedure working with a local copy.
If you want to avoid that change it to
procedure Proc1( var XS: array of Integer); begin writeln(length(XS)); end;
Slices won't help here, because the relevant functions that Alextp mentioned (e.g. SameText or LowerCase) take a String and not an array of Char, thus there will be a copy of the string anyway.
Now if local variables or result could be declared to be open arrays
Slices won't help here, because the relevant functions that Alextp mentioned (e.g. SameText or LowerCase) take a String and not an array of Char, thus there will be a copy of the string anyway.
Then perhaps we need new overloaded functions
Especially an open array version of Val
No, we don't. This is also about maintainability. And adding functions for everything and the kitchen sink is not maintainable. Also the main types for strings in Object Pascal are the various String types and not arrays of Char.
Use Forth.
No. Those would need to be allocated on the heap (especially the return value) and then you can just as well use a dynamic array.
No, we don't. This is also about maintainability. And adding functions for everything and the kitchen sink is not maintainable. Also the main types for strings in Object Pascal are the various String types and not arrays of Char.
And what would such a Val do?
Pascal is not the right choice.
Use Forth.
There you can optimize everything.
On speed. On size. On everything.
A Double has a 15..16 significant decimal digits precision.
A Double has a 15..16 significant decimal digits precision.
That is for printing a double
Parsing a string to a double needs to consider all digits: https://www.exploringbinary.com/decimal-to-floating-point-needs-arbitrary-precision/
No, have a look at
https://en.wikipedia.org/wiki/Forth_(programming_language) (https://en.wikipedia.org/wiki/Forth_(programming_language))
I knew that.
It was an attempt at humor.
Obviously I failed.
Bart
Humor is so seldom in this forum (the same with women) so I don't exspect it.Maybe it would "optimize" the forums to have a dating section... <chuckle>
Forth is so-named, because in 1968 "the file holding the interpreter was labeled FOURTH, for 4th (next) generation software, but the IBM 1130 operating system restricted file names to five characters."
Talking about Forth and humor:QuoteForth is so-named, because in 1968 "the file holding the interpreter was labeled FOURTH, for 4th (next) generation software, but the IBM 1130 operating system restricted file names to five characters."
Carbon dating?Because we use an ancient "dead" tongue? :P
For the "young blood":I remember the dread when hearing the disk reader head move back to reread a sector. An ominous sign that the disk may be failing.
A 5 1/4 inch floopy disk had (in the beginning) a capacity of 360 kB and an 8 inch floppy 1.1 MB
Glad that we dont have to fight this battle anymore.
Winni
[Hi!
I remember the dread when hearing the disk reader head move back to reread a sector. An ominous sign that the disk may be failing.
The Discussion goes: How many more BITS beyond bit 54 are needed, before you can decide if 54 is zero or one.
And how many bits you can put in a string255 with digits?????
For the "young blood":
A 5 1/4 inch floopy disk had (in the beginning) a capacity of 360 kB and an 8 inch floppy 1.1 MB
Glad that we dont have to fight this battle anymore.
Carbon dating?Because we use an ancient "dead" tongue? :P
I remember the dread when hearing the disk reader head move back to reread a sector. An ominous sign that the disk may be failing.
My first copy of DOS was in German, don't remember its version.
Time for the very old spanish joke:
DOS dos dos
Winni
No, we don't. This is also about maintainability. And adding functions for everything and the kitchen sink is not maintainable. Also the main types for strings in Object Pascal are the various String types and not arrays of Char.
Thanx to PascalDragon!
And for those who are eager to optimize the whole day and night:
Pascal is not the right choice.
Use Forth.
There you can optimize everything.
On speed. On size. On everything.
Winni
For the "young blood":You're too young; I do remember when floppies could be anywhere from 80 to 120 Kb ... both soft- and hard-sectored, 8" and the then "latest" 5,25" ... not to mention the 3" ones. And ultra-expensive hard-disks with up to .... 5 MiB ;D
A 5 1/4 inch floopy disk had (in the beginning) a capacity of 360 kB and an 8 inch floppy 1.1 MB
You're too young; I do remember when floppies could be anywhere from 80 to 120 Kb ... both soft- and hard-sectored, 8" and the then "latest" 5,25" ... not to mention the 3" ones. And ultra-expensive hard-disks with up to .... 5 MiB ;D
Ah! Those were the days ... 8)
No. Those would need to be allocated on the heap (especially the return value) and then you can just as well use a dynamic array.
But the point is to avoid heap allocations and return a reference to the already existing array
The Trim function would be the best example for that
And open arrays of char is how you make thing maintainable.
For example, there is CompareText
The Val function is especially horrible. 15 Val functions on 64-bit and a lot more:
And what would such a Val do?
It should do what Val always does and convert the string to a number type
But you can't. What if the input array is marked as const? What if the function does not take an array as input at all? The caller can't know how much space to reserve and the function's stack will be removed once it returns, so the only place to return an array is the heap. And as I said: then you can just as well use a dynamic array.
It's not, cause I might want to use the input for something else. And the input of Trim isn't marked as const for no reason.
No, it's not. Also AnsiString involves code page conversions, while ShortString or UnicodeString does not.
The Val intrinsic needs to be able to deal with all these combinations. If it ain't broken, don't fix it.
val (copy ansistring) run-time: 6379 ms
val (ansistring) run-time: 5098 ms
val (shortstring) run-time: 4981 ms
simplestPchar run-time: 1327 ms
simplestPchar (-O4) run-time: 914 ms
simplestPchar (-O4 -Cort) run-time: 2042 ms
run-time: 679 ms
(btw how do you do an unaligned read?)
Thinking a bit more about this. C compilers usually inline the above stated memcpy approach, but I don't know how FPC is handling this. So from a performance perspective it might not be an optimal choice - but it is a general solution.
BTW - the conversion of 8 digits can be done by 3 muls, 3 adds and some shifts+ands - if you are really speed crazy :-)
BTW - the conversion of 8 digits can be done by 3 muls, 3 adds and some shifts+ands - if you are really speed crazy :-)
How?
Something like you multiply by 10, 100, and 10000, and rearrange the digits that they participate at the right number of multiplications?
var
Higher, Lower: QWORD; // Lower contains 8 ASCII digits on entry
Dec( Lower, $3030303030303030 ); // convert to numbers 0-9
Higher := ( Lower * 10 ) shr 8; // no overflow possible
Lower := ( Lower and $00ff00ff00ff00ff ) + ( Higher and $00ff00ff00ff00ff );
Higher := ( Lower * 100 ) shr 16;
Lower := ( Lower and $0000ffff0000ffff ) + ( Higher and $0000ffff0000ffff );
Higher := ( Lower * 10000 ) shr 32;
Lower := ( Lower and $00000000ffffffff ) + Higher;
Code: [Select]var
Higher, Lower: QWORD; // Lower contains 8 ASCII digits on entry
Dec( Lower, $3030303030303030 ); // convert to numbers 0-9
Higher := ( Lower * 10 ) shr 8; // no overflow possible
Lower := ( Lower and $00ff00ff00ff00ff ) + ( Higher and $00ff00ff00ff00ff );
Higher := ( Lower * 100 ) shr 16;
Lower := ( Lower and $0000ffff0000ffff ) + ( Higher and $0000ffff0000ffff );
Higher := ( Lower * 10000 ) shr 32;
Lower := ( Lower and $00000000ffffffff ) + Higher;
old run-time: 606 ms
with 3 mul (wrong): run-time: 624 ms
with 3 mul endian corrected: run-time: 667 ms
program Project1;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes, bbutils, bbutilsbeta,bbdebugtools
{ you can add units after this };
function strTryParseInt(pstart, pend: pchar; out unsigned: UInt64): boolean;
var
length: SizeUInt;
begin
result := false;
if pend <= pstart then exit;
while (pstart < pend) and (pstart^ = '0') do inc(pstart);
length := pend - pstart;
if length > 20 then exit;
unsigned := 0;
while (pstart < pend) do begin
case pstart^ of
'0'..'9': unsigned := unsigned * 10 + UInt64(ord(pstart^) - ord('0'));
else exit;
end;
inc(pstart);
end;
if (length = 20) and (unsigned < 10000000000000000000) then exit;
result := true;
end;
{$AsmMode intel}
function PcharToInt(pstart, pend: pchar; out unsignedResult: UInt64): boolean;
const
selectFirstHalfByte8 = UInt64($F0F0F0F0F0F0F0F0);
decimalZeros8 = UInt64($3030303030303030);
overflowMaxDigit8 = UInt64($0606060606060606);
selectFirstHalfByte4 = UInt32($F0F0F0F0);
decimalZeros4 = UInt32($30303030);
overflowMaxDigit4 = UInt32($06060606);
var
length: SizeUInt;
temp8: UInt64;
temp4: UInt32;
bytes: pbyte;
unsigned: UInt64;
begin
result := false;
if pend <= pstart then exit;
while (pstart < pend) and (pstart^ = '0') do inc(pstart);
length := pend - pstart;
if length > 20 then exit;
if (length = 20) and (pstart^ >= '2') then exit;
unsigned := 0;
if PtrUInt(TObject(pstart)) and 7 = 0 then begin
while pstart + 8 <= pend do begin
temp8 := PUInt64(pstart)^;
if (temp8 and selectFirstHalfByte8) <> decimalZeros8 then exit;
temp8 := temp8 - decimalZeros8;
if ((temp8 + overflowMaxDigit8) and selectFirstHalfByte8) <> 0 then exit;
bytes := @temp8;
unsigned := unsigned * 100000000 + (((((((bytes[0] * 10) + bytes[1])* 10 + bytes[2])* 10 + bytes[3])* 10 + bytes[4])* 10 + bytes[5])* 10 + bytes[6])* 10 + bytes[7];
inc(pstart, 8);
end;
while pstart + 4 <= pend do begin
temp4 := PUInt32(pstart)^;
if (temp4 and selectFirstHalfByte4) <> decimalZeros4 then exit;
temp4 := temp4 - decimalZeros4;
if ((temp4 + overflowMaxDigit4) and selectFirstHalfByte4) <> 0 then exit;
bytes := @temp4;
unsigned := unsigned * 10000 + ((((bytes[0] * 10) + bytes[1])* 10 + bytes[2])* 10 + bytes[3]);
inc(pstart, 4);
end;
end;
while (pstart < pend) do begin
case pstart^ of
'0'..'9': unsigned := unsigned * 10 + UInt64(ord(pstart^) - ord('0'));
else exit;
end;
inc(pstart);
end;
if (length = 20) and (unsigned < 10000000000000000000) then exit;
result := true;
unsignedResult:=unsigned;
end;
function PcharToIntMM(pstart, pend: pchar; out unsignedResult: UInt64): boolean;
const
selectFirstHalfByte8 = UInt64($F0F0F0F0F0F0F0F0);
decimalZeros8 = UInt64($3030303030303030);
overflowMaxDigit8 = UInt64($0606060606060606);
selectFirstHalfByte4 = UInt32($F0F0F0F0);
decimalZeros4 = UInt32($30303030);
overflowMaxDigit4 = UInt32($06060606);
var
length: SizeUInt;
temp8: UInt64;
temp4: UInt32;
bytes: pbyte;
unsigned: UInt64;
var
Higher, Lower: QWORD; // Lower contains 8 ASCII digits on entry
begin
result := false;
if pend <= pstart then exit;
while (pstart < pend) and (pstart^ = '0') do inc(pstart);
length := pend - pstart;
if length > 20 then exit;
if (length = 20) and (pstart^ >= '2') then exit;
unsigned := 0;
if PtrUInt(TObject(pstart)) and 7 = 0 then begin
while pstart + 8 <= pend do begin
temp8 := PUInt64(pstart)^;
if (temp8 and selectFirstHalfByte8) <> decimalZeros8 then exit;
temp8 := temp8 - decimalZeros8;
if ((temp8 + overflowMaxDigit8) and selectFirstHalfByte8) <> 0 then exit;
// bytes := @temp8;
// unsigned := unsigned * 100000000 + (((((((bytes[0] * 10) + bytes[1])* 10 + bytes[2])* 10 + bytes[3])* 10 + bytes[4])* 10 + bytes[5])* 10 + bytes[6])* 10 + bytes[7];
unsigned := unsigned * 100000000;
lower := SwapEndian(temp8);
Higher := ( Lower * 10 ) shr 8; // no overflow possible
Lower := ( Lower and $00ff00ff00ff00ff ) + ( Higher and $00ff00ff00ff00ff );
Higher := ( Lower * 100 ) shr 16;
Lower := ( Lower and $0000ffff0000ffff ) + ( Higher and $0000ffff0000ffff );
Higher := ( Lower * 10000 ) shr 32;
unsigned += ( Lower and $00000000ffffffff ) + Higher;
inc(pstart, 8);
end;
while pstart + 4 <= pend do begin
temp4 := PUInt32(pstart)^;
if (temp4 and selectFirstHalfByte4) <> decimalZeros4 then exit;
temp4 := temp4 - decimalZeros4;
if ((temp4 + overflowMaxDigit4) and selectFirstHalfByte4) <> 0 then exit;
bytes := @temp4;
unsigned := unsigned * 10000 + ((((bytes[0] * 10) + bytes[1])* 10 + bytes[2])* 10 + bytes[3]);
inc(pstart, 4);
end;
end;
while (pstart < pend) do begin
case pstart^ of
'0'..'9': unsigned := unsigned * 10 + UInt64(ord(pstart^) - ord('0'));
else exit;
end;
inc(pstart);
end;
if (length = 20) and (unsigned < 10000000000000000000) then exit;
result := true;
unsignedResult:=unsigned;
end;
function PcharToIntMMWrong(pstart, pend: pchar; out unsignedResult: UInt64): boolean;
const
selectFirstHalfByte8 = UInt64($F0F0F0F0F0F0F0F0);
decimalZeros8 = UInt64($3030303030303030);
overflowMaxDigit8 = UInt64($0606060606060606);
selectFirstHalfByte4 = UInt32($F0F0F0F0);
decimalZeros4 = UInt32($30303030);
overflowMaxDigit4 = UInt32($06060606);
var
length: SizeUInt;
temp8: UInt64;
temp4: UInt32;
bytes: pbyte;
unsigned: UInt64;
var
Higher, Lower: QWORD; // Lower contains 8 ASCII digits on entry
begin
result := false;
if pend <= pstart then exit;
while (pstart < pend) and (pstart^ = '0') do inc(pstart);
length := pend - pstart;
if length > 20 then exit;
if (length = 20) and (pstart^ >= '2') then exit;
unsigned := 0;
if PtrUInt(TObject(pstart)) and 7 = 0 then begin
while pstart + 8 <= pend do begin
temp8 := PUInt64(pstart)^;
if (temp8 and selectFirstHalfByte8) <> decimalZeros8 then exit;
temp8 := temp8 - decimalZeros8;
if ((temp8 + overflowMaxDigit8) and selectFirstHalfByte8) <> 0 then exit;
// bytes := @temp8;
// unsigned := unsigned * 100000000 + (((((((bytes[0] * 10) + bytes[1])* 10 + bytes[2])* 10 + bytes[3])* 10 + bytes[4])* 10 + bytes[5])* 10 + bytes[6])* 10 + bytes[7];
unsigned := unsigned * 100000000;
lower := temp8;
Higher := ( Lower * 10 ) shr 8; // no overflow possible
Lower := ( Lower and $00ff00ff00ff00ff ) + ( Higher and $00ff00ff00ff00ff );
Higher := ( Lower * 100 ) shr 16;
Lower := ( Lower and $0000ffff0000ffff ) + ( Higher and $0000ffff0000ffff );
Higher := ( Lower * 10000 ) shr 32;
unsigned += ( Lower and $00000000ffffffff ) + Higher;
inc(pstart, 8);
end;
while pstart + 4 <= pend do begin
temp4 := PUInt32(pstart)^;
if (temp4 and selectFirstHalfByte4) <> decimalZeros4 then exit;
temp4 := temp4 - decimalZeros4;
if ((temp4 + overflowMaxDigit4) and selectFirstHalfByte4) <> 0 then exit;
bytes := @temp4;
unsigned := unsigned * 10000 + ((((bytes[0] * 10) + bytes[1])* 10 + bytes[2])* 10 + bytes[3]);
inc(pstart, 4);
end;
end;
while (pstart < pend) do begin
case pstart^ of
'0'..'9': unsigned := unsigned * 10 + UInt64(ord(pstart^) - ord('0'));
else exit;
end;
inc(pstart);
end;
if (length = 20) and (unsigned < 10000000000000000000) then exit;
result := true;
unsignedResult:=unsigned;
end;
var s: array of string = ('123', '456', '9223372036854775807', '9223372036854775808', '18446744073709551615');//, '18446744073709551616');
ss: array of shortstring = ('123', '456', '9223372036854775807', '9223372036854775808', '18446744073709551615');
var i, j, code: integer;
v: uint64;
t: String;
begin
i := 2;// high(s);
t := '12345678';
writeln(strTryParseInt(pchar(t), pchar(t) + length(t), v));
writeln(v);
writeln(PcharToIntMM(pchar(t), pchar(t) + length(t), v));
writeln(v);
//exit;
{ startTiming('copy');
for i := 0 to high(s) do begin
for j := 1 to 10000000 do
val(copy(s[i],1,length(s[i])), v, code)
end;
stopTiming('copy');
startTiming('val');
for i := 0 to high(s) do begin
for j := 1 to 10000000 do
val(s[i], v, code)
end;
stopTiming('val');
startTiming('valss');
for i := 0 to high(s) do begin
for j := 1 to 10000000 do
val(ss[i], v, code)
end;
stopTiming('valss'); }
startTiming('new');
for i := 0 to high(s) do begin
for j := 1 to 10000000 do
PcharToInt(pchar(s[i]), pchar(s[i]) + length(s[i]), v)
end;
stopTiming('new');
startTiming('new');
for i := 0 to high(s) do begin
for j := 1 to 10000000 do
PcharToIntMMWrong(pchar(s[i]), pchar(s[i]) + length(s[i]), v)
end;
stopTiming('new');
startTiming();
for i := 0 to high(s) do begin
for j := 1 to 10000000 do
PcharToIntMM(pchar(s[i]), pchar(s[i]) + length(s[i]), v)
end;
stopTiming();
end.
Point is - if you read in the ASCII digits from memory, then you have to differentiate between little & big endian CPU in the above!
However - if you really want to dive into implementing a faster 'Val' then maybe you want to take a look at this - https://lemire.me/blog/2021/01/29/number-parsing-at-a-gigabyte-per-second/
>PcharToUInt, PcharToUInt64, TryPcharToUInt, TryPcharToUInt64, or PcharToUIntTry, or PcharToUInt64TryI think better name them with 'Buffer...' instead of 'PChar' (PChar sounds not nice). For Widestring buffer, name them with 'BufferW...'.
[snip]
Although I had a bug that it only used the 8-digit fast path when there where at least 9 digits. The condition in the loop should have been while pstart + 8 <= pend do begin rather than while pstart + 8 < pend do begin. Changing that made it somewhat faster. Or sometimes slower. It is very fiddly to benchmark. The branch predicator appears to be very randomPoint is - if you read in the ASCII digits from memory, then you have to differentiate between little & big endian CPU in the above!
And the code got exactly the wrong endianess. Everything comes out in reverse.
I had to put in a SwapEndian but that call slow it down too much. FPC not inlining such functions is another big RTL problem.
Inline assembler bswap is even worse because it apparently prevents optimizations.
However - if you really want to dive into implementing a faster 'Val' then maybe you want to take a look at this - https://lemire.me/blog/2021/01/29/number-parsing-at-a-gigabyte-per-second/
That is for float. Integer should always be faster than float.
I have already convinced Bero to implement that. But I am not sure he got everything right.
I am absurdly bad at naming.In that case, I hope your wife named the kids. ;)
I am absurdly bad at naming.In that case, I hope your wife named the kids. ;)