Jump to content

Stefan Glienke

Members
  • Content Count

    1366
  • Joined

  • Last visited

  • Days Won

    130

Everything posted by Stefan Glienke

  1. Stefan Glienke

    SysUtils, AnsiString

    Yes, ditch AnsiString.
  2. TestInsight is test framework agnostic so it could work with GoogleTests - apart from direct support of the different run options from the TestInsight window itself you could probably implement that support yourself just from studying the sources that ship with TestInsight.
  3. Yes, developing generic code can be absolutely frustrating depending on the Delphi version. And codegen within generics can be less effective than it would if you would write the same identical code directly. Guess you could get a glimpse of what I have to endure
  4. ⚠️ Disclaimer: Micro optimization advice ahead 😉 You only do it the other way around (interpret a bool as a number) to embed conditionals into your algorithm to avoid branching.
  5. Your assessment on the C# dictionary is incorrect - in fact it uses a pretty nice design - the actual "hashtable" is just an array of integer. Pretty compact. They store the indices to the other array where the actual items reside in and there are no gaps because those items are stored in a contiguous way. No wasting space if you have a low load factor. Yes, there is one indirection but usually, unless you have so many items that these two does not fit into cache anymore this is pretty darn fast. Collisions are being solved by linking the items that collided - can certainly argue there is some wasted space because all items that never were subject to a collision have that next pointer being unused. There are other implementations such as the one in python or the one we implemented in Spring4d (which is very similar to the one in Python) that uses probing with some factor to avoid clustering. More on the C# implementation https://blog.markvincze.com/back-to-basics-dictionary-part-2-net-implementation/
  6. If you say so - people have profiled the crap out of hashtables - and none that is halfway decent is using unnecessary memory indirections or wastes space by storing pointers. The point with strings might be true but then you only pay the price when using strings - and chances are that the string you are using to look up the item is already in your cache.
  7. Stefan Glienke

    Round up to next $5

    Then only +1 when it has decimal places 😉 function RoundUpToFive(AValue: Double): Integer; begin Result := ((Trunc(AValue) div 5) + Byte(Frac(AValue) > 0)) * 5; end;
  8. The opposite - by forcing many specialized types into Spring.Collections.dcu it avoids stuffing them into each and every other dcu that might use them. Try following: create 10 units each with a simple class and a function that calls TCollections.CreateList<TThatClass> - now compile the project once with 1.2 and once with 2.0 and look at the dcus. Also I can only repeat my suggestion: precompile Spring and use its dcus and don't recompile it over and over every time you compile your project. Because then you will gain the main benefit - most commonly used specializations are in Spring.Collections.dcu and the compiler just needs to reference them. Compiletime and memory usage of the compiler for projects using Spring 2.0 have dropped significantly.
  9. https://delphisorcery.blogspot.com/2021/06/introducing-springbenchmark-port-of.html
  10. Stefan Glienke

    Convert C# function to delphi

    Good question - probably because I did not look into the implementation as the API does not tell it automatically does UTF8 conversion which it usually does not. But as far as I can see GetHashString encodes as hex and not as base64
  11. Stefan Glienke

    Convert C# function to delphi

    uses System.Hash, System.NetEncoding, System.SysUtils; function GenerateDigest: string; var bodyText: string; payloadBytes: TBytes; sha256Hash: THashSHA2; begin bodyText := '{ your JSON payload }'; sha256Hash := THashSHA2.Create(); sha256Hash.Update(TEncoding.UTF8.GetBytes(bodyText)); payloadBytes := sha256Hash.HashAsBytes; Result := 'SHA-256=' + TNetEncoding.Base64.EncodeBytesToString(payloadBytes); end;
  12. 64bit StrPos raises AV when either string is empty - also the overall design of your API is a bit weird to me (or I misunderstand something) - why do I need different functions when doing a one time search vs repeated searches. For example when using StrPos in a loop it gets terribly slow because of all the initialization happen in that function.
  13. http://docwiki.embarcadero.com/Libraries/Sydney/en/System.MulDivInt64 - since all platforms apart from win32 are 64bit anyway no need to worry Int64 might be overkill.
  14. Exactly - this one of the biggest gripes I have with Delphi/Pascal - the two separate concerns of allocating memory and initialization are mixed together. This is also what prevents having stack objects. C++ for example does it differently using new to allocate memory which also calls the ctor but they are not mixed - you can also just use an object on the stack or a vector/array of objects without memory indirection.
  15. Stefan Glienke

    Out parameter is read before set

    Out parameters are just bad var parameters - just saying.
  16. Delphi developers usually have the association of allocating memory when they read constructor - and thus think that for a value type it is wrong to have one. C++ has struct ctors and nobody (that I know) thinks they are wrong - because usually, C++ developers don't immediately think of memory allocation when they see a ctor.
  17. In fact, its a regression that happened just recently: https://quality.embarcadero.com/browse/RSP-33455 sometime in 10.4.x
  18. Looking at asm without $O+ is pointless. Here is the code from both options with $O+: RecordCtorVsClassFunc.dpr.26: begin 00408F94 51 push ecx RecordCtorVsClassFunc.dpr.27: LRecCon := TRecConstructor.Create( 42 ); 00408F95 8BC4 mov eax,esp 00408F97 BA2A000000 mov edx,$0000002a 00408F9C E813000000 call TRecConstructor.Create RecordCtorVsClassFunc.dpr.28: end; 00408FA1 5A pop edx 00408FA2 C3 ret 00408FA3 90 nop RecordCtorVsClassFunc.dpr.33: begin 00408FA4 51 push ecx RecordCtorVsClassFunc.dpr.34: LRecCls := TRecClassFunc.Create( 84 ); 00408FA5 B854000000 mov eax,$00000054 00408FAA E809000000 call TRecClassFunc.Create 00408FAF 890424 mov [esp],eax RecordCtorVsClassFunc.dpr.35: end; 00408FB2 5A pop edx 00408FB3 C3 ret Because you have a record here that fits into a register and does not have any managed type fields it will simply be returned via eax when using a function. When using a ctor it passes its address via eax. Now let's add a second Integer field to both records and look again: RecordCtorVsClassFunc.dpr.26: begin 00408F94 83C4F8 add esp,-$08 RecordCtorVsClassFunc.dpr.27: LRecCon := TRecConstructor.Create( 42 ); 00408F97 8BC4 mov eax,esp 00408F99 BA2A000000 mov edx,$0000002a 00408F9E E819000000 call TRecConstructor.Create RecordCtorVsClassFunc.dpr.28: end; 00408FA3 59 pop ecx 00408FA4 5A pop edx 00408FA5 C3 ret 00408FA6 8BC0 mov eax,eax RecordCtorVsClassFunc.dpr.33: begin 00408FA8 83C4F8 add esp,-$08 RecordCtorVsClassFunc.dpr.34: LRecCls := TRecClassFunc.Create( 84 ); 00408FAB 8BD4 mov edx,esp 00408FAD B854000000 mov eax,$00000054 00408FB2 E809000000 call TRecClassFunc.Create RecordCtorVsClassFunc.dpr.35: end; 00408FB7 59 pop ecx 00408FB8 5A pop edx 00408FB9 C3 ret Whooop, no difference. Now let's add another field - of type string - I stripped the prologue and epilogue from the asm shown to reduce the noise: RecordCtorVsClassFunc.dpr.28: begin ... RecordCtorVsClassFunc.dpr.29: LRecCon := TRecConstructor.Create( 42 ); 004099B1 8D45E8 lea eax,[ebp-$18] 004099B4 BA2A000000 mov edx,$0000002a 004099B9 E89A000000 call TRecConstructor.Create 004099BE 8D55E8 lea edx,[ebp-$18] 004099C1 8D45F4 lea eax,[ebp-$0c] 004099C4 8B0DD0984000 mov ecx,[$004098d0] 004099CA E871D2FFFF call @CopyRecord RecordCtorVsClassFunc.dpr.30: end; ... RecordCtorVsClassFunc.dpr.35: begin ... RecordCtorVsClassFunc.dpr.36: LRecCls := TRecClassFunc.Create( 84 ); 00409A22 8D55F4 lea edx,[ebp-$0c] 00409A25 B854000000 mov eax,$00000054 00409A2A E82D000000 call TRecClassFunc.Create RecordCtorVsClassFunc.dpr.37: end; ... Now we see a missing optimization when using the ctor opposed to the function - as you might know functions returning a managed type (such as a record with at least one field of a managed type such as string) are actually passed as var param (last parameter after all others, thus edx here). The ctor code however uses an unnecessary temp copy.
  19. Stefan Glienke

    Spring4D TPair removed

    There is no new released master - there was one single commit for an inc file to fix a compiler issue on OSX64
  20. Stefan Glienke

    Spring4D TPair removed

    1.2 never had TPair - it got introduced in develop and will be in 2.0
  21. Stefan Glienke

    Delphi 10.4.2 always recompiling in IDE

    IIRC there was (maybe still is?) an issue which causes relinking when error insight is enabled - I think it was among the many things that IDEFixPack patched.
  22. Certainly better than the RTL - however if you want it really fast and justify implementing in asm then SSE should be used. For reference: https://www.strchr.com/strcmp_and_strlen_using_sse_4.2 Also, people might not want to use your code because of GPLv3 unless they put it into an extra DLL.
  23. Any branching (if, case) is expensive - especially when it cannot be predicted because you have a random pattern. Here is how it's done fast: procedure CountAllItems4(const aDocuments: TDocuments; var counts: array of Integer); var i: integer; begin counts[0] := 0; counts[1] := 0; counts[2] := 0; for i := Low(aDocuments) to High(aDocuments) do Inc(counts[Ord(aDocuments[i].DocType)]); end; var counts: array[TDocType] of Integer; begin ... CountAllItems4(vDocuments, counts); q := counts[dtQuote]; o := counts[dtOrder]; i := counts[dtInvoice];
  24. We can certainly argue over the term "big" - but performance is either CPU or memory bound - and with hash table items be scattered all over the heap it most certainly will be memory bound. Hash tables are complex beasts and there are many considerations when designing one but usually, you want to avoid blasting your items all over the heap. Interesting read: https://leventov.medium.com/hash-table-tradeoffs-cpu-memory-and-variability-22dc944e6b9a
×