Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation on 11/21/20 in Posts

  1. Mahdi Safsafi

    Hex2Binary

    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.
  2. julkas

    NERO5 chess engine

    Nero 5 is a chess program written in Pascal by Jari Huikari.
  3. Stefan Glienke

    Hex2Binary

    "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;
  4. Bernard

    Hex2Binary

    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
  5. Stefan Glienke

    Hex2Binary

    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
  6. Stefan Glienke

    Hex2Binary

    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.
  7. Stefan Glienke

    64 bit compiler running out of memory

    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.
  8. Stefan Glienke

    Hex2Binary

    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.
  9. FPiette

    Speed of Graphics32

    You forgot Direct2D bitmaps. Recent Delphi versions has a Direct2D canvas which can be used.
  10. Mahdi Safsafi

    Hex2Binary

    @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;
  11. Attila Kovacs

    Organizing enums

    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;
  12. 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.
×