Jump to content

Stefan Glienke

Members
  • Content Count

    1365
  • Joined

  • Last visited

  • Days Won

    130

Everything posted by Stefan Glienke

  1. My profile shows what I am using in production at work. That does not mean I don't also have access to 11.1 which had this issue addressed 😉
  2. Fun fact: it does with {$WARN IMPLICIT_CONVERSION_LOSS ON} (which is not turned on by default - probably because it would produce a ton of those in existing code)
  3. IIRC in Delphi 7, you have to list all fields in a const record declaration while in later versions you can omit some as I did - they are then automatically initialized by their default value. And depending on the usage of such a record they don't matter like in my case because I never need the CurrencyString. And you don't need it either because JSON has no currency representation. If you need that usually its represented by different fields, the amount and the currency abbreviation Personally, I could not care less about a Delphi version from almost 20 years ago lol
  4. The solution is to use the FloatToStr overload that takes a TFormatSettings and pass one that has DecimalSeparator set to dot. I typically declare such a TFormatSettings as const and fill all the fields that are required for JSON or some other textual format like this - the following for example is being used in Spring for converting various data types to string (from TValue): const ISO8601FormatSettings: TFormatSettings = ( DateSeparator: '-'; TimeSeparator: ':'; ShortDateFormat: 'YYYY-MM-DD'; LongDateFormat: 'YYYY-MM-DD'; TimeAMString: ''; TimePMString: ''; ShortTimeFormat: 'hh:nn:ss'; LongTimeFormat: 'hh:nn:ss'; DecimalSeparator: '.'; );
  5. Stefan Glienke

    Do you need an ARM64 compiler for Windows?

    Yes, just as much as it was just recompiling VS for x64.
  6. This is a pointless endeavor unless you have some proof that the new code in Delphi 11 has a performance regression - the generated assembly code for the purepascal code is almost identical to the handwritten version before on x86 and all other platforms it should be significantly faster than the previous pure pascal implementation. (I should know - I did that change)
  7. You do a sort of the group by criteria 1-3, then you check if there are any ties between teams and build a subset of those teams and do a sort on those by criteria 4-6. That second step is actually similar to the first just that you don't have a full group but only 2 or 3 teams and the results of the matches between those.
  8. Stefan Glienke

    Simple debugger bug in 11.1

    MidStr has nothing to do with it - it's a bug in the evaluator. If you put MidStr(vLine, 6, 1) into the evaluator it properly shows 'n'. Please report
  9. Stefan Glienke

    Simple debugger bug in 11.1

    Indeed broken since after 10.2.3, can repro the F2084 in the evaluator in 10.3.3, 10.4.2 and 11.1
  10. Stefan Glienke

    Developer Express gave up on FMX

    On the german Delphipraxis, it was mentioned that DevExpress has its developers in Russia - so their operations might be affected by the current situation.
  11. Stefan Glienke

    DUnitX and StackTraces

    You should really give TestInsight a try - it will make developing tests so much easier. Are you sure about that? Unless something on the CI system causes the tests to fail which is not the case locally you will see the exact spot when running them locally. FWIW DUnit has had support for stack trace collecting via either Jcl or madExcept for ages (enable via defines).
  12. Stefan Glienke

    Overloads in implementation

    Nothing strange at all but standard pascal behavior - compiler only sees what has been declared so far. Possible solutions: - change implementation order - declare in separate unit interface part - use forward
  13. Please describe the specs for a real set type.
  14. From my experience it never does that - if you have 3 different lines with function calls returning a string the compiler will generate 3 distinct hidden local variables. Even if it's guaranteed they will never be used both such as calling different string returning functions in both branches of an if or in a case. To understand what Peter showed before and how strings (and other managed type) results are handled look at this code: function Test: string; begin Result := Result + 'hello'; end; procedure Main; var s: string; begin s := Test; s := Test; s := Test; Writeln(s); end; var s: string; i: Integer; begin // case 1 Main; // case 2 s := Test; s := Test; s := Test; Writeln(s); // case 3 for i := 1 to 3 do s := Test; Writeln(s); end. 1. When you assign the string result to a local variable the compiler directly passes the local variable as that hidden result var parameter - hence you will see the output 'hellohellohello' 2. When you assign the string result to a global variable (or a field of an object) the compiler generates distinct hidden variables for each occurence of the call - three in this case - they are also initialized to zero like any explicit variable. It then assigns that to s. This is why you will see 'hello' 3. When doing multiple calls in a loop the compiler had only generated one hidden variable - of course which will be reused for each call - now again you will see 'hellohellohello'
  15. Delphi does not do caller site checks such as Java or .NET do.
  16. Stefan Glienke

    Indy & OpenSSL 1.1.1 & TLS 1.3

    As a solo maintainer of an open-source library myself I can say this: if that person does not have a working dev environment installed anymore and does not have time to work on it - I personally consider that project being de facto dead. That is not an offense but there has to be taken action. Nobody can blame you for not working on a spare time project if you got no time or the mood to. But if that project is of fundamental importance - and I consider Indy as a crucial piece of the 3rd party ecosystem for Delphi - there needs to be something done about it. Not having an up-to-date networking library available (yes I know there are several others, but Indy always has been out of the box and freely available) is one of the many reasons that make Delphi unappealing for decision-makers.
  17. Stefan Glienke

    Indy & OpenSSL 1.1.1 & TLS 1.3

    Given the activity on Indy, I am tempted to call that project dead.
  18. Stefan Glienke

    Is Move the fastest way to copy memory?

    While it cannot squeeze the maximum it is far far away from performing anywhere near "well" for larger amounts - simply because a) the x86 implementation only moves 8 bytes at once in a loop using FILD and FISTP which is slower than an SSE loop using instructions that are available since like 2000. and b) because Win64 uses a rather terrible purepascal implementation which also at most moves 8 byte at a time.
  19. That is why FastMM and probably other memory managers have diagnostic features such as setting freed memory to a certain pattern. If you enable that your ShowMessage will most likely blow up because Danger would be an invalid string pointer.
  20. Stefan Glienke

    Micro optimization: IN vs OR vs CASE

    No, that is a const record - which is not really const but just a write protected variable. You know how to declare integer consts, do you?
  21. Stefan Glienke

    Micro optimization: IN vs OR vs CASE

    The point that others already have expressed is that despite being interested in a topic as performance improvement so low level (as in instruction-level instead of algorithmic level) you seem to lack some important knowledge to do so such as assembly - it does not require as much as it does to write assembly code but to understand it in order to be able to look at the code in the debugger and see that some comparisons are apples and bananas. I did not even read through your code but simply placed a breakpoint into your IsIN function and noticed that it contained a function call to System.SetElem (that even was the first time I have ever seen that function being called so a TIL for me). Why was that the case? Because you are not using consts here but variables. Had you simply made consts for all those IDs the code would have almost as fast as the IsOR which does not suffer to extra function calls but from memory reads (not noticeable in the benchmark because its all in L1 cache already). On my CPU InOR is still a little faster than IsIN which is due to the fact how the compiler builds the in - you can see that for yourself in the disassembly and then look at instruction timings, read up on macro-operation fusion and data dependency For reference, this is the assembly for the two functions when using consts Project1.dpr.40: Result := aID in [xControlsRec.ButtonID, xControlsRec.FormID, xControlsRec.ListBoxID, xControlsRec.TabControlID, xControlsRec.ComboBoxID]; 004CEE7C 83E802 sub eax,$02 004CEE7F 7417 jz $004cee98 004CEE81 83E802 sub eax,$02 004CEE84 7412 jz $004cee98 004CEE86 83E802 sub eax,$02 004CEE89 740D jz $004cee98 004CEE8B 83E802 sub eax,$02 004CEE8E 7408 jz $004cee98 004CEE90 83E802 sub eax,$02 004CEE93 7403 jz $004cee98 004CEE95 33C0 xor eax,eax 004CEE97 C3 ret 004CEE98 B001 mov al,$01 Project1.dpr.41: end; 004CEE9A C3 ret 004CEE9B 90 nop Project1.dpr.45: Result := (aID = xControlsRec.ButtonID) or (aID = xControlsRec.FormID) or (aID = xControlsRec.ListBoxID) or (aID = xControlsRec.TabControlID) or (aID = xControlsRec.ComboBoxID); 004CEE9C 83F802 cmp eax,$02 004CEE9F 7417 jz $004ceeb8 004CEEA1 83F804 cmp eax,$04 004CEEA4 7412 jz $004ceeb8 004CEEA6 83F806 cmp eax,$06 004CEEA9 740D jz $004ceeb8 004CEEAB 83F808 cmp eax,$08 004CEEAE 7408 jz $004ceeb8 004CEEB0 83F80A cmp eax,$0a 004CEEB3 7403 jz $004ceeb8 004CEEB5 33C0 xor eax,eax 004CEEB7 C3 ret 004CEEB8 B001 mov al,$01 Project1.dpr.46: end; 004CEEBA C3 ret Depending on the number of IDs you have it might be worth using power of two and bitmasks or an enum directly because that would only require one cmp/test making the function twice as fast and perfect for inlining which would then also eliminate the function call overhead at all.
  22. Stefan Glienke

    Micro optimization: IN vs OR vs CASE

    I am usually happy to help people improve in that subject but seeing that you simply ignored half the advice given to you before in countless similar threads I consider this a waste of time.
  23. Stefan Glienke

    Cannot build for linux on Windows 11

    Does this help you? https://stackoverflow.com/questions/43023217/delphi-linux-ld-linux-exe-error-cannot-find-lgcc-s
  24. Little explanation of how the RTL dictionary works: - as Capacity, it always uses n^2 - that enables a fast calculation from the hashcode to the bucket index by simply bitmasking it (which makes it important to have a good hash function that also has good distribution in the lower bits of the hashcode - since 11 the RTL uses FNV1a which is quite good). - now how to deal with collisions (and I mean those in the buckets, not the hashcode itself because for 300 entries there would be a capacity of 512). When distributing 300 items across 512 slots it is very likely that you get a few collisions. The RTL uses a very simple so-called linear probing for that - it keeps looking at the next bucket until it finds a free one. While this provides good locality it naturally leads to primary clustering which you can see by the Collision count. This number tells the number of items that don't sit in the slot where their hashcode would have put them. As of 2.0 Spring uses a different hashtable implementation which is using a clever algorithm from the Python dictionary which mitigates this quite nicely. FWIW especially if you have a fixed amount of strings such a keywords it might be better to not use a hashtable because of the hash function overhead but rather some handcrafted lookup table. Since keywords most likely always start with a letter you can do something like DWS does: see https://github.com/EricGrange/DWScript/blob/master/Source/dwsTokenizer.pas#L798 just with the first letter you get the very tiny array of the keywords starting with that letter and then you do a linear search - which beats a hashtable any time.
×