Jump to content

dummzeuch

Members
  • Content Count

    2704
  • Joined

  • Last visited

  • Days Won

    93

Everything posted by dummzeuch

  1. You are right about that this is not a good test. My goal was not to create a real performance test for it, just something easy to write that gives me an idea which possible solution to investigate further. But yes, inserting strings in ascending order is definitely going to create some bias. Where am I sorting the string list? It's set to be sorted in the beginning and not changed. Actually I didn't post the code for the string list at all, just for the hash and the dictionary. (OK, i did)
  2. Hey, I just noted that my dzlib is missing from that list. 😉 Thanks for the hints.
  3. Hm, I must be doing something wrong. I can't see any improvement of a TStringHash over a sorted TStringList. Adding the same entries to a TStringHash took 3.12 seconds while it took 3.07 for a sorted TStringList. The same took 0.32 seconds for a TDictionary. Looking them up with ValueOf took approximately the same amount of time for TStringHash and sorted TStringList, while for TDictionary it took 0.32 seconds. I tried sizes of 1024 and 10240. This is the timing code: procedure TForm1.DoTiming(sl: TStringHash); const CYCLES = 100; var IdxCycles: Integer; Idx1: Integer; Idx2: Integer; Idx3: Integer; sw: TStopwatch; s: string; Idx: Integer; begin sw := TStopwatch.StartNew; for IdxCycles := 1 to CYCLES do begin for Idx1 := Ord('A') to Ord('Z') do begin for Idx2 := Ord('A') to Ord('Z') do begin for Idx3 := Ord('A') to Ord('Z') do begin s := chr(Idx1) + chr(Idx2) + chr(Idx3); if sl.ValueOf(s) = -1 then sl.Add(s, Idx1 * 10000 + Idx2 * 100 + Idx3); end; end; end; end; sw.Stop; m_Output.Lines.Add('Add: ' + sw.Elapsed.ToString); sw.Reset; sw.Start; for IdxCycles := 1 to CYCLES do begin for Idx1 := Ord('A') to Ord('Z') do begin for Idx2 := Ord('A') to Ord('Z') do begin for Idx3 := Ord('A') to Ord('Z') do begin s := chr(Idx1) + chr(Idx2) + chr(Idx3); Idx := sl.ValueOf(s); Assert(Idx = Idx1 * 10000 + Idx2 * 100 + Idx3); end; end; end; end; m_Output.Lines.Add('IndexOf: ' + sw.Elapsed.ToString); end; The code for TStringList is very similar. It uses AddObject instead of Add and IndexOf instead of ValueOf. procedure Tf_HashedStringListTest.DoTiming(sl: TStringList); const CYCLES = 100; var IdxCycles: Integer; Idx1: Integer; Idx2: Integer; Idx3: Integer; sw: TStopwatch; s: string; Idx: Integer; begin sl.Sorted := True; sl.CaseSensitive := True; sl.Duplicates := dupError; sw := TStopwatch.StartNew; sl.BeginUpdate; for IdxCycles := 1 to CYCLES do begin for Idx1 := Ord('A') to Ord('Z') do begin for Idx2 := Ord('A') to Ord('Z') do begin for Idx3 := Ord('A') to Ord('Z') do begin s := chr(Idx1) + chr(Idx2) + chr(Idx3); if sl.IndexOf(s) = -1 then sl.AddObject(s, Pointer(Idx1 * 10000 + Idx2 * 100 + Idx3)); end; end; end; end; sl.EndUpdate; sw.Stop; m_Output.Lines.Add(sl.Count.ToString + ': Add: ' + sw.Elapsed.ToString); sw.Reset; sw.Start; for IdxCycles := 1 to CYCLES do begin for Idx1 := Ord('A') to Ord('Z') do begin for Idx2 := Ord('A') to Ord('Z') do begin for Idx3 := Ord('A') to Ord('Z') do begin s := chr(Idx1) + chr(Idx2) + chr(Idx3); sl.IndexOf(s); end; end; end; end; m_Output.Lines.Add(sl.Count.ToString + ': IndexOf: ' + sw.Elapsed.ToString); And the one for the TDictionary just calls TryGetValue instead of IndexOf and Add instead of AddObject. I guess it has something to do with the limited length of the strings. Or I am overlooking something else entirely (it has been a long day and I'm not going to test the other solutions right now).
  4. Would you please be so kind and send it to me via private message? (Unless of course you prefer to attach it publicly to this thread.)
  5. Both look interesting, thanks. Now, I just need to find the time to run timing tests. 😉
  6. If I remember correctly (I bought Tomes of Delphi years ago), the source is not freely available. But it's a good idea, I'll have to check.
  7. Yes: I thought of improving it too, but it probably would be more work than writing my own. Just the fact that it's based on TStrings adds overhead that I would like to avoid. (I guess you meant THashedStringlist, because if I remember correctly, TStringHash isn't even exported.)
  8. What's the license of this code?
  9. Even if that was as easy as it sounds, I doubt that this meets the definition of "free implementation". Yes, this is what many people tell me. I might even drop some of them, but since I do most of my professional work with Delphi 2007 (the last pre Unicode version), dropping support for it in GExperts would not really be smart.
  10. You must keep in mind that Embarcadero only occasionally syncs the public jira with their internal one. Usually just before a new release lots of entries get closed. Unfortunately that does not always mean that they have been fixed.
  11. Everybody seems to be blogging about Delphi having been around for 25 years, so I won’t stay back and tell some of my story. When I finished university and started a job, Delphi was just about being “born” and I was working with Turbo Pascal and later Visual Basic. VB was great in some aspects because it allowed to easily design user interfaces and write code only where you needed it. It wasn’t after several years later that I was introduced to Delphi when I took a job at fPrint UK Ltd. (Yes, that’s what web pages looked in 1997) and moved from Germany to the UK. The time I worked there was among the best of my life. I had some great coworkers there who were expert software developers ... read on in the blog post.
  12. Which web page are you talking about? Mine? fPrint's? If it's the latter: I think it originally had a background color matching the GIFs' background, so it didn't look quite as bad as in the archived version.
  13. Given a class like this: type TMyClass = class private FSomeInstance: TSomeOtherClass; public constructor Create(_SomeInstance: TSomeOtherClass); destructor Destroy; override; end; [...] constructor TMyClass.Create(_SomeInstance: TSomeOtherClass); begin inherited Create; FSomeInstance := _SomeInstance; // other code here end; destructor TMyClass.Destroy; begin FSomeInstance.Free; inherited; end; Note: The class takes ownership of the TSomeOtherClass instance passed to it in the constructor. It frees it in its destructor. How would you write the code constructing it? My current code looks like this: SomeInstance := TSomeOtherClass.Create; try MyInstance := TMyClass.Create(SomeInstance); SomeInstance := nil; finally FreeAndNil(SomeInstance) end; The try..finally is there for handling the case where TMyClass.Create raises an exception, so there won't be a memory leak. But unfortunately the destructor is called even for partially created instances, so if the code after assigning FSomeInstance raises an exception, the instance of TSomeOtherClass would be freed twice. The only safe way I can think of would be to change TMyClass.Create as follows: constructor TMyClass.Create(var _SomeInstance: TSomeOtherClass); begin inherited Create; FSomeInstance := _SomeInstance; _SomeInstance := nil; // other code here end; If an exception gets raised in the inherited constructor (or in any other code before setting FSomeInstance) FSomeInstance would be nil, and _SomeInstance would be freed in the calling code. If it happens after setting _SomeInstance to nil, the destructor would free FSomeInstance and the calling code would not, becuse the var parameter was set to nil, which FreeAndNil (or even .Free) would detect. Of course, I could use Interfaces and rely on reference counting.
  14. I was assuming that this constructor should replace the one in my original example, where it was called from within a try finally block: constructor TMyClass.Create(_SomeInstance: TSomeOtherClass); begin inherited Create; // other code here FSomeInstance := _SomeInstance; end; If that's not the case, you have a memory leak if the inherited Create fails.
  15. dummzeuch

    TTimer equivalent with smaller interval

    The TTimer component has a minimum interval of 50ms. I understand that this is a limitation of the Windows timer which uses messages. What would be the alternative if I need smaller intervals, e.g. down to 10ms? The code is still supposed to be run in the main VCL thread. It doesn't matter if some events get lost because executing the code takes longer than the timer interval because the computer is busy. I just want to be able to get a smaller interval if the computer can support it.
  16. As Dalija said: Raising exceptions in the constructor is possible and even normal (e.g. TFileStream.Create for a file that does not exist or maybe is only temporarily not available). It's documented that the destructor must handle partly constructed objects. Allen Bauer blogged about this in 2006 (aparently he has removed his About Me page from the blog, or did it never have one?) But yes, many Delphi developers are not aware of this. I only recently (as in "a few years, maybe a decade ago") became aware of this issue and am still finding code that does not take this into account.
  17. That Wikipedia article links to an article by Thomas Wang Prime Double Hash Table from 1997 which explains what the point of choosing a prime number is. That sounds plausible to me, unless of course it is outdated by now due to different implementations of hash tables and hash functions.
  18. dummzeuch

    TTimer equivalent with smaller interval

    (Plus some kind of time interval checking.) No, but it's an interesting idea. Unfortunately not helpful in my particular case as I have now determined that it is not the timer interval but the code executed in the event that causes my slow down effect. I have tried to optimize it but so far failed. There are too many complex dependencies on multithreaded code to easily find the culprit.
  19. Oh, TDictionary is hash table based. I wasn't aware of that. In that case, of course it should be much faster for significantly large lists.
  20. You mean like this? SomeInstance := TSomeOtherClass.Create; try MyInstance := TMyClass.Create(SomeInstance); except FreeAndNil(SomeInstance) raise; end;
  21. I don't see how assigning the field as the last operation in the constructor would solve the problem of duplicate destruction. Yes, if there is an exception, the class' destructor won't free SomeInstance and leave that for the finally block. But what if there is no exception? Then the try..finally around the construction will free SomeInstance and leave a stale pointer to an invalid instance inside the class.
  22. What kind of difference do you mean here? Performance or readability?
  23. There are very rare cases where Destroy won't be called, but I would simply ignore them in this context. Hm, assigning FSomeInstance before calling inherited Create. I am so used to call inherited Create as the first thing in the constructor, I didn't think of this. Sounds promising. Of course that only works if the destructor itself works as expected with a partially created object which - looking at the real world code this is about - I think doubtful. But that's a different problem I'll have to fix anyway.
  24. How would DI (I guess you mean dependency injection) solve this problem? Can you please give an example?
  25. How can you be sure? As always: Nobody has found and reported a bug (yet).
×