Jump to content
Alex7691

Rapid.Generics revamp

Recommended Posts

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 by Alex7691
  • Like 4
  • Thanks 3

Share this post


Link to post

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 by Alex7691

Share this post


Link to post
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

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 :classic_laugh:

  • Like 6

Share this post


Link to post
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?)

  • Like 1

Share this post


Link to post
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

  • Haha 2

Share this post


Link to post
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 :classic_laugh:

Share this post


Link to post
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.

  • Like 1

Share this post


Link to post
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
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

"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.

  • Like 6

Share this post


Link to post
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.

  • Like 3

Share this post


Link to post
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.

  • Like 1

Share this post


Link to post

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
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

  • Like 1
  • Thanks 1

Share this post


Link to post

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 by Ian Branch
  • Like 1

Share this post


Link to post
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

@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
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

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
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
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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×