Alex7691 17 Posted Tuesday at 10:42 PM (edited) Hello everyone, Last week I (unsuccessfully) tried to use the Rapid.Generics library in a specific project. After replacing the std TDictionary/TList with the Rapid.Generics equivalents, the application fell apart within the first 10 seconds of testing, which was quite disappointing. Having some spare time during the weekend, I decided to give it a shot and see if I could make it work properly, at least fixing the most important and visible issues. As a result, I am now sharing my changes so others can benefit from them or contribute. https://github.com/Alexandre-Atozed/Rapid.Generics.v2 Improvements (Readme contains a more detailed list): Fixed multiple bugs Refactored and streamlined the code Removed some unused code Added 123 unit tests, creating a comprehensive test suite The modified code passes all tests and is also free of memory leaks. The real world application is being tested with it now and the initial results are promising. One specific scenario where I replaced the std TDictionary with the Rapid.Generics one (a notification bus where a dictionary is used to map the instances) had a performance increase of 10x or more which is beyond what I would expect and too good to ignore. Any feedback or contributions are welcome! Cheers, Edited Wednesday at 04:48 AM by Alex7691 4 3 Share this post Link to post
Stefan Glienke 2104 Posted Wednesday at 05:53 AM You could have given spring4d a try, which is actively maintained 6 Share this post Link to post
Alex7691 17 Posted Wednesday at 07:33 AM (edited) Hi Stefan, yes, I know that Spring4D is great but it would be impossible to refactor hundreds of occurrences of different data structures in a massive 3+ MLOC application... Rapid.Generics took me no more than 2 or 3 hours do change the application (of course, excluding many hours devoted to fix the library itself, but I was mostly doing it for fun in a "let's see if I can make it work" spirit). Basically I needed to do a grep seach/replace + a handful of tweaks here and there and voilá... it was building.... Cheers, Edited Wednesday at 07:33 AM by Alex7691 Share this post Link to post
David Heffernan 2406 Posted Wednesday at 07:50 AM 16 minutes ago, Alex7691 said: yes, I know that Spring4D is great but it would be impossible to refactor hundreds of occurrences of different data structures in a massive 3+ MLOC application And yet you did this with a other library? I'm curious. Why was it simple to integrate rapid? Does it have the same interface as rtl but is just faster? Share this post Link to post
Anders Melander 1960 Posted Wednesday at 09:31 AM It looks to have the same interface but jeez it's a lot of code to maintain. Share this post Link to post
Stefan Glienke 2104 Posted Wednesday at 02:00 PM I was just curious. I expected the fact that it's a drop-in replacement to be a reason. Rapid is quite an achievement and damn those collections are fast - I use them to challenge my implementation from time to time. The design decisions are different, and for the extensive API of spring, I have to sacrifice a few nanoseconds here and there, as much as I dislike that 6 Share this post Link to post
Alex7691 17 Posted Wednesday at 06:39 PM 10 hours ago, David Heffernan said: And yet you did this with a other library? I'm curious. Why was it simple to integrate rapid? Does it have the same interface as rtl but is just faster? Yep, Rapid is a drop-in replacement. Actually my code now has: uses {$IFDEF USE_RAPIDGENERICS} Rapid.Generics, {$ELSE} System.Generics.Collections, System.Generics.Defaults, {$ENDIF} I can just toggle this compiler directive globally and the 3+ MLOC application can be built either using Rapid generics or std System.Generics.Collections, which is a major bonus. At anytime I can just switch this off with minimal impact (e.g. let's say EMB releases a new faster System.Generics.Collections in Delphi 12+x?) 1 Share this post Link to post
David Heffernan 2406 Posted Wednesday at 06:47 PM 7 minutes ago, Alex7691 said: At anytime I can just switch this off with minimal impact (e.g. let's say EMB releases a new faster System.Generics.Collections in Delphi 12+x?) Nice thought experiment 2 Share this post Link to post
Alex7691 17 Posted Wednesday at 06:52 PM 9 hours ago, Anders Melander said: It looks to have the same interface but jeez it's a lot of code to maintain. As I said, it started more like a curiosity... like "it's so damn fast, but it's buggy... Can it be fixed to the point of being useful?" And after "wasting" the weekend, here I am Share this post Link to post
Anders Melander 1960 Posted Wednesday at 06:56 PM 14 minutes ago, Alex7691 said: I can just toggle this compiler directive globally and the 3+ MLOC application can be built either using Rapid generics or std System.Generics.Collections, which is a major bonus. At anytime I can just switch this off with minimal impact (e.g. let's say EMB releases a new faster System.Generics.Collections in Delphi 12+x?) Nice! Unfortunately I don't have any code where generics is anywhere near the bottleneck or I would have had a go with it. 1 Share this post Link to post
David Heffernan 2406 Posted Thursday at 08:24 AM 13 hours ago, Anders Melander said: I don't have any code where generics is anywhere near the bottleneck That's not what this is about, generics being a bottleneck. The potential bottleneck is the RTL dictionary class. Which happens to be a generic type. Share this post Link to post
Anders Melander 1960 Posted Thursday at 08:33 AM 2 minutes ago, David Heffernan said: The potential bottleneck is the RTL dictionary class. Which happens to be a generic type. Thanks for explaining that to me. Now I understand everything exactly like I did before. I don't have any code where anything in system.generics.collections or system.generics.defaults is anywhere near the bottleneck Better? Share this post Link to post
David Heffernan 2406 Posted Thursday at 09:56 AM 1 hour ago, Anders Melander said: Thanks for explaining that to me. You are most welcome! 😉 Share this post Link to post
Stefan Glienke 2104 Posted Thursday at 03:32 PM "It's not the bottleneck, therefore, I don't care" is the reason why Embarcadero does not care to implement anything that is reasonably optimized. 6 Share this post Link to post
Anders Melander 1960 Posted Thursday at 06:06 PM 2 hours ago, Stefan Glienke said: "It's not the bottleneck, therefore, I don't care" is the reason why Embarcadero does not care to implement anything that is reasonably optimized I really think it's more a question of lack of expertise, limited resources, and priorities. I think they would if they could but since they can't communicate that to the customer they instead come up with excuses that comes across as if they don't care. 3 Share this post Link to post
Tommi Prami 141 Posted Friday at 05:01 AM On 4/2/2025 at 1:42 AM, Alex7691 said: Improvements (Readme contains a more detailed list): Fixed multiple bugs Refactored and streamlined the code Removed some unused code Added 123 unit tests, creating a comprehensive test suite Could you please make the benchmark results like in the original version has. And possibly add the original into the mix, so can see how much the bugfixes ads overhear, correctness sometimes sadly has some penalty 😉 But anyhow, would be good addition. 1 Share this post Link to post
Sebam 0 Posted yesterday at 02:16 PM TObjectList<T> does not free owned objects upon destruction. program TestObjectList; {$APPTYPE CONSOLE} uses System.SysUtils, //System.Generics.Collections, Rapid.Generics in 'C:\d11\Rapid.Generics.v2\source\Rapid.Generics.pas'; type TSampleClass = class public constructor Create; destructor Destroy; override; end; constructor TSampleClass.Create; begin inherited; Writeln('Object created'); end; destructor TSampleClass.Destroy; begin Writeln('Object destroyed'); inherited; end; procedure Test; var List: TObjectList<TSampleClass>; begin List := TObjectList<TSampleClass>.Create(True); try List.Add(TSampleClass.Create); List.Add(TSampleClass.Create); List.Add(TSampleClass.Create); Writeln('Number of objects in list: ', List.Count); finally List.Free; end; end; begin try Test; Writeln('Press Enter to exit.'); Readln; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. Output: Object created Object created Object created Number of objects in list: 3 Press Enter to exit. Share this post Link to post
Alex7691 17 Posted yesterday at 08:27 PM 6 hours ago, Sebam said: TObjectList<T> does not free owned objects upon destruction. True thing. Please update the source code from github repo. It's fixed now and there are a few new test cases to cover TObjectList (tests for TObjectList are not complete yet). Thanks 1 1 Share this post Link to post
Ian Branch 135 Posted 21 hours ago (edited) D12.3, latest Git release of Rapid Generics 2, 64-bit testing using RapidGenericsTestSuite: [dcc64 Error] uListTest.pas(2225): E2532 Couldn't infer generic type argument from different argument types for method 'AreEqual' Edited 21 hours ago by Ian Branch 1 Share this post Link to post
Alex7691 17 Posted 20 hours ago 31 minutes ago, Ian Branch said: D12.3, latest Git release of Rapid Generics 2, 64-bit testing using RapidGenericsTestSuite: [dcc64 Error] uListTest.pas(2225): E2532 Couldn't infer generic type argument from different argument types for method 'AreEqual' It has also been fixed in the lastest version in github repo. Cheers, Share this post Link to post
pyscripter 740 Posted 20 hours ago @Alex7691Good work. It is useful to have a drop-in fast generics replacement. A couple of questions: Is there an impact on code size? I see a lot of code included that it is not directly related to generics. (e.g. TSyncSpinlock, TSyncLocker, TaggedPointer, TCustomObject. TLiteCustomObject etc.). Are they actually essential for the rapid collections? Share this post Link to post
Ian Branch 135 Posted 20 hours ago 19 minutes ago, Alex7691 said: It has also been fixed in the lastest version in github repo. Confirmed. Tks. Share this post Link to post
Rollo62 568 Posted 12 hours ago Peeking into this nice library, it seems pretty much multi-platform compatible to me, even if there were some encapsuled Intel ASMs inside. Nevertheless, it seems to be specified only for x86 basically. Quote Compatibility This has been tested with: Delphi 10, 10.1, 10.2, 10.3, 10.4, 11, 12 (x86 and x64) Not tested with any version of Lazarus/FPC yet Other compiler versions will be tested in the near future Have you tested this under VCL, FMX, Win32, Win64, Macos (x86/ARM), iOS, Android, Linux as well? Would be great to know, if this is fully supported under all Delphi platforms, maybe it was already tested against these? Share this post Link to post
Anders Melander 1960 Posted 12 hours ago 12 minutes ago, Rollo62 said: Have you tested this under VCL, FMX, Win32, Win64, Macos (x86/ARM), iOS, Android, Linux as well? Did you read the text you just quoted? Share this post Link to post
David Heffernan 2406 Posted 11 hours ago 53 minutes ago, Rollo62 said: Have you tested this under VCL, FMX How could VCL/FMX be relevant to code at the RTL level? Share this post Link to post