-
Content Count
1365 -
Joined
-
Last visited
-
Days Won
130
Everything posted by Stefan Glienke
-
TStringHelper.IndexOfAny( array of string, ...): Why is this private ?
Stefan Glienke replied to Rollo62's topic in RTL and Delphi Object Pascal
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 😉 -
TStringHelper.IndexOfAny( array of string, ...): Why is this private ?
Stefan Glienke replied to Rollo62's topic in RTL and Delphi Object Pascal
No. -
TStringHelper.IndexOfAny( array of string, ...): Why is this private ?
Stefan Glienke replied to Rollo62's topic in RTL and Delphi Object Pascal
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) -
ISuperObject local double conversion problem. {"value":0,22}
Stefan Glienke replied to PizzaProgram's topic in ICS - Internet Component Suite
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 -
ISuperObject local double conversion problem. {"value":0,22}
Stefan Glienke replied to PizzaProgram's topic in ICS - Internet Component Suite
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: '.'; ); -
Do you need an ARM64 compiler for Windows?
Stefan Glienke replied to Lars Fosdal's topic in Cross-platform
Yes, just as much as it was just recompiling VS for x64. -
How to compile against ASM versions of System unit code?
Stefan Glienke replied to kaarigar's topic in RTL and Delphi Object Pascal
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) -
Football Championship Group Stage Order
Stefan Glienke replied to PawelPepe's topic in Algorithms, Data Structures and Class Design
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. -
Simple debugger bug in 11.1
Stefan Glienke replied to Mike Torrettinni's topic in Delphi IDE and APIs
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 -
Simple debugger bug in 11.1
Stefan Glienke replied to Mike Torrettinni's topic in Delphi IDE and APIs
Indeed broken since after 10.2.3, can repro the F2084 in the evaluator in 10.3.3, 10.4.2 and 11.1 -
On the german Delphipraxis, it was mentioned that DevExpress has its developers in Russia - so their operations might be affected by the current situation.
-
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).
-
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
-
Reorganize a class / Set Of ..
Stefan Glienke replied to PatV's topic in Algorithms, Data Structures and Class Design
Please describe the specs for a real set type. -
Can undefined function result point to valid/used data in memory?
Stefan Glienke replied to Mike Torrettinni's topic in RTL and Delphi Object Pascal
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' -
Class not instantiated ... but method executed
Stefan Glienke replied to DelphiUdIT's topic in Algorithms, Data Structures and Class Design
Delphi does not do caller site checks such as Java or .NET do. -
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.
-
Given the activity on Indy, I am tempted to call that project dead.
-
Is Move the fastest way to copy memory?
Stefan Glienke replied to dummzeuch's topic in RTL and Delphi Object Pascal
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. -
Why isn't this dangerous, or is it?
Stefan Glienke replied to Renate Schaaf's topic in Algorithms, Data Structures and Class Design
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. -
Micro optimization: IN vs OR vs CASE
Stefan Glienke replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
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? -
Micro optimization: IN vs OR vs CASE
Stefan Glienke replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
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. -
Micro optimization: IN vs OR vs CASE
Stefan Glienke replied to Mike Torrettinni's topic in Algorithms, Data Structures and Class Design
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. -
Cannot build for linux on Windows 11
Stefan Glienke replied to Mirko Bianco's topic in Cross-platform
Does this help you? https://stackoverflow.com/questions/43023217/delphi-linux-ld-linux-exe-error-cannot-find-lgcc-s -
Trying to avoid using SetString when doing a token lookup in a TDictionary
Stefan Glienke replied to MarkShark's topic in RTL and Delphi Object Pascal
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.