Jump to content

Stefan Glienke

Members
  • Content Count

    1366
  • Joined

  • Last visited

  • Days Won

    130

Everything posted by Stefan Glienke

  1. 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.
  2. I am very sure that record finalization is way faster than heap allocations caused by SetString - if after profiling it still has noticeable impact then one could extract the string keeping from the record into some other structure - the point is they need to be kept alive as long as they are inside the dictionary - how does not matter.
  3. This is how I would solve this - using the record type to preserve the original string and a dedicated equality comparer. Using Spring4d collections here, but would be similar when using the RTL. {$APPTYPE CONSOLE} uses Spring.Collections, Spring.Comparers, System.SysUtils, System.StrUtils, System.Generics.Defaults; type TTokenKey = record s: string; p: PChar; len: Integer; constructor Create(const s: string); overload; constructor Create(p: PChar; len: Integer); overload; end; TTokenKeyComparer = class(TInterfacedObject, IEqualityComparer<TTokenKey>) public function Equals(const left, right: TTokenKey): Boolean; reintroduce; function GetHashCode(const value: TTokenKey): Integer; reintroduce; end; { TTokenKey } constructor TTokenKey.Create(const s: string); begin self.s := s; p := Pointer(s); len := Length(s); end; constructor TTokenKey.Create(p: PChar; len: Integer); begin self.p := p; self.len := len; end; { TTokenKeyComparer } function TTokenKeyComparer.Equals(const left, right: TTokenKey): Boolean; begin Result := (left.len = right.len) and CompareMem(left.p, right.p, left.len); end; function TTokenKeyComparer.GetHashCode(const value: TTokenKey): Integer; begin Result := DefaultHashFunction(value.p^, value.len * SizeOf(Char)); end; const sentence = 'the quick brown fox jumps over the lazy dog'; begin var tokens := TCollections.CreateSet<TTokenKey>(TTokenKeyComparer.Create); for var s in SplitString(sentence, ' ') do tokens.Add(TTokenKey.Create(s)); Writeln(tokens.Count); Writeln(tokens.Contains(TTokenKey.Create(@sentence[5], 5))); // 'quick' ->true Writeln(tokens.Contains(TTokenKey.Create(@sentence[6], 4))); // 'uick' -> false Readln; end.
  4. Stefan Glienke

    Delphi 11.1 is available

    Yeah, they could at least have made Fishfacts, amirite?!
  5. I assume you are not building a debugger because it says:
  6. Stefan Glienke

    Delphi 11.1 is available

    Doing their best to avoid an even bigger disaster?
  7. Nice idea - however I dislike using some special kind of comment format for that including the string for some kind of parsing. As so often .NET solves this pretty nicely. I would even claim this could be already be done by a third party and requires no work from Embarcadero - no claims on performance. The debugger afaik is able to read RTTI from an inspected object and thus the attribute. It can then execute the proxy code which generates the output for the debugger. ToolsAPI has interfaces for that.
  8. If you look at https://www.w3.org/TR/2010/REC-xquery-20101214/#EBNFNotation and https://www.w3.org/TR/xml/#sec-notation you will see that it uses some in between format of BNF and EBNF - using the ::= from BNF but not the angle brackets.
  9. Stefan Glienke

    SynEdit just got a major uplift

    Could be DirectWrite related if you have a rather poor onboard GPU. Check CPU and GPU usage when it's lagging.
  10. Took me less than 5 minutes to find this and another 5 minutes to try it out with chrome dev tools.
  11. Stefan Glienke

    Announcement: Magenta Hardware Components

    The early 2000s want their website design back
  12. Stefan Glienke

    Dragging GExperts windows show too large

    When dragging the Grep window (and also other dockable windows from GExperts) they show as too large - I would say by exactly the scaling amount (the screenshot is from a system with 175%)
  13. Stefan Glienke

    Dragging GExperts windows show too large

    Yes, otherwise I would have reported that to Embarcadero
  14. Stefan Glienke

    Forum for Spring4D

    I still have to prepare the blog article about the beta. Vincent already summarized what 2.0 provides. Testing early is always appreciated as it helps find any possible issues. There might be some minor breaking changes which I will outline in the article. Still no response from google though after sending feedback about the blocked forum. Their reaction times are atrocious.
  15. Stefan Glienke

    The Curiously Recurring Generic Pattern

    The issue is not static typing but the ridiculous amount of ceremony required.
  16. Stefan Glienke

    Forum for Spring4D

    I already sent feedback to Google - nothing else I can do about it.
  17. And there I am, still hoping for that day to come
  18. Stefan Glienke

    Delphi profiler

    Mostly the language war that some people made out of that. The accepted Delphi solution even with inlining is like half as fast as it could be if you know the quirks of the Delphi compiler. Since the process of what was accepted and not was apples and bananas (such as insisting to create instances for Delphi/Pascal but use stack allocated objects in C++ that don't require a heap allocation) I did not bother with providing any version. Plus the non-availability of a docker image with Delphi to run the tests.
  19. Stefan Glienke

    Delphi profiler

    Which PrimeSieve implementation do you use? There has been quite a fuss around that last year after the Youtube Video series by Dave Plummer.
  20. Usually it is left to right like in C# which is one of the few languages that has very little UB - https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions#12623-run-time-evaluation-of-argument-lists
  21. Stefan Glienke

    Profiler for Delphi

    It does not - it simply does not contain that version in some Combobox. SamplingProfiler (both 32 and 64) works with the latest Delphi versions just fine. For VTune - I am so happy they finally added Flamegraph to the Hotspot profiler.
  22. While writing the code I figured that for the issue to show it does not need anything that really inherits from the type the generic is constrained on because the issue is within the generic itself. I just did not remove the Child declaration. It's just there to show that you might instantiate the generic type with anything inheriting from Base that satisfies the constraint. If I had to guess I would say that when inheriting Y from X the compiler does not put the actual T into the generic type parameter but just the type it is constrained on or something - at least for the parameter checking.
  23. The supports is misleading. The generic type parameter I is contrained to be of IBaseBroker and thus ABroker can directly be assigned to BaseBroker (as a side note in that case you would not get an AV because then the same interface pointer that points to the IMT for IFooBroker would be passed down satisfying the later call to GetMsg). However the bug is that the inherited Create call which has I as parameter (which is in this case IFooBroker) accepts the base type that it was constrained on. Here is the issue a bit more condensed - and showing it does not only apply to interfaces: type Base = class end; Child = class(Base) end; X<T: Base> = class procedure Test(obj: T); end; Y<T: Base> = class(X<T>) procedure Test(obj: T); end; procedure X<T>.Test(obj: T); begin end; procedure Y<T>.Test(obj: T); var b: Base; begin b := obj; // obj IS a Base because T is constrained to be a Base inherited Test(b); // this MUST NOT compile because the parameter is T and Base is not a T end;
  24. Stefan Glienke

    Delphi Compilers - # lines of code

    I wonder where that chart is from because the numbers of LLVM and GCC are clearly bogus
  25. Stefan Glienke

    HTML-based MVVM

    It's a UI thing so I never considered thread safety. Especially as I just wrote it as a prototype.
×