Jump to content

Stefan Glienke

Members
  • Content Count

    1366
  • Joined

  • Last visited

  • Days Won

    130

Everything posted by Stefan Glienke

  1. Stefan Glienke

    GExperts supports even more laziness

    I am really missing a "don't bother me at all about any exception during the current debug session" button.
  2. Stefan Glienke

    Patch 2 for RAD Studio 10.4 now available

    I reported the regression and added a suggestion how to fix this kinda properly (without going too deep into the rabbit hole of extending constraints): https://quality.embarcadero.com/browse/RSP-30078
  3. Stefan Glienke

    Patch 2 for RAD Studio 10.4 now available

    @Vincent Parrett Ewww looks like they made record constraint only allow record with no managed fields although things were just working
  4. Stefan Glienke

    Help with string extraction function

    Just to make sure you don't misunderstand: The common way of compilers (check it for yourself using compiler explorer) to write while x do is to turn it into if x then repeat until not x (simply said). The code you posted could get rid of the check at the bottom because there was nothing else to do after the check for '0' which results in a result true so that basically serves as this loop breaking check. The first check makes sure the loop is even entered at all, otherwise it jumps over it (0 iterations) - the "real" loop condition is at the bottom of the loop body. What is usually not good is to only put the loop condition at the bottom and statically jump over the loop body (because the duplicated condition at the top is missing). Thats two unnecessary jumps for the common case of "at least 1 iteration".
  5. Stefan Glienke

    Help with string extraction function

    Here it is clearly not - but I was referring to the fact that mostly it is the way to do - see this exhaustive answer. But what you typically don't do is to statically jump over the loop body to the condition at the bottom to then conditionally jump back up because the loop very likely runs at least once. Either the compiler restructures the loop or simply puts the loop condition as if ontop of the loop as well (sometimes dcc even does it)
  6. Stefan Glienke

    Help with string extraction function

    At least regarding life expectancy 😉
  7. Stefan Glienke

    Help with string extraction function

    I stopped reading after that because we are in a Delphi forum... Well - I did not - lets see - gcc and clang both generate this (so does vc++ I guess): test1(char const*): .L14: movzx edx, BYTE PTR [eax] test dl, dl je .L15 add eax, 1 cmp dl, 48 jne .L14 mov eax, 1 ret .L15: xor eax, eax ret and this is what Delphi makes out of this function: function Test(str: PAnsiChar): Boolean; var i: Integer; begin i := 0; while str[i] <> #0 do begin if '0' = str[i] then Exit(True); Inc(i); end; Result := False; end; Project25.dpr.13: i := 0; 0041BD90 33D2 xor edx,edx 0041BD92 EB09 jmp $0041bd9d Project25.dpr.16: if '0' = str[i] then Exit(True); 0041BD94 80F930 cmp cl,$30 0041BD97 7503 jnz $0041bd9c 0041BD99 B001 mov al,$01 0041BD9B C3 ret Project25.dpr.17: Inc(i); 0041BD9C 42 inc edx Project25.dpr.14: while str[i] <> #0 do 0041BD9D 0FB60C10 movzx ecx,[eax+edx] 0041BDA1 84C9 test cl,cl 0041BDA3 75EF jnz $0041bd94 Project25.dpr.19: Result := False; 0041BDA5 33C0 xor eax,eax Project25.dpr.20: end; 0041BDA7 C3 ret And the other one: function Test2(str: PAnsiChar): Boolean; var p: PAnsiChar; begin p := @str[0]; while p^ <> #0 do begin if '0' = p^ then Exit(True); Inc(p); end; Result := False; end; Project25.dpr.26: p := @str[0]; 0041BD90 EB09 jmp $0041bd9b Project25.dpr.29: if '0' = p^ then Exit(True); 0041BD92 80FA30 cmp dl,$30 0041BD95 7503 jnz $0041bd9a 0041BD97 B001 mov al,$01 0041BD99 C3 ret Project25.dpr.30: Inc(p); 0041BD9A 40 inc eax Project25.dpr.27: while p^ <> #0 do 0041BD9B 0FB610 movzx edx,[eax] 0041BD9E 84D2 test dl,dl 0041BDA0 75F0 jnz $0041bd92 Project25.dpr.32: Result := False; 0041BDA2 33C0 xor eax,eax Project25.dpr.33: end; 0041BDA4 C3 ret What we can see is that the Delphi compiler is clearly not clever but rather sticks to some hardcoded behavior such as that it is addicted to put loop conditions after the body (which usually is a good pattern) even if it makes little sense to do so. Another thing has to do with the post increment operator that we don't have in Delphi which causes the instruction after the check inside the loop which causes the additional jump because it cannot just restart the loop after it did not find the '0'. Things like that require writing loops in a very ass backwards way in Delphi to get the same code generated. In order to get the same code that the C++ compilers emit you have to write unreadable shit like this: function Test3(str: PAnsiChar): Boolean; var c: AnsiChar; begin repeat c := str^; if c = #0 then Break; Inc(str); if c = '0' then Exit(True); until False; Result := False; end; When I used the local variable p here that I assigned str to it did not even keep using eax but insisted in putting a useless mov into a new register.
  8. Stefan Glienke

    Help with string extraction function

    No, because if you are doing something with whatever is at x[ i ] its faster to use a pointer that starts at @x[ 0 ] and just inc that rather than having a loop which then has 2 counter variables (they like to count down to zero while maintaining the upwards counting i). And on x86 that is a precious register that can be used for something else.
  9. Stefan Glienke

    Help with string extraction function

    Congrats, you just broke it for empty S. Also not storing Pointer(Result) makes it slower at least for me. You have to know where it's worth to put effort and where it does not matter.
  10. Stefan Glienke

    Help with string extraction function

    Yes but who cares for the nop (well for this one at least, yes the Win64 compiler inserts a ton of them in weird places), the next line is the SetLength before the ret.
  11. Stefan Glienke

    Help with string extraction function

    I was merely commenting on the things that can happen when using common practice for benchmarking and comparing different algorithms or implementation by simply putting them all into the same executable and then running them after each other in any order and less so on which of them are affecting this particular test. FWIW here is an optimized version of your routine (tested on 10.3.3 with $O+,W-): - no unnecessary Inc(r)/Dec(r) when '{' was found - no unnecessary check on the inner loop before first Inc(p) - better code generated for inner loop condition - yours generated imul - could even use until (p^ in ['}', #0]) which is even faster but generates W1050 but produces valid code as it uses the 16bit registers still. function ExtractContent(const S: string): string; var p, ep, r, sr: pchar; len: Integer; begin len := S.Length; SetLength(Result, len); r := Pointer(Result); sr := r; p := Pointer(S); ep := p + len; while p < ep do begin r^ := p^; if p^ = '{' then begin repeat Inc(p); until (p^ = '}') or (p^ = #0); end else Inc(r); Inc(p); end; SetLength(Result, r - sr); end;
  12. Stefan Glienke

    Help with string extraction function

    @Kas Ob. Your benchmark is flawed - every time you test multiple implementations in the same test you have a bias because of sorts of things such as memory layout/alignment, branch prediction, prefetcher and so on. For example when I run the code you posted "Kas not fixed wins" - when I comment out all others the code runs slower. When I comment out one of the tests all routines suddenly run faster. This clearly is an indicator that depending on where the code is put by the compiler sometimes they might be sitting in the cache more beneficial than some other times. Unfortunately at least to my knowledge with the Delphi compiler you have no control over code alignment - unless you write stuff in asm if course...
  13. Stefan Glienke

    Are we just "Cash Cows"?

    Will probably be reclassified as "new feature" :trollface:
  14. Stefan Glienke

    Help with string extraction function

    For parsing purposes it might not be a good idea actually to return a string as that causes heap allocation every time you extract a substring (*) which I would guess does not only happen once but many times. Something like a PChar with additional Length information might be a better fit. If you really need the text as new string entity you can still materialize it. (*) You will just not notice in your benchmark because in the loop the same string instance is being reused all the time - but if you would run this in 10.4 story might be different (see my comment in https://quality.embarcadero.com/browse/RSP-29450)
  15. Stefan Glienke

    Open Type Arrays?

    Yes, because 1 is not Integer
  16. Stefan Glienke

    Open Type Arrays?

    If we just could write class operator Implicit<T>(const value: T): TValue; ... oh wait... we can! type TValueHelper = record helper for TValue class function &&op_Implicit<T>(const value: T): TValue; static; end; class function TValueHelper.&&op_Implicit<T>(const value: T): TValue; begin TValue.Make(@value, System.TypeInfo(T), Result); end;
  17. Stefan Glienke

    FreeAndNil or NilAndFree ?

    FreeAndNil is correct from the consumer point of view - you call the routine it frees the thing and then once it returns you have a nil reference. That it sets the reference to nil before it actually calls Free is an implementation detail to protect against potentially recursive attempts to FreeAndNil the same reference or accessing an object which is undergoing destruction and probably a result of the convoluted architecture of TComponent and Co.
  18. Stefan Glienke

    Open Type Arrays?

    No - Anything is an untyped var parameter - if you put such a signature into an interface with methodinfo on you even get a funky compiler error E2134 Type '<void>' has no type info
  19. Stefan Glienke

    Open Type Arrays?

    Then make it do a strict type check comparing with TValue.TypeInfo... also method overloading and distinct types is a source of errors in itself because they are always assignment compatible with their base type so that does not count as argument. I can also pass a TFileName, a TCaption or whatever to your DoSomething2 and it will happily go into the string overload. BTT if Delphi was more functional I could very well see more usecases for this in the form of monads like the maybe or either type because they can very well solve a lot of unnecessary cyclomatic complexity
  20. Stefan Glienke

    Open Type Arrays?

    Even worse than Variant - you can pass anything to DoSomething now and it simply wont get handled (or raise an exception if you add that as else case)
  21. Did you install the hooks in a way that you don't override any existing hook that might be there already?
  22. Stefan Glienke

    Open Type Arrays?

    Variant will accept everything and their dog which is hardly what is wanted here. @PeterPanettone This is not in any form an array but as @david berneda rightly said a union. See https://en.wikipedia.org/wiki/Union_type We already have such things in Delphi: TVarRec, Variant, TValue. However none of them can be specified to only accept a certain amount of types. If you want something like that then write a custom record type with implicit operators and a TValue as internal storage (saves you from taking care of mixing unmanaged and managed types in a proper way) Just for the lulz: {$APPTYPE CONSOLE} uses SysUtils, Rtti; type Union = record fvalue: TValue; class operator Implicit(const value: string): Union; class operator Implicit(const value: Integer): Union; class operator Implicit(const value: Union): string; class operator Implicit(const value: Union): Integer; function IsType<T>: Boolean; end; procedure DoSomething(const AParam: Union); begin if AParam.IsType<string> then Writeln('string: ', string(AParam)) else if AParam.IsType<Integer> then Writeln('integer: ', IntToStr(AParam)); end; class operator Union.Implicit(const value: Integer): Union; begin Result.fvalue := value; end; class operator Union.Implicit(const value: string): Union; begin Result.fvalue := value; end; class operator Union.Implicit(const value: Union): string; begin Result := value.fvalue.AsString; end; class operator Union.Implicit(const value: Union): Integer; begin Result := value.fvalue.AsInteger; end; function Union.IsType<T>: Boolean; begin Result := fvalue.IsType<T>; end; begin DoSomething(42); DoSomething('Hello world'); end. If you want to go funky then make it a generic type with 2, 3, 4, ... generic type parameters 😉
  23. Stefan Glienke

    Varialbe set to nil

    Thats wrong, only the one that had the [weak] attribute needs to be a Weak<T>. If the reference a weak is pointing to is being destroyed the RTL takes care of making any [weak] marked reference nil. Same does Spring with a Weak<T>. With the pointer that does not happen. So if DoSomethingWithInterface2 happens to be called after TMyClass2 was destroyed (possibly not with this example but a common scenario) with the pointer approach you will run into an AV or other bad things if you dont explicitly set those weak reference back to nil which is why Remy made the dtor which did that - however in real code an object usually does not keep track of all the places it might be weak referenced.
  24. Stefan Glienke

    Varialbe set to nil

    Use Weak<T> from Spring4D in any version before one that supports [weak] if you want proper cleanup of such weak references. Using the pointer approach will not do and you might run into AVs caused by dangling references.
  25. Stefan Glienke

    Should Delphi have native interfaces?

    What you display is known as "default interface methods" in other languages. Java has them and they are in the making for C#8 from what I know. However in both implementations those interfaces can just operate on other interface methods and not force fields of the implementing object - that would be more like scala traits (not exactly sure how they implement that ontop of the JVM)
×