Jump to content

MarkShark

Members
  • Content Count

    88
  • Joined

  • Last visited

Everything posted by MarkShark

  1. I realized in my benchmarking above that I was comparing a case insensitive dictionary to a case sensitive one. After correcting that the performance improvement for using this technique is 30%, which is still significant, but not what I reported above for sure lol. I do notice something odd. I'm loading about 300 SQL keywords (select, from, etc.) into a dictionary<string, integer> and if I check FKeywords.Collisions I'm getting a result of 90. Looking at the code for TDictionary it looks like they take the results of the hash and convert it a positive integer for the array of buckets (I hope that's the correct terminology.) I *think* that's where the collisions come in. It looks like Spring4d doesn't have the collisions property, so I haven't been able to do a sanity check against it.
  2. @Stefan Glienke Thank you! My code is very close to yours, which is a nice validation for me lol. I also put the "stored" string in the record (@Frost I also tested keeping them in a separate list to remove the record being managed, but it didn't seem to make any difference when I benchmarked it.) I would be using Spring4D for this in my own code, however it may end up in SynEdit's highlighters and so I'm trying to keep dependencies down and to leverage the rtl as much as possible.
  3. Excellent, thank you! I realize now that this is what Frost meant in his initial suggestion above and thank you Remy for that confirmation and link. I've done exactly what was suggested with the record as key and a custom equality comparer. Seems to work great and is still three times faster than my original version. Awesome!
  4. My technique above (using a hash as the key in a TDictionary) seems to work, however, it assumes that hashing my tokens will always give a unique value. Am I correct that that's a problem? Any thoughts on how to handle it?
  5. Thanks all! I ended up using Frost's suggestion of using the hash as the key like so: Hash := THashFNV1a32.GetHashValue(FTokenStart^, FTokenLength * SizeOf(Char)); if not FTokenDictionary.TryGetValue(Hash, Result) then Result := UnknownToken; And it appears to be roughly three times faster than my old verson using SetString! I used that particular hash because it looks like that's what Delphi's default string comparer uses.
  6. Hi all! I'm doing the following to convert a string to utf8 and then to move it into a memory location (it happens to be a globalallocbuffer for a clipboard format, but I don't' think that matters to my question.) It works well, but I was wondering if it's possible to do it without the intermediate byte array. Delphi 11 btw. Any thoughts appreciated. Buf := TEncoding.UTF8.GetBytes(AString); Move(Buf, PtrData^, Length(Buf));
  7. Wow, great answer! I used the LocaleCharsFromUnicode suggestion as I ended up needing to support some other codepages. Works great! Much appreciated!
  8. MarkShark

    SynEdit just got a major uplift

    @pyscripter went above and beyond with these improvements!
  9. MarkShark

    String optimization

    Wow! I've actually been bitten by strlen in the past. Well done sir!!
  10. I'm trying to use Spring4D's Shared<> smartpointer feature to get a smartpointer to a TStringList. This is easy and works really well, but I haven't been able to figure out how to do this and call an overloaded constructor. So this works great: var SList := Shared<TStringList>.Make; SList.Add('Blah'); But I'd like to create a stringlist using the overloaded create call that includes the duplicate option. Something like the following, which doesn't work. var SList := Shared<TStringList>.Make(dupIgnore, True, False); SList.Add('Blah'); Anyone know how to do it? Thanks!
  11. @pyscripter, you pointed me in the right direction, but I think your syntax is off (I'm guessing just a typo.) Here's what worked: var SList := Shared.Make<TStringList>(TStringList.Create(dupIgnore, True, False)); Unlike some of my other tries this one just creates a single StringList with the correct parameters. I mention that just because some of my other syntax tests created a parameter-less StringList, immediately freed it, and then created the dupIgnore one. This syntax above seems to do the trick! Thanks! Also @Vincent Parrett I think you're right about Shared<>.Make! It turns out I was using the wrong "thing". Shared.Make<> vs Shared<>.Make.
  12. MarkShark

    SynEdit just got a major uplift

    I'm using the new SynEdit with the SQL Highlighter. I've tested with a 250,000 line sql script containing a real mix of statements including comments, string literals, lots of brackets, etc. I don't see any lag or stuttering. Make sure to test outside of debug mode as that really seems to affect the dwrite stuff. My machine isn't a high end machine, it's an AMD mini pc with a mobile 4-core (Ryzen 7 3750H), Windows 11, Delphi 11. I can't say enough about the new features of the SynEdit, the new gutter stuff, the alpha blended select and especially the new way it does the end of row selections is really nice. Pyscripter has done an amazing job modernizing the control. It will be interesting to get feedback on what cpu/gpu people are using and if there are particular settings that seem to affect performance.
  13. I realize that this is going to be very subjective, but what are people's thoughts on using begin end just to limit the scope of an inline variable? For example: ... code begin var LocalString := 'My String'; ... Use LocalString for something end; ... more code Are there any caveats to doing so? I know that doing this can cause an implicit try-finally for managed types, so I can see that being a performance factor in a loop and something to be aware of. It also seems like they've fixed the issues with the debugger and inline vars in Delphi 11 (at least in the simple cases I've tried), which makes them more usable. -Mark
  14. Thanks Dalija and the other posters, this is all "good stuff" and I love to learn new things and techniques. I'm not sure how the word condescending matches anything said here so far btw. Also just as a note I see that Emba switched to using AtomicCmpExchange instead of InterlockedCompareExchangePointer in Delphi 11 (or perhaps earlier.) Kind of interesting.
  15. Anders, that may be the case. I determined that it was for thread safety from some stack overflow posts that seemed to match the code style. The link is https://stackoverflow.com/q/5392107/113128 and there are a number of suggestions that some of the implementation examples aren't fully safe. It's definitely a good thing to keep in mind, thanks!
  16. Going to answer my own question after some research. Looks like it's for thread safety! Got it now, thanks.
  17. Thank you Peter! I'm checking out your code and trying to learn some uncoupling/interface techniques. In the HelperRegistry function I notice you use the following code to set the InternalHelperRegistry while also returning it. Is it done this way for thread safety or is it just related to maintaining the proper refcount of the interface? BTW: Your posts through the years have been extremely helpful to me, so thanks as always! Result := THelperRegistry.Create; Result._AddRef; // the call below does not increment the refcount! P:= InterlockedCompareExchangeObject(InternalHelperRegistry, TObject(Pointer(Result)), nil); if P <> nil then begin Result._Release; Result := InternalHelperRegistry; end; {if} -Mark
  18. Remy, all the techniques I tried caused my application to be the foreground one. I'm guessing that's what the OP found as well. That might be how the TRzTrayIcon works or possibly I just missed something obvious back then.
  19. I had to do this for a password management tray app I wrote for personal use. Here's the technique I ended up with (with lots of help from stack overflow and others.) Step 1: Use EnumWindows with a callback to build up a list of window handles. They will be in the current z-order. It needs to check if the window is visible, unowned, and has WS_EX_APPWINDOW in its style before adding it to your list. Step 2: Loop through the window handles above and find the first one in the list with a thread process id different than your current program. This is done using GetWindowThreadProcessId on each window handle from your enum list and comparing to the results of GetCurrentThreadID. That window handle should be the main window of the app that was last active. I would perform the above whenever my tray app was activated. I'm guessing there are exceptions for "stay on top" apps and such but it seemed to work for my needs. I hope that helps or at least gives a clue! -Mark Update note: Once I get the window handle of the last activated app I would also call GetLastActivePopup on it (as I needed to know if it had a modal popup active.)
  20. Agreed! A quick search found this one: https://quality.embarcadero.com/browse/RSP-34831 -Mark
  21. Summary: Using a Parallel.BackgroundWorker if I check for WorkItem.CancellationToken.IsSignalled within the .OnExecute anonymous procedure I see that it is terminated immediately if I call FWorker.CancelAll from the main thread. HOWEVER if I use a function called from the OnExecute handler it seems that the token is only signaled once the function completes. I've included some code below, but if anyone could explain what I'm seeing it would be much appreciated! Using a Paralel.BackgroundWorker created like: FWorker := Parallel.BackgroundWorker.Execute(nil); I then schedule a work item like: FWorker.Schedule( FWorker.CreateWorkItem(TOmniValue.Null), FWorker.Config .OnExecute( procedure(const WorkItem: IOmniWorkItem) begin var LElapsed: Integer := 1; repeat Sleep(100); Inc(LElapsed, 100); until WorkItem.CancellationToken.IsSignalled or (LElapsed > 5000); end) .OnRequestDone( procedure (const Sender: IOmniBackgroundWorker; const WorkItem: IOmniWorkItem) begin if WorkItem.CancellationToken.IsSignalled then Memo1.Lines.Add('Task ' + WorkItem.UniqueID.ToString + ' onRequestDone and was cancelled.') else Memo1.Lines.Add('Task ' + WorkItem.UniqueID.ToString + ' onRequestDone'); end ) ); Calling FWorker.CancelAll; from the main thread cancels this workitem immediately. However if instead of the "repeat until" above I call the following function, the token is never signaled "during the call", though it is signaled after the call returns. function NiceWaitEx(const ACancelToken: IOmniCancellationToken; AWaitMs: Integer): Integer; begin const LagMs = 100; var LElapsed: Integer := 1; repeat Sleep(LagMs); Inc(LElapsed, LagMs); until ACancelToken.IsSignalled or (LElapsed > AWaitMs); Result := LElapsed; end; FWorker.Schedule( FWorker.CreateWorkItem(TOmniValue.Null), FWorker.Config .OnExecute( procedure(const WorkItem: IOmniWorkItem) begin NiceWaitEx(WorkItem.CancellationToken, 5000); end) .OnRequestDone( procedure (const Sender: IOmniBackgroundWorker; const WorkItem: IOmniWorkItem) begin if WorkItem.CancellationToken.IsSignalled then Memo1.Lines.Add('Task ' + WorkItem.UniqueID.ToString + ' onRequestDone and was cancelled.') else Memo1.Lines.Add('Task ' + WorkItem.UniqueID.ToString + ' onRequestDone'); end ) ); When I use the second version (with the call) the workitem always goes the full 5 seconds although shows that it was cancelled at the end. I hope that's enough to explain the issue (and my confusion lol.) Any thoughts appreciated! -Mark
  22. MarkShark

    TListView and Stylebook on 10.4

    Ha! Sorry about that. I didn't notice this question was in FMX! -Mark
  23. MarkShark

    TListView and Stylebook on 10.4

    I can confirm it. The only solutions are to use a different control (such as the Virtual String Tree) or to use the following library. RRuz VCL Style Utils -Mark
  24. MarkShark

    Annoying IDE behavior changes in 10.4.2

    I can confirm the change as well. I've also noticed that the Ctrl-Alt-Up and Ctrl-Alt-Down keys don't seem to navigate between methods anymore (although Ctrl-Alt-Mousewheel still seems to do it.) I was going to suggest it to jump to the first line after begin, but it's gone. So if you want the old behavior you can do Ctrl-Shift-Up, Ctrl-Shift-Down, and then Ctrl-Alt-Mousewheel up! Simple, right? (Wink) Method Hopping: http://docwiki.embarcadero.com/RADStudio/Sydney/en/Code_Navigation_(IDE_Tutorial)
  25. MarkShark

    Issue about calling Canvas.Polygon

    Thanks! I notice you're calling it like ACanvas.SaveDC so I thought maybe you were encapsulating the API functions (my TCanvas doesn't seem to have those as methods.) I do use the api calls directly, but obviously your code is cleaner.
×