3
3
4
4
using System ;
5
5
using System . Collections . Concurrent ;
6
- using System . Collections . Generic ;
7
6
using System . Diagnostics ;
8
7
using System . Diagnostics . CodeAnalysis ;
9
8
using System . Linq ;
10
9
using System . Runtime . CompilerServices ;
11
10
using System . Threading ;
11
+ using System . Threading . Channels ;
12
12
using System . Threading . Tasks ;
13
13
using Nethermind . Core ;
14
14
using Nethermind . Core . Collections ;
15
+ using Nethermind . Core . Cpu ;
15
16
using Nethermind . Core . Crypto ;
16
17
using Nethermind . Core . Extensions ;
17
18
using Nethermind . Logging ;
@@ -32,7 +33,7 @@ public class TrieStore : ITrieStore, IPruningTrieStore
32
33
private readonly Task [ ] _dirtyNodesTasks = [ ] ;
33
34
private readonly ConcurrentDictionary < HashAndTinyPath , Hash256 ? > [ ] _persistedHashes = [ ] ;
34
35
private readonly Action < TreePath , Hash256 ? , TrieNode > _persistedNodeRecorder ;
35
- private readonly Task [ ] _disposeTasks = new Task [ Environment . ProcessorCount ] ;
36
+ private readonly Task [ ] _disposeTasks = new Task [ RuntimeInformation . PhysicalCoreCount ] ;
36
37
37
38
// This seems to attempt prevent multiple block processing at the same time and along with pruning at the same time.
38
39
private readonly object _dirtyNodesLock = new object ( ) ;
@@ -790,27 +791,33 @@ void TopLevelPersist(TrieNode tn, Hash256? address2, TreePath path)
790
791
// However, anything that we are trying to persist here should still be in dirty cache.
791
792
// So parallel read should go there first instead of to the database for these dataset,
792
793
// so it should be fine for these to be non atomic.
793
- using BlockingCollection < INodeStorage . WriteBatch > disposeQueue = new BlockingCollection < INodeStorage . WriteBatch > ( 4 ) ;
794
-
795
- for ( int index = 0 ; index < _disposeTasks . Length ; index ++ )
794
+ Task [ ] disposeTasks = _disposeTasks ;
795
+ Channel < INodeStorage . WriteBatch > disposeQueue = Channel . CreateBounded < INodeStorage . WriteBatch > ( disposeTasks . Length * 2 ) ;
796
+ try
796
797
{
797
- _disposeTasks [ index ] = Task . Run ( ( ) =>
798
+ for ( int index = 0 ; index < disposeTasks . Length ; index ++ )
798
799
{
799
- while ( disposeQueue . TryTake ( out INodeStorage . WriteBatch disposable , Timeout . Infinite ) )
800
+ disposeTasks [ index ] = Task . Run ( async ( ) =>
800
801
{
801
- disposable . Dispose ( ) ;
802
- }
803
- } ) ;
804
- }
802
+ await foreach ( IDisposable disposable in disposeQueue . Reader . ReadAllAsync ( ) )
803
+ {
804
+ disposable . Dispose ( ) ;
805
+ }
806
+ } ) ;
807
+ }
805
808
806
- using ArrayPoolList < Task > persistNodeStartingFromTasks = parallelStartNodes . Select (
807
- entry => Task . Run ( ( ) => PersistNodeStartingFrom ( entry . trieNode , entry . address2 , entry . path , persistedNodeRecorder , writeFlags , disposeQueue ) ) )
808
- . ToPooledList ( parallelStartNodes . Count ) ;
809
+ using ArrayPoolList < Task > persistNodeStartingFromTasks = parallelStartNodes . Select (
810
+ entry => Task . Run ( ( ) => PersistNodeStartingFrom ( entry . trieNode , entry . address2 , entry . path , persistedNodeRecorder , writeFlags , disposeQueue ) ) )
811
+ . ToPooledList ( parallelStartNodes . Count ) ;
809
812
810
- Task . WaitAll ( persistNodeStartingFromTasks . AsSpan ( ) ) ;
813
+ Task . WaitAll ( persistNodeStartingFromTasks . AsSpan ( ) ) ;
814
+ }
815
+ finally
816
+ {
817
+ disposeQueue . Writer . Complete ( ) ;
818
+ }
811
819
812
- disposeQueue . CompleteAdding ( ) ;
813
- Task . WaitAll ( _disposeTasks ) ;
820
+ Task . WaitAll ( disposeTasks ) ;
814
821
815
822
// Dispose top level last in case something goes wrong, at least the root won't be stored
816
823
topLevelWriteBatch . Dispose ( ) ;
@@ -824,28 +831,28 @@ void TopLevelPersist(TrieNode tn, Hash256? address2, TreePath path)
824
831
LastPersistedBlockNumber = commitSet . BlockNumber ;
825
832
}
826
833
827
- private void PersistNodeStartingFrom ( TrieNode tn , Hash256 address2 , TreePath path ,
834
+ private async Task PersistNodeStartingFrom ( TrieNode tn , Hash256 address2 , TreePath path ,
828
835
Action < TreePath , Hash256 ? , TrieNode > ? persistedNodeRecorder ,
829
- WriteFlags writeFlags , BlockingCollection < INodeStorage . WriteBatch > disposeQueue )
836
+ WriteFlags writeFlags , Channel < INodeStorage . WriteBatch > disposeQueue )
830
837
{
831
838
long persistedNodeCount = 0 ;
832
839
INodeStorage . WriteBatch writeBatch = _nodeStorage . StartWriteBatch ( ) ;
833
840
834
- void DoPersist ( TrieNode node , Hash256 ? address3 , TreePath path2 )
841
+ async ValueTask DoPersist ( TrieNode node , Hash256 ? address3 , TreePath path2 )
835
842
{
836
843
persistedNodeRecorder ? . Invoke ( path2 , address3 , node ) ;
837
844
PersistNode ( address3 , path2 , node , writeBatch , writeFlags ) ;
838
845
839
846
persistedNodeCount ++ ;
840
847
if ( persistedNodeCount % 512 == 0 )
841
848
{
842
- disposeQueue . Add ( writeBatch ) ;
849
+ await disposeQueue . Writer . WriteAsync ( writeBatch ) ;
843
850
writeBatch = _nodeStorage . StartWriteBatch ( ) ;
844
851
}
845
852
}
846
853
847
- tn . CallRecursively ( DoPersist , address2 , ref path , GetTrieStore ( address2 ) , true , _logger ) ;
848
- disposeQueue . Add ( writeBatch ) ;
854
+ await tn . CallRecursivelyAsync ( DoPersist , address2 , ref path , GetTrieStore ( address2 ) , _logger ) ;
855
+ await disposeQueue . Writer . WriteAsync ( writeBatch ) ;
849
856
}
850
857
851
858
private void PersistNode ( Hash256 ? address , in TreePath path , TrieNode currentNode , INodeStorage . WriteBatch writeBatch , WriteFlags writeFlags = WriteFlags . None )
0 commit comments