You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When there are many elements cached in the FIFOSet, its Add and ExceptWith efficiency will become worse. It is very obvious during performance testing, in the TaskManager's RestartTask,
the ExceptWith method is used to exclude 16,000 of the 62,800 knownHashes. Each delete performance is 0.015s and totally spends 240s. The process will get stuck. That means under high overload, if the nodes are out of sync due to lacking of a large number of transactions, obtaining transactions will become extremely slow.
The reason is mainly the computational efficiency issue of the OrderedDictionary. The unit test below will show the performance for large number of elements.
[TestMethod]publicvoidTestFIFOSet(){Stopwatchstopwatch=newStopwatch();varbucket=newFIFOSet<int>(150_000);stopwatch.Start();for(inti=1;i<=550_000;i++){bucket.Add(i);}stopwatch.Stop();Console.WriteLine($"Add timespan: {stopwatch.Elapsed.TotalSeconds}s");stopwatch.Reset();varitems=newint[10000];varvalue=550_000;for(inti=0;i<=9999;i++){items[i]=value;value-=50;}stopwatch.Start();bucket.ExceptWith(items);stopwatch.Stop();Console.WriteLine($"except with timespan: {stopwatch.Elapsed.TotalSeconds}s");stopwatch.Reset();stopwatch.Start();varret=bucket.Contains(140_000);stopwatch.Stop();Console.WriteLine($"contains with timespan: {stopwatch.Elapsed.TotalSeconds}s result: {ret}");stopwatch.Reset();stopwatch.Start();ret=bucket.Contains(545_001);stopwatch.Stop();Console.WriteLine($"contains with timespan: {stopwatch.Elapsed.TotalSeconds}s result: {ret}");stopwatch.Reset();}
Console prints
Add timespan: 21.3015352s
except with timespan: 10.8901768s
contains with timespan: 0.0001552s result: False
contains with timespan: 5E-07s result: True
Do you have any solution you want to propose?
Currently, we only use FIFOSet to cache UInt256 hashes. The elimination order is based on the sequence of the hashes. In this case the elimination order is not critical. Therefore, we can use the List of HashSet instead, create a List containing multiple HashSets, and each element is stored in the HashSet in chronological order. When the previous HashSet is full, it starts to load the next HashSet. If all HashSets are full, delete the first HashSet in the List and add a new empty HashSet at the end of the List to hold the newly added elements.
The design of this class is as follows
publicclassHashSetCache<T>:IEnumerable<T>whereT:IEquatable<T>{privatereadonlyinthashSetCapacity;//maxmium of elements in each hashsetprivatereadonlyList<HashSet<T>>sets=newList<HashSet<T>>();publicintSize{get{intsize=0;foreach(varsetinsets){size+=set.Count;}returnsize;}}publicHashSetCache(inthashSetCapacity,inthashSetCount=10){if(hashSetCapacity<=0)thrownewArgumentOutOfRangeException(nameof(hashSetCapacity));if(hashSetCount<=0||hashSetCount>20)thrownewArgumentOutOfRangeException($"{nameof(hashSetCount)} should between 1 and 20");this.hashSetCapacity=hashSetCapacity;for(inti=0;i<hashSetCount;i++){sets.Add(newHashSet<T>());}}publicboolAdd(Titem){if(Contains(item))returnfalse;foreach(varsetinsets){if(set.Count<hashSetCapacity){returnset.Add(item);}}sets.RemoveAt(0);varnewSet=newHashSet<T>{item};sets.Add(newSet);returntrue;}publicboolContains(Titem){foreach(varsetinsets){if(set.Contains(item))returntrue;}returnfalse;}publicvoidExceptWith(IEnumerable<T>items){foreach(variteminitems){foreach(varsetinsets){if(set.Remove(item))break;}}}publicIEnumerator<T>GetEnumerator(){foreach(varsetinsets){foreach(variteminset){yieldreturnitem;}}}IEnumeratorIEnumerable.GetEnumerator()=>GetEnumerator();}
The unit test shows this design is much faster than OrderedDictionary under the same input condition.
[TestMethod]publicvoidTestHashSetCache(){Stopwatchstopwatch=newStopwatch();varbucket=newHashSetCache<int>(15_000);stopwatch.Start();for(inti=1;i<=550_000;i++){bucket.Add(i);}stopwatch.Stop();Console.WriteLine($"Add timespan: {stopwatch.Elapsed.TotalSeconds}s");stopwatch.Reset();varitems=newint[10000];varvalue=550_000;for(inti=0;i<=9999;i++){items[i]=value;value-=50;}stopwatch.Start();bucket.ExceptWith(items);stopwatch.Stop();Console.WriteLine($"except with timespan: {stopwatch.Elapsed.TotalSeconds}s");stopwatch.Reset();stopwatch.Start();varret=bucket.Contains(140_000);stopwatch.Stop();Console.WriteLine($"contains with timespan: {stopwatch.Elapsed.TotalSeconds}s result: {ret}");stopwatch.Reset();stopwatch.Start();ret=bucket.Contains(545_001);stopwatch.Stop();Console.WriteLine($"contains with timespan: {stopwatch.Elapsed.TotalSeconds}s result: {ret}");stopwatch.Reset();}
Here is the Console print
Add timespan: 0.2357612s
except with timespan: 0.0035483s
contains with timespan: 2.1E-06s result: False
contains with timespan: 4.6E-06s result: True
Neo Version
Neo 3
Where in the software does this update applies to?
Consensus
Ledger
The text was updated successfully, but these errors were encountered:
Summary or problem description
When there are many elements cached in the
FIFOSet
, itsAdd
andExceptWith
efficiency will become worse. It is very obvious during performance testing, in the TaskManager's RestartTask,neo/src/neo/Network/P2P/TaskManager.cs
Lines 140 to 142 in 44e7f3b
the ExceptWith method is used to exclude 16,000 of the 62,800 knownHashes. Each delete performance is 0.015s and totally spends 240s. The process will get stuck. That means under high overload, if the nodes are out of sync due to lacking of a large number of transactions, obtaining transactions will become extremely slow.
The reason is mainly the computational efficiency issue of the OrderedDictionary. The unit test below will show the performance for large number of elements.
Console prints
Do you have any solution you want to propose?
Currently, we only use
FIFOSet
to cacheUInt256
hashes. The elimination order is based on the sequence of the hashes. In this case the elimination order is not critical. Therefore, we can use the List of HashSet instead, create a List containing multiple HashSets, and each element is stored in the HashSet in chronological order. When the previous HashSet is full, it starts to load the next HashSet. If all HashSets are full, delete the first HashSet in the List and add a new empty HashSet at the end of the List to hold the newly added elements.The design of this class is as follows
The unit test shows this design is much faster than OrderedDictionary under the same input condition.
Here is the Console print
Neo Version
Where in the software does this update applies to?
The text was updated successfully, but these errors were encountered: