1+ using System . Buffers ;
12using System . Collections ;
3+ using System . Runtime . CompilerServices ;
24
35namespace Requests . Channel
46{
@@ -18,6 +20,17 @@ public class ConcurrentPriorityQueue<TElement> : IEnumerable<PriorityItem<TEleme
1820 /// <summary>The binary logarithm of <see cref="Arity"/>.</summary>
1921 private const int Log2Arity = 2 ;
2022
23+ /// <summary>Struct comparer to avoid delegate allocation during sorting.</summary>
24+ private readonly struct ItemComparer : IComparer < ( PriorityItem < TElement > Item , long Order ) >
25+ {
26+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
27+ public int Compare ( ( PriorityItem < TElement > Item , long Order ) a , ( PriorityItem < TElement > Item , long Order ) b )
28+ {
29+ int priorityComparison = a . Item . Priority . CompareTo ( b . Item . Priority ) ;
30+ return priorityComparison != 0 ? priorityComparison : a . Order . CompareTo ( b . Order ) ;
31+ }
32+ }
33+
2134 /// <summary>Array-backed quaternary min-heap storing priority items.</summary>
2235 private PriorityItem < TElement > [ ] _nodes ;
2336
@@ -241,32 +254,59 @@ public bool TryRemove(PriorityItem<TElement> item)
241254 }
242255
243256 /// <summary>
244- /// Copies the elements stored in the queue to a new array.
257+ /// Copies the elements stored in the queue to a new array in heap order (unsorted).
258+ /// This is a fast O(n) operation that returns the internal heap structure.
245259 /// </summary>
246- /// <returns>A new array containing a snapshot of elements from the queue.</returns>
247- public PriorityItem < TElement > [ ] ToArray ( )
260+ /// <returns>A new array containing a snapshot of elements from the queue in heap order .</returns>
261+ public PriorityItem < TElement > [ ] ToHeapArray ( )
248262 {
249263 lock ( _lock )
250264 {
251265 if ( _size == 0 ) return [ ] ;
252266
253267 PriorityItem < TElement > [ ] result = new PriorityItem < TElement > [ _size ] ;
254268 Array . Copy ( _nodes , 0 , result , 0 , _size ) ;
269+ return result ;
270+ }
271+ }
255272
256- // Sort by priority, then by insertion order for stability
257- Array . Sort ( result , ( a , b ) =>
273+ /// <summary>
274+ /// Copies the elements stored in the queue to a new array in priority order (sorted).
275+ /// This is an O(n log n) operation. Uses ArrayPool to reduce GC pressure.
276+ /// </summary>
277+ /// <returns>A new array containing elements sorted by priority.</returns>
278+ public PriorityItem < TElement > [ ] ToArray ( )
279+ {
280+ ( PriorityItem < TElement > Item , long Order ) [ ] buffer ;
281+ int count ;
282+
283+ lock ( _lock )
284+ {
285+ count = _size ;
286+ if ( count == 0 ) return [ ] ;
287+
288+ buffer = ArrayPool < ( PriorityItem < TElement > , long ) > . Shared . Rent ( count ) ;
289+ for ( int i = 0 ; i < count ; i ++ )
258290 {
259- int priorityComparison = a . Priority . CompareTo ( b . Priority ) ;
260- if ( priorityComparison != 0 ) return priorityComparison ;
291+ buffer [ i ] = ( _nodes [ i ] , _insertionOrder [ i ] ) ;
292+ }
293+ }
261294
262- // For items with same priority, maintain insertion order
263- int aIndex = FindInsertionIndex ( a ) ;
264- int bIndex = FindInsertionIndex ( b ) ;
265- return aIndex . CompareTo ( bIndex ) ;
266- } ) ;
295+ try
296+ {
297+ buffer . AsSpan ( 0 , count ) . Sort ( default ( ItemComparer ) ) ;
267298
299+ PriorityItem < TElement > [ ] result = new PriorityItem < TElement > [ count ] ;
300+ for ( int i = 0 ; i < count ; i ++ )
301+ {
302+ result [ i ] = buffer [ i ] . Item ;
303+ }
268304 return result ;
269305 }
306+ finally
307+ {
308+ ArrayPool < ( PriorityItem < TElement > , long ) > . Shared . Return ( buffer , clearArray : true ) ;
309+ }
270310 }
271311
272312 /// <summary>
@@ -295,21 +335,41 @@ public bool IsValidQueue()
295335 }
296336
297337 /// <summary>
298- /// Returns an enumerator that iterates through the queue in heap order (not priority order).
338+ /// Returns an enumerator that iterates through the queue in priority order (sorted).
339+ /// Uses ArrayPool for reduced GC pressure. This is an O(n log n) operation.
299340 /// </summary>
300- /// <returns>An enumerator for the queue.</returns>
341+ /// <returns>An enumerator for the queue in priority order .</returns>
301342 public IEnumerator < PriorityItem < TElement > > GetEnumerator ( )
302343 {
303- PriorityItem < TElement > [ ] snapshot ;
344+ ( PriorityItem < TElement > Item , long Order ) [ ] rentedArray ;
345+ int count ;
346+
304347 lock ( _lock )
305348 {
306- snapshot = new PriorityItem < TElement > [ _size ] ;
307- Array . Copy ( _nodes , 0 , snapshot , 0 , _size ) ;
349+ count = _size ;
350+ if ( count == 0 ) yield break ;
351+
352+ // Rent from pool to reduce GC pressure
353+ rentedArray = ArrayPool < ( PriorityItem < TElement > , long ) > . Shared . Rent ( count ) ;
354+
355+ // Copy data while holding lock
356+ for ( int i = 0 ; i < count ; i ++ )
357+ {
358+ rentedArray [ i ] = ( _nodes [ i ] , _insertionOrder [ i ] ) ;
359+ }
308360 }
309361
310- foreach ( PriorityItem < TElement > item in snapshot )
362+ try
363+ {
364+ rentedArray . AsSpan ( 0 , count ) . Sort ( default ( ItemComparer ) ) ;
365+ for ( int i = 0 ; i < count ; i ++ )
366+ {
367+ yield return rentedArray [ i ] . Item ;
368+ }
369+ }
370+ finally
311371 {
312- if ( item != null ) yield return item ;
372+ ArrayPool < ( PriorityItem < TElement > , long ) > . Shared . Return ( rentedArray , clearArray : true ) ;
313373 }
314374 }
315375
@@ -509,24 +569,6 @@ private int FindIndex(PriorityItem<TElement> item)
509569 return - 1 ;
510570 }
511571
512- /// <summary>
513- /// Finds the insertion index for a given item (used for sorting).
514- /// </summary>
515- /// <param name="item">The item to find insertion index for.</param>
516- /// <returns>The insertion index, or -1 if not found.</returns>
517- private int FindInsertionIndex ( PriorityItem < TElement > item )
518- {
519- for ( int i = 0 ; i < _size ; i ++ )
520- {
521- if ( ReferenceEquals ( _nodes [ i ] , item ) ||
522- ( item != null && item . Equals ( _nodes [ i ] ) ) )
523- {
524- return ( int ) _insertionOrder [ i ] ;
525- }
526- }
527- return - 1 ;
528- }
529-
530572 /// <summary>
531573 /// Gets the index of an element's parent in the heap.
532574 /// </summary>
@@ -543,4 +585,4 @@ private int FindInsertionIndex(PriorityItem<TElement> item)
543585
544586 #endregion
545587 }
546- }
588+ }
0 commit comments