`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;

I put it in, but it did not help.

`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.

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 random

Point 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.

A bigger issue is how to name this function. Could be PcharToUInt, PcharToUInt64, TryPcharToUInt, TryPcharToUInt64, or PcharToUIntTry, or PcharToUInt64Try. Try at the beginning is more common, but Try at the end is easier to find in the Lazarus completion list. But there is not other PcharTo* function in the RTL.

Could be TextToUInt, TextToUInt64, TryTextToUInt, TryTextToUInt64, TextToUIntTry, or TextToUInt64Try, because there is sysutils.TextToFloat(). But that is an odd one, a Try function without Try in its name and the only one of its kind.

Could be StrToUInt, StrToUInt64, TryStrToUInt, TryStrToUInt64, or StrToUIntTry, TryStrToUInt64Try, because the overload resolving can find it from its type, so the type does not need to be in the name. Nor the 64.

But the classical one also handles other bases. Perhaps DecimalStrToUInt, DecimalStrToUInt64, TryDecimalStrToUInt, TryDecimalStrToUInt64, DecimalStrToUIntTry, DecimalStrToUInt64Try.

StrDecimalToUInt, StrDecimalToUInt64, TryStrDecimalToUInt, TryStrDecimalToUInt64, or StrDecimalToUIntTry, StrDecimalToUInt64Try

Could also drop the string type, TryParse, or TryParseDecimal or TryParseDecimalNumber