Leaderboard
Popular Content
Showing content with the highest reputation on 11/21/20 in Posts
-
Benchmark does not always tell the full story 🙂 Unlike my code, Yours isn't cache friendly const BinaryValues: array [0..15] of string = ( '0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111' ); Let's see what happens for x64 : You need 16 * SizeOf(Pointer) to store string reference = 16 * 8 = 128 bytes = 2 Cache Line. You need 16 * (4 * SizeOf(Char) + 2 byte(null terminated) + SizeOf(StrRec)) to store data = 16 *(4 * 2 + 2 + 16) = 416 bytes = 7 Cache Line. N.B: I supposed that compiler did a great job by placing your data in a continuous region. -------------------------------------------------------- You ended up consuming 416 + 128 = 544 bytes. You ended up consuming 9 Cache Line. type TChar4 = array[0..3] of Char; PChar4 = ^TChar4; const Table1: array['0'..'9'] of TChar4 = ('0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001'); Table2: array['a'..'f'] of TChar4 = ('1010', '1011', '1100', '1101', '1110', '1111'); I only need 16 * (4 * SizeOf(Char)) to store data = 16 * (4 * 2) = 128 bytes = 2 Cache Line.
-
Nero 5 is a chess program written in Pascal by Jari Huikari.
-
"Get your conditional jumps and error handling garbage outta my hot loop, kay?" function HexToBinStefan(const HexValue: string): string; // put the exception stuff into a subroutine to not pollute our routine procedure Error(c: PChar; s: string); begin raise EConvertError.CreateFmt('Invalid hex digit ''%s'' found in ''%s''', [c^, s]); end; label _Error; type TChar4 = array[0..3] of Char; PChar4 = ^TChar4; {$POINTERMATH ON} PInteger = ^Integer; {$POINTERMATH OFF} const Table: array[0..22] of TChar4 = ( '0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001', // 0-9 'xxxx', 'xxxx', 'xxxx', 'xxxx', 'xxxx', 'xxxx', 'xxxx', // :-@ - unused '1010', '1011', '1100', '1101', '1110', '1111'); // A-F var HexDigit: PChar; P: PChar4; i, n: Cardinal; begin // do not use PChar cast because that causes a call to UStrToPWChar // we don't need that special PChar to #0 when HexValue is empty HexDigit := Pointer(HexValue); if HexDigit = nil then Exit; // we know that HexDigit is not nil so we can avoid the conditional jump from Length // this also directly moves it into the correct register for the SetLength call SetLength(Result, PInteger(HexDigit)[-1] * 4); P := PChar4(Result); for i := 1 to PInteger(HexDigit)[-1] do begin // subtract 48 to make '0'-'9' 0-9 which enables unconditionally downcasing any upper case char // when we hit the #0 it will simply produce an invalid value for n that we will break on next n := Cardinal(Integer(Ord(HexDigit^)) - 48) and not 32; // avoid one check by simply subtracting 10 and checking the invalid range of 10-16 // thank you godbolt.org and amazingly optimizing c++ compilers for that idea! <3 if (Cardinal(Integer(n)-10) <= 6) or (n > 22) then goto _error; P^ := Table[n]; Inc(P); Inc(HexDigit); end; Exit; _error: Error(HexDigit, HexValue); end;
-
This has turned into a great thread for learning about speed improvements by simple tweaks. Using Sets instead of Case function HexToBinMahdi5(const HexValue: string): string; type TChar4 = array [0 .. 3] of Char; PChar4 = ^TChar4; const Table1: array ['0' .. '9'] of TChar4 = ('0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001'); Table2: array ['a' .. 'f'] of TChar4 = ('1010', '1011', '1100', '1101', '1110', '1111'); Table3: array ['A' .. 'F'] of TChar4 = ('1010', '1011', '1100', '1101', '1110', '1111'); var HexDigit: Char; P: PChar4; begin SetLength(Result, Length(HexValue) * 4); P := PChar4(Result); for var i: integer := low(HexValue) to high( HexValue ) do begin if HexValue[i] in [ '0' .. '9'] then P^ := Table1[HexValue[i]] else if HexValue[i] in [ 'a' .. 'f'] then P^ := Table2[HexValue[i]] else if HexValue[i] in [ 'A' .. 'F'] then P^ := Table3[HexValue[i]] else raise EConvertError.CreateFmt('Invalid hex digit ''%s'' found in ''%s''', [HexDigit, HexValue]); Inc(P); end; end; Compiled 32bit Release Pascal lookup (Heff): 4091 Pascal lookup (Mahdi): 4107 Pascal lookup (Mahdi 3 Table): 4264 Pascal lookup (Mahdi 3 Table Set): 3665 Pascal lookup (Mahdi 3 Table Set Local Var loop): 2951
-
Kinda pointless to limit the power of sse to handling single characters instead of simply processing multiple characters at once - especially since you now have a call inside the loop slowing stuff down significantly plus having to move the same stuff over and over into xmm2-4. Using simd should be 2-10times faster than the regular 1 char in a loop implementation
-
Certainly not in a microbenchmark where probably everything fits into L1 cache. Not so in a real program where that array of string might be placed in different locations than the strings it contains.
-
64 bit compiler running out of memory
Stefan Glienke replied to Dave Novo's topic in RTL and Delphi Object Pascal
Kids these days - making them 64bit would be the lazy solution - being more reasonable with allocating memory would be the good one. Especially with confirmed performance issues in the compiler that are caused by unnecessary heap allocations. -
That was a typo that David copied - my first version had 3 checks in the loop where {$B+} made it better, now with only 2 checks I don't need that anymore - see my post with the currently best version.
-
You forgot Direct2D bitmaps. Recent Delphi versions has a Direct2D canvas which can be used.
-
@David Heffernan Few remarks about your code if you don't mind : 1- Its pointless to use string when characters are fixed in size ... Simply use static array of X char. 2- Its also pointless to calculate index when you already used a case ... Simply declare your array using char-range. In your case, compiler generated additional instructions to compute the index. function HexToBin2(const HexValue: string): string; type TChar4 = array [0 .. 3] of Char; PChar4 = ^TChar4; const Table1: array ['0' .. '9'] of TChar4 = ('0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111', '1000', '1001'); Table2: array ['a' .. 'f'] of TChar4 = ('1010', '1011', '1100', '1101', '1110', '1111'); var HexDigit: Char; P: PChar4; begin SetLength(Result, Length(HexValue) * 4); P := PChar4(Result); for HexDigit in HexValue do begin case HexDigit of '0' .. '9': P^ := Table1[HexDigit]; 'a' .. 'f': P^ := Table2[HexDigit]; 'A' .. 'F': P^ := Table2[Chr(Ord(HexDigit) xor $20)]; else raise EConvertError.CreateFmt('Invalid hex digit ''%s'' found in ''%s''', [HexDigit, HexValue]); end; Inc(P); end; end;
-
Organizing enums
Attila Kovacs replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
He wants to _find_ the appropriate enum and not obfuscate it deeper. It should be a task for the IDE which fail big time in it, even the help provided by emba is trying to hide which are the possible values. Still, I would not say that your example is a "better way" but you are mixing variables with types. So it should be something like: type TGEnums = class type THTMLType = (htTemplate, htStatic, htHeader, htCustom); end; var x: TGEnums.THTMLType; begin x := TGEnums.THTMLType.htTemplate; --- or --- type THTMLType = (htTemplate, htStatic, htHeader, htCustom); TGEnums = class type HTMLType = THTMLType; end; var x: THTMLType; begin x := TGEnums.HTMLType.htTemplate; -
I don't understand this CONST record argument behavior
David Heffernan replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
Wrong. The answer to my question is yes. SubItems: TList<TSubItem> When you access the items, a function getter is called, and that's where the copy comes from. That function getter has to assign its result somewhere and the compiler makes a temporary local variable for it. Hence the record init etc. Access the array directly and there will be no copy. aItem.SubItems.List[i] The const arg works as you expect.