1- using Schedulers . Utils ;
1+ using System . Runtime . InteropServices ;
2+ using Schedulers . Utils ;
23
34namespace Schedulers ;
45
@@ -11,15 +12,21 @@ internal class Worker
1112 private readonly int _workerId ;
1213 private readonly Thread _thread ;
1314
15+ /// <summary>
16+ /// Use a single combined <see cref="UnorderedThreadSafeQueue{T}"/> for all <see cref="Worker"/>s to enqueue <see cref="JobHandle"/>s.
17+ /// Because there really isn't performance advantage to having multiple queues for each <see cref="Worker"/>,
18+ /// </summary>
1419 private static readonly UnorderedThreadSafeQueue < JobHandle > _incomingQueue = new ( ) ;
15- private readonly WorkStealingDeque < JobHandle > _queue ;
1620
21+ private readonly WorkStealingDeque < JobHandle > _queue ;
1722 private readonly JobScheduler _jobScheduler ;
1823 private volatile CancellationTokenSource _cancellationToken ;
24+ private static readonly BulkQueue < JobHandle > _bulkQueue = new ( 1000 , 128 ) ;
1925 public bool IsCurrentlyWorking { get ; private set ; } = false ;
26+
2027 // use a high spin count to avoid sleeping the thread under variable load.
2128 // 2047 is the maximum value for a ManualResetEventSlim spin count.
22- private readonly ManualResetEventSlim _workAvailable = new ( false , 2047 ) ;
29+ private readonly static ManualResetEventSlim _workAvailable = new ( false , 2047 ) ;
2330
2431 /// <summary>
2532 /// Creates a new <see cref="Worker"/>.
@@ -39,16 +46,25 @@ public Worker(JobScheduler jobScheduler, int id)
3946 _thread . Name = $ "Arch Worker #{ _workerId } ";
4047 }
4148
42- public bool Enqueue ( JobHandle handle )
49+ public static bool Enqueue ( JobHandle handle )
4350 {
4451 var result = _incomingQueue . TryEnqueue ( handle ) ;
45- _workAvailable . Set ( ) ;
4652 return result ;
4753 }
4854
4955 public void ForceEnqueue ( JobHandle handle )
5056 {
51- _incomingQueue . ForceEnqueue ( handle ) ;
57+ _incomingQueue . ForceEnqueue ( handle ) ;
58+ }
59+
60+ public static bool GetEmptySegment ( out BulkQueue < JobHandle > . Segment segment )
61+ {
62+ return _bulkQueue . GetEmtySegment ( out segment ) ;
63+ }
64+
65+ public static void EnqueueFullSegment ( BulkQueue < JobHandle > . Segment segment )
66+ {
67+ _bulkQueue . Enqueue ( segment ) ;
5268 }
5369
5470 /// <summary>
@@ -81,7 +97,14 @@ public void Start()
8197 public void Stop ( )
8298 {
8399 _cancellationToken . Cancel ( ) ;
84- // _workAvailable.Set();
100+ _workAvailable . Set ( ) ;
101+ }
102+
103+ public static bool TryStealJobExternal ( out JobHandle job )
104+ {
105+ // job = new JobHandle();
106+ // return false;
107+ return _incomingQueue . TryDequeue ( out job ) ;
85108 }
86109
87110 /// <summary>
@@ -95,11 +118,29 @@ private void Run(CancellationToken token)
95118 {
96119 while ( ! token . IsCancellationRequested )
97120 {
121+ // it is faster to do it in a single threaded manner to lower the queue contention.
122+ if ( _workerId == 0 )
123+ {
124+ while ( _bulkQueue . GetFullSegment ( out var segment ) )
125+ {
126+ while ( segment . Dequeue ( out var item ) )
127+ {
128+ ForceEnqueue ( item ) ;
129+ }
130+ _bulkQueue . Return ( segment ) ;
131+ }
132+ }
133+
98134 IsCurrentlyWorking = true ;
99135 var noWorkFound = true ;
100136 // Pass jobs to the local queue
101137 while ( _queue . Size ( ) < 32 && _incomingQueue . TryDequeue ( out var jobHandle ) )
102138 {
139+ if ( _workerId == 0 )
140+ {
141+ _workAvailable . Set ( ) ;
142+ }
143+
103144 _queue . PushBottom ( jobHandle ) ;
104145 noWorkFound = false ;
105146 }
@@ -134,6 +175,7 @@ private void Run(CancellationToken token)
134175 break ;
135176 }
136177 }
178+
137179 if ( noWorkFound )
138180 {
139181 IsCurrentlyWorking = false ;
@@ -146,7 +188,7 @@ private void Run(CancellationToken token)
146188 else
147189 {
148190 _workAvailable . Reset ( ) ;
149- _workAvailable . Wait ( 1 , token ) ;
191+ _workAvailable . Wait ( 100 , token ) ;
150192 }
151193 }
152194 }
0 commit comments