Scalable reference counting with efficient support for weak references
Scalable reference counting with efficient support for weak references version 1.39
Author: Amine Moulay Ramdane
Description:
I have invented the following "scalable" reference counting with efficient support for weak references, it is suited for "highly" parallel applications and i have implemented it in Delphi and FreePascal, and you will not find it in C++ or Rust, I have benchmarked it and it is really amazing ! because it is really fast and it is "scalable"(on NUMA systems and multicores), read about it and download it from below:
I have enhanced my scalable algorithm and now it is much powerful, now my scalable algorithm implementation works also as a "scalable" counter that supports both "increment" and "decrement" using two scalable counting networks, please take a look at my new scalable algorithm implementation inside the source code..
This is my scalable reference counting with efficient support for weak references, and since problems that cannot be solved without weak references are rare, so this library does scale very well, this scalable reference counting is implemented using scalable counting networks that eliminate completely false sharing , so it is fully scalable on multicore processors and manycore processors and this scalable algorithm is optimized, and this library does work on both Windows and Linux (x86), and it is easy to port to Mac OS X.
I have modified my scalable algorithm, now as you will notice i am not using decrement with support for antitokens in the balancers of the scalable counting networks, i am only using an "increment", please look at my new scalable algorithm inside the zip file, i think it is working correctly. Also notice that the returned value of _Release() method will be valid if it is equal to 0.
I have optimized it more, now i am using only tokens and no antitokens in the balancers of the scalable counting networks, so i am only supporting increment, not decrement, so you have to be smart to invent it correctly, this is what i have done, so look at the AMInterfacedObject.pas file inside my zip file, you will notice that it uses counting_network_next_value() function, counting_network_next_value() increments the scalable counting network by 1, the _AddRef() method is simple, it increment by 1 to increment the reference to the object, but look inside the _Release() method it calls counting_network_next_value() three times, and my invention is calling counting_network_next_value(cn1) first inside the _Release() method to be able to make my scalable algorithm works, so just debug it more and you will notice that my scalable algorithm is smart and it is working correctly, i have debugged it and i think it is working correctly.
I have to prove my scalable reference counting algorithm, like with mathematical proof, so i will use logic to prove like in PhD papers:
You will find the code of my scalable reference counting inside AMInterfacedObject.pas inside the zip file.
If you look inside the code there is two methods, _AddRef() and _Release() methods, i am using two scalable counting networks, think about them like counters, so in the _AddRef() method i am executing the following:
v1 := counting_network_next_value(cn1);
cn1 is the scalable counting network, and counting_network_next_value() is a function that increment the scalable counting network by 1.
In the _Release() method i am executing the following:
v2 := counting_network_next_value(cn1);
v1 := counting_network_next_value(cn2);
v1 := counting_network_next_value(cn2);
So my scalable algorithm is "smart", because the logical proof is that i am calling counting_network_next_value(cn1) first in the above, so this allows my scalable algorithm to work correctly, because we are advancing cn1 by 1 to obtain the value of cn1, so the other threads are advancing also cn1 by one inside _Release() , it is the last thread that is advancing cn1 by 1 that will make the reference counter equal to 0 , and _AddRef() method is the same and it is easy to reason about, so this scalable algorithm is working. Please look more carefully at my algorithm and you will notice that it is working as i have just logically proved it.
Please read also the following to understand better:
To use scalable reference counting with Delphi and FreePascal, just replace TInterfacedObject with my TAMInterfacedObject that is the scalable reference counted version, and just replace TInterfacedPersistent with my TAMInterfacedPersistent that is the scalable reference counted version, and you will find both my TAMInterfacedObject and my TAMInterfacedPersistent inside the AMInterfacedObject.pas file, and to know how to use weak references please take a look at the demo that i have included called example.dpr and look inside my zip file at the tutorial about weak references, and to know how to use delegation take a look at the demo that i have included called test_delegation.pas, and take a look inside my zip file at the tutorial about delegation that learns you how to use delegation.
Here is the parameters of the constructors of my TAMInterfacedObject and my TAMInterfacedPersistent:
First parameter is: The width of the scalable counting networks that permits my scalable refererence counting algorithm to be scalable, this parameter must be 1 to 31, it is now at 4 , this is the power, so it is equal to 2 power 4 , that means 2^4=16, and you have to pass this counting networks width to the n of following formula:
(n*log(n)*(1+log(n)))/4
The log of the formula is in base 2
This formula gives the number of gates of the scalable counting networks, and if we replace n by 16, this will equal 80 gates, that means you can scale the scalable counting networks to 80 cores, and beyond 80 cores you will start to have contention.
Second parameter is: a boolean that tells if reference counting is used or not, it is by default to true, that means that reference counting is used.
About the weak references support: the Weak<T> type supports assignment from and to T and makes it usable as if you had a variable of T. It has the IsAlive property to check if the reference is still valid and not a dangling pointer. The Target property can be used if you want access to members of the reference.
Note: the use of the IsAlive property on our weak reference, this tells us whether the referenced object is still available, and provides a safe way to get a concrete reference to the parent.
I have ported efficient weak references support to Linux by implementing efficient code hooking, look at my DSharp.Core.Detour.pas file for Linux that i have written to see how i have implemented it in the Linux library. Please look at the example.dpr and test.pas demos to see how weak references work etc.
Call _AddRef() and _Release() methods to manually increment or decrement the number of references to the object.
Weak references support is done by hooking the TObject.FreeInstance method so every object destruction is noticed and if a weak reference for that object exists it gets removed from the internal dictionary where all weak references are stored. While it works I am aware that this is hacky approach and it might not work if someone overrides the FreeInstance method and does not call inherited.
You can go to download the zip files by clicking on the following web link:
https://drive.google.com/drive/folders/15rpMPdDTPzt3ClsODeI7d4uX1onwxq97?usp=sharing
- Platform: Windows and Linux(x86)
Language: FPC Pascal v3.1.x+ / Delphi 2007+:
http://www.freepascal.org/
Required FPC switches: -O3 -Sd
-Sd for delphi mode....
Required Delphi switches: -$H+ -DDelphi
For Delphi XE versions and Delphi Tokyo use the -DXE switch
The defines options inside defines.inc are:
{$DEFINE CPU32} for 32 bit systems
{$DEFINE CPU64} for 64 bit systems