Skip to content

Commit 5ae35c0

Browse files
Optimize StablePriorityQueue
1 parent 10df32e commit 5ae35c0

File tree

2 files changed

+40
-27
lines changed

2 files changed

+40
-27
lines changed

src/PolygonClipper/PolygonClipper.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ public Polygon Run()
122122
// Process all segments in the subject polygon
123123
Vertex min = new(double.PositiveInfinity);
124124
Vertex max = new(double.NegativeInfinity);
125-
StablePriorityQueue<SweepEvent, SweepEventComparer> eventQueue = new(new SweepEventComparer());
125+
126+
int eventCount = (subject.GetVertexCount() + clipping.GetVertexCount()) * 2;
127+
StablePriorityQueue<SweepEvent, SweepEventComparer> eventQueue = new(new SweepEventComparer(), eventCount);
126128
int contourId = 0;
127129
for (int i = 0; i < subject.ContourCount; i++)
128130
{

src/PolygonClipper/StablePriorityQueue{T,TComparer}.cs

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
// Copyright (c) Six Labors.
1+
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

44
using System;
55
using System.Collections.Generic;
66
using System.Diagnostics;
7+
using System.Runtime.CompilerServices;
8+
using System.Runtime.InteropServices;
79

810
namespace PolygonClipper;
911

@@ -16,14 +18,18 @@ namespace PolygonClipper;
1618
internal sealed class StablePriorityQueue<T, TComparer>
1719
where TComparer : IComparer<T>
1820
{
19-
private readonly List<T> heap = [];
21+
private readonly List<T> heap;
2022

2123
/// <summary>
2224
/// Initializes a new instance of the <see cref="StablePriorityQueue{T, TComparer}"/> class with a specified comparer.
2325
/// </summary>
2426
/// <param name="comparer">The comparer to determine the priority of the elements.</param>
25-
public StablePriorityQueue(TComparer comparer)
26-
=> this.Comparer = comparer ?? throw new ArgumentNullException(nameof(comparer));
27+
/// <param name="capacity">The initial capacity of the priority queue.</param>
28+
public StablePriorityQueue(TComparer comparer, int capacity)
29+
{
30+
this.Comparer = comparer ?? throw new ArgumentNullException(nameof(comparer));
31+
this.heap = new List<T>(capacity > 0 ? capacity : 16);
32+
}
2733

2834
/// <summary>
2935
/// Gets the number of elements in the priority queue.
@@ -42,7 +48,7 @@ public StablePriorityQueue(TComparer comparer)
4248
public void Enqueue(T item)
4349
{
4450
this.heap.Add(item);
45-
this.Up(this.heap.Count - 1);
51+
this.Up((uint)this.heap.Count - 1);
4652
}
4753

4854
/// <summary>
@@ -86,61 +92,66 @@ public T Peek()
8692
}
8793

8894
/// <summary>
89-
/// Restores the heap property by moving the item at the specified index upward.
95+
/// Restores the min-heap property by moving the item at the specified index upward
96+
/// through the heap until it is in the correct position. This is called after insertion.
9097
/// </summary>
91-
/// <param name="index">The index of the item to move upward.</param>
92-
private void Up(int index)
98+
/// <param name="index">The index of the newly added item to sift upward.</param>
99+
private void Up(uint index)
93100
{
94-
List<T> data = this.heap;
95-
T item = data[index];
101+
ref T dRef = ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(this.heap));
102+
T item = Unsafe.Add(ref dRef, index);
96103
TComparer comparer = this.Comparer;
97104

98105
while (index > 0)
99106
{
100-
int parent = (index - 1) >> 1;
101-
T current = data[parent];
107+
uint parent = (index - 1u) >> 1;
108+
T current = Unsafe.Add(ref dRef, parent);
102109
if (comparer.Compare(item, current) >= 0)
103110
{
104111
break;
105112
}
106113

107-
data[index] = current;
114+
Unsafe.Add(ref dRef, index) = current;
108115
index = parent;
109116
}
110117

111-
data[index] = item;
118+
Unsafe.Add(ref dRef, index) = item;
112119
}
113120

114121
/// <summary>
115-
/// Restores the heap property by moving the item at the specified index downward.
122+
/// Restores the min-heap property by moving the item at the specified index downward
123+
/// through the heap until it is in the correct position. This is called after removal of the root.
116124
/// </summary>
117-
/// <param name="index">The index of the item to move downward.</param>
118-
private void Down(int index)
125+
/// <param name="index">The index of the item to sift downward (typically the root).</param>
126+
private void Down(uint index)
119127
{
120-
List<T> data = this.heap;
121-
int halfLength = data.Count >> 1;
122-
T item = data[index];
128+
Span<T> data = CollectionsMarshal.AsSpan(this.heap);
129+
ref T dRef = ref MemoryMarshal.GetReference(data);
130+
131+
uint length = (uint)data.Length;
132+
uint halfLength = length >> 1;
133+
T item = Unsafe.Add(ref dRef, index);
123134
TComparer comparer = this.Comparer;
124135

125136
while (index < halfLength)
126137
{
127-
int bestChild = (index << 1) + 1; // Initially left child
128-
int right = bestChild + 1;
138+
uint bestChild = (index << 1) + 1; // Initially left child
139+
uint right = bestChild + 1u;
129140

130-
if (right < data.Count && comparer.Compare(data[right], data[bestChild]) < 0)
141+
if (right < length && comparer.Compare(Unsafe.Add(ref dRef, right), Unsafe.Add(ref dRef, bestChild)) < 0)
131142
{
132143
bestChild = right;
133144
}
134145

135-
if (comparer.Compare(data[bestChild], item) >= 0)
146+
if (comparer.Compare(Unsafe.Add(ref dRef, bestChild), item) >= 0)
136147
{
137148
break;
138149
}
139150

140-
data[index] = data[bestChild];
151+
Unsafe.Add(ref dRef, index) = Unsafe.Add(ref dRef, bestChild);
141152
index = bestChild;
142153
}
143154

144-
data[index] = item;
155+
Unsafe.Add(ref dRef, index) = item;
145156
}
146157
}

0 commit comments

Comments
 (0)