Skip to content

Commit 679618a

Browse files
author
Nathan Shreve
committed
Added Doxygen comments to HeapImplementation and its children (excluding bucket heap)
1 parent bde3cae commit 679618a

File tree

4 files changed

+201
-88
lines changed

4 files changed

+201
-88
lines changed

vpr/src/route/four_ary_heap.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@ class FourAryHeap : public KAryHeap {
1212
private:
1313
void sift_down(size_t hole) final;
1414
size_t parent(size_t i) const final;
15+
16+
/**
17+
* @param i The parent node.
18+
*
19+
* @return The child node of i with the smallest cost.
20+
*/
1521
size_t smallest_child(size_t i) const;
1622
};
1723

vpr/src/route/heap_type.h

Lines changed: 120 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -96,116 +96,152 @@ class HeapStorage {
9696
size_t num_heap_allocated_;
9797
};
9898

99-
// Interface to heap used for router optimization.
100-
//
101-
// Note: Objects used in instances of HeapInterface must always be allocated
102-
// and free'd using the HeapInterface::alloc and HeapInterface::free methods
103-
// of that instance. Object pools are likely in use.
104-
//
105-
// As a general rule, any t_heap objects returned from this interface,
106-
// **must** be HeapInterface::free'd before destroying the HeapInterface
107-
// instance. This ensure that no leaks are present in the users of the heap.
108-
// Violating this assumption may result in a assertion violation.
99+
/**
100+
* @brief Interface to heap used for router optimization.
101+
*
102+
* @details
103+
* Note: Objects used in instances of HeapInterface must always be allocated
104+
* and free'd using the HeapInterface::alloc and HeapInterface::free methods
105+
* of that instance. Object pools are likely in use.<BR><BR>
106+
* As a general rule, any t_heap objects returned from this interface,
107+
* **must** be HeapInterface::free'd before destroying the HeapInterface
108+
* instance. This ensure that no leaks are present in the users of the heap.
109+
* Violating this assumption may result in a assertion violation.
110+
*/
109111
class HeapInterface {
110112
public:
111113
virtual ~HeapInterface() {}
112114

113-
// Allocate a heap item.
114-
//
115-
// This transfers ownership of the t_heap object from HeapInterface to the
116-
// caller.
115+
/**
116+
* @brief Allocate a heap item.
117+
*
118+
* @details
119+
* This transfers ownership of the t_heap object from HeapInterface to the
120+
* caller.
121+
*/
117122
virtual t_heap* alloc() = 0;
118123

119-
// Free a heap item.
120-
//
121-
// HeapInterface::free can be called on objects returned from either
122-
// HeapInterface::alloc or HeapInterface::get_heap_head.
124+
/**
125+
* @brief Free a heap item.
126+
*
127+
* @details
128+
* HeapInterface::free can be called on objects returned from either
129+
* HeapInterface::alloc or HeapInterface::get_heap_head.
130+
*
131+
* @param hptr The element to free.
132+
*/
123133
virtual void free(t_heap* hptr) = 0;
124134

125-
// Initializes heap storage based on the size of the device.
126-
//
127-
// Note: this method **must** be invoked at least once prior to the
128-
// following methods being called:
129-
// - add_to_heap
130-
// - push_back
131-
// - get_heap_head
132-
// - is_empty_heap
133-
// - empty_heap
134-
// - build_heap
135+
/**
136+
* @brief Initializes heap storage based on the size of the device.
137+
*
138+
* @details
139+
* Note: this method **must** be invoked at least once prior to the
140+
* following methods being called:<BR>
141+
* - add_to_heap<BR>
142+
* - push_back<BR>
143+
* - get_heap_head<BR>
144+
* - is_empty_heap<BR>
145+
* - empty_heap<BR>
146+
* - build_heap<BR>
147+
*
148+
* @param grid
149+
*/
135150
virtual void init_heap(const DeviceGrid& grid) = 0;
136151

137-
// Add t_heap to heap, preserving heap property.
138-
//
139-
// This transfers ownership of the t_heap object to HeapInterface from the
140-
// called.
152+
/**
153+
* @brief Add t_heap to heap, preserving heap property.
154+
*
155+
* @details
156+
* This transfers ownership of the t_heap object to HeapInterface from the
157+
* called.
158+
*
159+
* @param hptr The element to add.
160+
*/
141161
virtual void add_to_heap(t_heap* hptr) = 0;
142162

143-
// Add t_heap to heap, however does not preserve heap property.
144-
//
145-
// This is useful if multiple t_heap's are being added in bulk. Once
146-
// all t_heap's have been added, HeapInterface::build_heap can be invoked
147-
// to restore the heap property in an efficient way.
148-
//
149-
// This transfers ownership of the t_heap object to HeapInterface from the
150-
// called.
163+
/**
164+
* @brief Add t_heap to heap, however does not preserve heap property.
165+
*
166+
* @details
167+
* This is useful if multiple t_heap's are being added in bulk. Once
168+
* all t_heap's have been added, HeapInterface::build_heap can be invoked
169+
* to restore the heap property in an efficient way.<BR><BR>
170+
* This transfers ownership of the t_heap object to HeapInterface from the
171+
* called.
172+
*
173+
* @param hptr The element to insert.
174+
*/
151175
virtual void push_back(t_heap* const hptr) = 0;
152176

153-
// Restore the heap property.
154-
//
155-
// This is useful in conjunction with HeapInterface::push_back when adding
156-
// multiple heap elements.
177+
/**
178+
* @brief Restore the heap property.
179+
*
180+
* @details
181+
* This is useful in conjunction with HeapInterface::push_back when adding
182+
* multiple heap elements.
183+
*/
157184
virtual void build_heap() = 0;
158185

159-
// Pop the head (smallest element) of the heap, and return it.
160-
//
161-
// This transfers ownership of the t_heap object from HeapInterface to the
162-
// caller.
186+
/**
187+
* @brief Pop the head (smallest element) of the heap, and return it.
188+
*
189+
* @details
190+
* This transfers ownership of the t_heap object from HeapInterface to the
191+
* caller.
192+
*/
163193
virtual t_heap* get_heap_head() = 0;
164194

165-
// Is the heap empty?
195+
/**
196+
* @brief Is the heap empty?
197+
*/
166198
virtual bool is_empty_heap() const = 0;
167199

168-
// Is the heap valid?
200+
/**
201+
* @brief Is the heap valid?
202+
*/
169203
virtual bool is_valid() const = 0;
170204

171-
// Empty all items from the heap.
205+
/**
206+
* @brief Empty all items from the heap.
207+
*/
172208
virtual void empty_heap() = 0;
173209

174-
// Free all storage used by the heap.
175-
//
176-
// This returns all memory allocated by the HeapInterface instance. Only
177-
// call this if the heap is no longer being used.
178-
//
179-
// Note: Only invoke this method if all objects returned from this
180-
// HeapInterface instace have been free'd.
210+
/**
211+
* @brief Free all storage used by the heap.
212+
*
213+
* @details
214+
* This returns all memory allocated by the HeapInterface instance. Only
215+
* call this if the heap is no longer being used.<BR><BR>
216+
* Note: Only invoke this method if all objects returned from this
217+
* HeapInterface instance have been free'd.
218+
*/
181219
virtual void free_all_memory() = 0;
182220

183-
// Set maximum number of elements that the heap should contain
184-
// (the prune_limit). If the prune limit is hit, then the heap should
185-
// kick out duplicate index entries.
186-
//
187-
// The prune limit exists to provide a maximum bound on memory usage in
188-
// the heap. In some pathological cases, the router may explore
189-
// incrementally better paths, resulting in many duplicate entries for
190-
// RR nodes. To handle this edge case, if the number of heap items
191-
// exceeds the prune_limit, then the heap will compacts itself.
192-
//
193-
// The heap compaction process simply means taking the lowest cost entry
194-
// for each index (e.g. RR node). All nodes with higher costs can safely
195-
// be dropped.
196-
//
197-
// The pruning process is intended to bound the memory usage the heap can
198-
// consume based on the prune_limit, which is expected to be a function of
199-
// the graph size.
200-
//
201-
// max_index should be the highest index possible in the heap.
202-
// prune_limit is the maximuming number of heap entries before pruning
203-
// should take place.
204-
//
205-
// The prune_limit should always be higher than max_index, likely by a
206-
// significant amount. The pruning process has some overhead, so
207-
// prune_limit should be ~2-4x the max_index to prevent excess pruning
208-
// when not required.
221+
/**
222+
* @brief Set maximum number of elements that the heap should contain
223+
* (the prune_limit). If the prune limit is hit, then the heap should
224+
* kick out duplicate index entries.
225+
*
226+
* @details
227+
* The prune limit exists to provide a maximum bound on memory usage in
228+
* the heap. In some pathological cases, the router may explore
229+
* incrementally better paths, resulting in many duplicate entries for
230+
* RR nodes. To handle this edge case, if the number of heap items
231+
* exceeds the prune_limit, then the heap will compacts itself.<BR><BR>
232+
* The heap compaction process simply means taking the lowest cost entry
233+
* for each index (e.g. RR node). All nodes with higher costs can safely
234+
* be dropped.<BR><BR>
235+
* The pruning process is intended to bound the memory usage the heap can
236+
* consume based on the prune_limit, which is expected to be a function of
237+
* the graph size.
238+
*
239+
* @param max_index The highest index possible in the heap.
240+
* @param prune_limit The maximum number of heap entries before pruning should
241+
* take place. This should alwasy be higher than max_index, likely by a
242+
* significant amount. The pruning process has some overhead, so prune_limit
243+
* should be ~2-4x the max_index to prevent excess pruning when not required.
244+
*/
209245
virtual void set_prune_limit(size_t max_index, size_t prune_limit) = 0;
210246
};
211247

vpr/src/route/k_ary_heap.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ void KAryHeap::empty_heap() {
6060
size_t KAryHeap::size() const { return heap_tail_ - 1; } // heap[0] is not valid element
6161

6262
// runs in O(n) time by sifting down; the least work is done on the most elements: 1 swap for bottom layer, 2 swap for 2nd, ... lgn swap for top
63-
// 1*(n/2) + 2*(n/4) + 3*(n/8) + ... + lgn*1 = 2n (sum of i/2^i)
63+
// 1*(n/k^1) + 2*(n/k^2) + 3*(n/k^3) + ... + lgn*1 = k*n (sum of i/k^i)
6464
void KAryHeap::build_heap() {
6565
for (size_t i = parent(heap_tail_); i != 0; --i)
6666
sift_down(i);
@@ -74,7 +74,6 @@ void KAryHeap::set_prune_limit(size_t max_index, size_t prune_limit) {
7474
prune_limit_ = prune_limit;
7575
}
7676

77-
// O(lgn) sifting up to maintain heap property after insertion (should sift down when building heap)
7877
void KAryHeap::sift_up(size_t leaf, heap_elem const& node) {
7978
while ((leaf > 1) && (node.cost < heap_[parent(leaf)].cost)) {
8079
// sift hole up
@@ -85,7 +84,6 @@ void KAryHeap::sift_up(size_t leaf, heap_elem const& node) {
8584
heap_[leaf] = node;
8685
}
8786

88-
//expands heap by "realloc"
8987
void KAryHeap::expand_heap_if_full() {
9088
if (heap_tail_ >= heap_size_) { /* Heap is full */
9189
heap_size_ *= 2;

vpr/src/route/k_ary_heap.h

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@
44
#include "heap_type.h"
55
#include <vector>
66

7+
/**
8+
* @brief Abstract class whose children are HeapInterface implementations of a k-ary minheap.
9+
*
10+
* @details
11+
* Currently, KAryHeap's two children are BinaryHeap and FourAryHeap. On small circuits, these
12+
* heaps have negligible differences in runtime, but on larger heaps, runtime is lower when
13+
* using FourAryHeap. On titan benchmarks, the runtime is ~1.8% better on FourAryHeap compared
14+
* to BinaryHeap. This is likely because FourAryHeap is more cache friendly, as we can fit 5
15+
* heap_elem on a cache line.
16+
*/
717
class KAryHeap : public HeapInterface {
818
public:
919
KAryHeap();
@@ -25,23 +35,86 @@ class KAryHeap : public HeapInterface {
2535
virtual t_heap* get_heap_head() = 0;
2636

2737
protected:
38+
/**
39+
* @brief The struct which the heap_ vector contains.
40+
*
41+
* @details
42+
* Previously, heap_ was made of only t_heap pointers. This meant that
43+
* all comparisons required dereferencing to attain the element's cost.
44+
* Now, the cost is attained by dereferencing only once in add_to_heap().
45+
* This resulted in a slightly larger memory footprint but a ~1.4% runtime
46+
* improvement.
47+
*
48+
* @param elem_ptr A pointer to the t_heap struct which contains all
49+
* the node's information.
50+
* @param cost The cost of the node.
51+
*/
52+
/* TODO: We are currently storing the node cost in two places (in elem_ptr->cost and cost). This might be fixed in two ways:
53+
* 1. Don't store the cost in t_heap.
54+
* 2. Instead of using pointers, use a 32-bit ID. If we do this, we can create a new 8-ary heap, which is likely to be even
55+
* faster as we can fit more heap_elem on one cache line (currently, we can fit 5 as heap_elem is 12 bytes), even with more
56+
* comparisons.*/
2857
struct heap_elem {
2958
t_heap* elem_ptr;
3059
float cost;
3160
};
3261

62+
/**
63+
* @return The number of elements in the heap.
64+
*/
3365
size_t size() const;
66+
67+
/**
68+
* @brief Sift node up until it satisfies minheap property.
69+
*
70+
* @details
71+
* O(lgn) sifting up to maintain heap property after insertion (should sift
72+
* own when building heap)
73+
*
74+
* @param leaf The heap leaf where node currently resides.
75+
* @param node The node to be sifted up.
76+
*/
3477
void sift_up(size_t leaf, heap_elem const& node);
78+
79+
/**
80+
* @brief Expands heap by 2 times if it is full.
81+
*/
3582
void expand_heap_if_full();
83+
84+
/**
85+
* @brief If the size of the heap is greater than the prune limit, prune the heap.
86+
*
87+
* @return Whether the heap was pruned.
88+
*/
3689
bool check_prune_limit();
90+
91+
/**
92+
* @brief Prune the heap.
93+
*/
3794
void prune_heap();
3895

96+
/**
97+
* @brief Make a heap rooted at index hole by **sifting down** in O(lgn) time
98+
*
99+
* @param hole
100+
*/
39101
virtual void sift_down(size_t hole) = 0;
102+
103+
/**
104+
* @param i Heap child node.
105+
*
106+
* @return Heap parent node.
107+
*/
40108
virtual size_t parent(size_t i) const = 0;
41109

42110
HeapStorage storage_;
43111

44-
/* heap_ is indexed from [1..heap_size] */
112+
/**
113+
* heap_ is indexed from [1..heap_size]; the 0th element is unused. For BinaryHeap, this simplifies
114+
* arithmetic in left() and parent() functions. Using a heap beginning at index 0 would simplify
115+
* leftmost_child() and parent() functions in FourAryHeap, but this does not improve runtime.
116+
*/
117+
/* TODO: If an 8-ary heap is implemented, experiment with starting at index 0 */
45118
std::vector<heap_elem> heap_;
46119

47120
size_t heap_size_; /* Number of slots in the heap array */

0 commit comments

Comments
 (0)