-
Notifications
You must be signed in to change notification settings - Fork 5
/
graph_plan.txt
354 lines (259 loc) · 12.7 KB
/
graph_plan.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# Plan for a C++20 Graph Class Optimized for Chip Design (Netlist)
## Introduction
This plan outlines the design of a C++20 class for handling graphs optimized for chip design or netlists. The graph represents logic cells (nodes) and their connections (edges) at the pin level. The goal is to create an efficient and mutable graph structure that handles frequent node deletions, supports topological traversals, and optimizes memory usage for both small and large numbers of edges.
By focusing on efficient storage, mutability, and optimization techniques like delta encoding and overflow management, the design aims to handle the specific needs of hardware logic representation. The separation of meta-information and careful management of IDs and edges ensure that the graph remains efficient even as it mutates and scales.
## Data Structures
### Nodes
- **Node ID (Nid)**: A unique 32-bit integer that indexes into a `std::vector`.
- **Pins**: Each node contains at least one pin. To optimize storage, the first pin (Pin 0) ID corresponds to the Node ID.
- **Type**: A small integer representing the node's logic type (e.g., XOR, AND).
- **Edges**: Stored within the node if they originate from Pin 0. Overflow to a hashmap if too many edges exist.
### Pins
- **Pin ID**: Unique identifiers for each pin, also indexing into a `std::vector`.
- **Port ID**: Represents the specific port (`Port_ID`) of the node (e.g., input A, output Y).
- **Edges**: Connections to other pins, optimized for a small number of bidirectional edges. Overflow to a hashmap if necessary.
### Edges
- **Bidirectional**: Edges are bidirectional connections between pins.
- **Edge Storage**:
- **Local Edges**: Stored directly within the pin or node using delta encoding.
- **Overflow Edges**: If edges exceed local storage capacity, they overflow into a separate data structure (e.g., a `hash_set8`).
## Graph Representation
- **Vectors Instead of Pointers**: All nodes and pins are stored in `std::vector`s, indexed by their IDs to avoid pointer overhead.
- **Entry Types**: Each entry in the vectors can be of type Free, Node, Pin, or Overflow.
- **Overflow Handling**: For nodes or pins with many edges (e.g., reset/clk pins), edges overflow into a hashmap or a larger continuous array.
## Main Characteristics
1. **Mutability**: The graph supports adding and removing nodes and edges dynamically.
2. **Fixed IDs**: Once an ID is assigned to a node or pin, it is never reused, even after deletion (tombstone deletion).
3. **Frequent Node Deletion**: Nodes can be deleted frequently, which is common in optimization phases.
4. **Traversal Methods**:
- **Any Order Traversal**: For operations that don't require a specific order.
- **Topological Sort Traversal**: Essential for tasks that depend on processing order.
5. **Edge Addition Frequency**: Adding edges is most frequent during the initial graph creation phase.
6. **Edge Optimization**:
- **Delta Encoding**: Since most edges are "short" (connect nearby nodes), delta encoding is used to store edge differences as smaller integers.
- **Local Edge Storage**: Most nodes have under 8 edges, so edges are stored locally when possible.
7. **Meta Information**: Attributes and additional data are stored separately to keep the core graph structure lightweight.
8. **Concurrency**: The graph does not require thread-safe updates. Parallelism is achieved through parallel read-only operations.
## Main Methods
### Node Management
- **Add Node**: Creates a new node with a unique ID and initializes its pins.
- **Delete Node**: Marks a node as deleted without reusing its ID (tombstone). All the pin edges are deleted.
- **Get Node**: Retrieves a node by its ID. Used by iterators.
- **Set Node Type**: Sets the logic type of a node.
- **Get Node Type**: Returns the logic type of a node.
### Pin Management
- **Add Pin**: Adds a pin to a node, assigning it a unique ID.
- **Delete Pin**: Marks the pin as deleted without reusing the ID (tombstone). All the edges are deleted.
- **Get Pin**: Retrieves a pin by its ID. Used by iterators.
- **Set Port ID**: Sets the port ID of a pin.
- **Get Port ID**: Retrieves the port ID of a pin.
- **Check if Pin is Sink/Driver**: Determines whether a pin is an input (sink) or output (driver).
### Edge Management
- **Add Edge**:
- Adds a bidirectional edge between two pins.
- If the number of edges is small, stores it locally using delta encoding.
- If the edge count exceeds local storage, moves edges to an overflow structure.
- **Delete Edge**: Removes an edge between two pins.
- **Overflow Handling**: Manages the transition of edges from local storage to overflow structures and vice versa.
- **Get Connected Pins**: Iterates over all pins connected to a given pin.
### Traversal Methods
- **Any Order Traversal**: Iterates over nodes and pins without a specific order.
- **Topological Sort Traversal**: Processes nodes in an order respecting dependencies, essential for certain analyses.
## Structure
### Classes and Components from the `hhds` Namespace
1. **Graph**: The main class managing the entire graph structure.
- Contains vectors for nodes, pins, and overflow edges.
- Manages ID assignments and tombstone tracking.
- Provides methods for node, pin, and edge management.
2. **Graph_node**: Represents a logic cell (Node).
- Contains at least one pin (Pin 0).
- Stores type information and local edges.
- Manages overflow if too many edges exist.
3. **Graph_pin**: Represents a connection point on a node (Pin).
- Stores port ID and local edges.
- Knows whether it is an input (sink) or output (driver) pin.
- Manages overflow if too many edges exist.
4. **Edge Overflow Structures**:
- Used when a pin's edges exceed local storage capacity.
- Implemented as hashmaps or larger continuous arrays.
### Data Organization
- **Entry Types**: Each entry in the node and pin vectors has an `Entry_type` to indicate its status.
- **Delta Encoding**: Edge differences are stored as small integers when possible to save space.
- **Overflow Indicators**: Flags like `overflow_link` and `set_link` indicate whether to look for edges in overflow structures.
## Optimizations
### Delta Encoding for Edges
- Stores the difference between connected pin IDs instead of full 32-bit IDs.
- Effective when most edges connect nearby pins.
- Reduces memory footprint and improves cache performance.
### Handling Overflow Edges
- For pins with many edges, local storage is insufficient.
- Edges overflow into a hashmap (`emhash8::HashSet`) or a larger array.
- Overflow structures are managed carefully to balance performance and memory usage.
### Tombstone Deletion
- Nodes and pins are marked as deleted without reusing their IDs.
- Simplifies ID management and avoids potential conflicts.
- Deleted entries can be skipped during traversal.
## Data Types
```cpp
namespace hhds {
using Port_ID = uint16_t; // 16 bits max value
constexpr int Port_bits = std::numeric_limits<Port_ID>::digits - 1;
constexpr Port_ID Port_invalid = ((1 << Port_bits) - 1);
using Bits_t = int32_t; // 17 bits max size
constexpr int Bits_bits = 17;
constexpr Bits_t Bits_max = ((1ULL << Bits_bits) - 1);
}; // namespace hhds
```
## Class Definitions with Methods to Implement
Below are the class definitions for `Graph_node` and `Graph_pin` with the methods that need to be implemented.
### `Graph_node.hpp`
```cpp
#pragma once
#include <array>
#include <cstdint>
#include <vector>
#include "graph_base.hpp"
#include "iassert.hpp"
namespace hhds {
class __attribute__((packed)) Graph_node {
public:
Graph_node();
// Node Type Methods
[[nodiscard]] uint16_t get_type() const;
void set_type(uint16_t t);
// Instance Methods
void set_instance(uint32_t gid);
[[nodiscard]] bool has_instance() const;
[[nodiscard]] uint32_t get_instance() const;
// Edge Management
bool add_edge(uint32_t self_id, uint32_t other_id);
bool del_edge(uint32_t self_id, uint32_t other_id);
// Edge Iteration
class iterator {
public:
using value_type = uint32_t;
using pointer = const int16_t*;
iterator(uint32_t sid, uint32_t x, pointer p, pointer e);
[[nodiscard]] value_type operator*() const;
iterator& operator++();
[[nodiscard]] const iterator operator++(int);
[[nodiscard]] bool operator==(const iterator& rhs) const;
[[nodiscard]] bool operator!=(const iterator& rhs) const;
[[nodiscard]] bool in_xtra() const;
[[nodiscard]] pointer get_ptr();
private:
value_type self_id;
value_type xtra;
pointer ptr;
pointer end;
};
iterator begin(uint32_t self_id);
iterator end();
[[nodiscard]] iterator begin(uint32_t self_id) const;
[[nodiscard]] iterator end() const;
// Edge Utilities
void erase(iterator it);
[[nodiscard]] size_t get_num_local_edges() const;
[[nodiscard]] bool has_edges() const;
// Pin Management
uint32_t get_next_pin_ptr() const;
void set_next_pin_ptr(uint32_t pin_id);
// Node Deletion (deletes all Pins (not only Pin 0) edges too)
void clear();
// Debugging
void dump(uint32_t self_id) const;
private:
void switch_to_overflow(uint32_t overflow_id);
// Node (16 bytes)
// Byte 0:1
Entry_type entry_type : 2; // Free, Node, Pin, Overflow
uint8_t set_link : 1; // Indicates if set link is used
uint8_t overflow_link : 1; // When set, ledge points to overflow
uint8_t n_sedges : 2; // Number of short edges
uint16_t type : 10; // Node type
// SEDGE: 2:7
std::array<int16_t, 3> sedge; // Short edges
// Next Pin Pointer: 8:11
uint32_t next_pin_ptr; // Pointer to the next pin
// Byte 12:15
uint32_t ledge_or_overflow_or_set; // Ledgers or overflow/set pointer
};
}; // namespace hhds
```
### `Graph_pin.hpp`
```cpp
#pragma once
#include <array>
#include <cstdint>
#include <vector>
#include "graph_base.hpp"
#include "iassert.hpp"
namespace hhds {
class __attribute__((packed)) Graph_pin {
public:
Graph_pin(Port_ID pid, bool sink);
// Port ID Methods
[[nodiscard]] Port_ID get_port_id() const;
// Sink/Driver Methods
[[nodiscard]] bool is_sink() const;
// Edge Management
bool add_edge(uint32_t self_id, uint32_t other_id);
bool del_edge(uint32_t self_id, uint32_t other_id);
// Edge Iteration
class iterator {
public:
using value_type = uint32_t;
iterator(uint32_t x1, uint32_t x2);
[[nodiscard]] value_type operator*() const;
iterator& operator++();
[[nodiscard]] const iterator operator++(int);
[[nodiscard]] bool operator==(const iterator& rhs) const;
[[nodiscard]] bool operator!=(const iterator& rhs) const;
[[nodiscard]] bool in_data1() const;
[[nodiscard]] bool in_data2() const;
private:
value_type data1;
value_type data2;
};
iterator begin(uint32_t self_id);
iterator end();
[[nodiscard]] iterator begin(uint32_t self_id) const;
[[nodiscard]] iterator end() const;
// Edge Utilities
void erase(iterator it);
[[nodiscard]] size_t get_num_local_edges() const;
[[nodiscard]] bool has_edges() const;
// Node Association
uint32_t get_node_id() const;
void set_node_id(uint32_t nid);
// Next Pin Pointer (iterate over Pins in a Node)
uint32_t get_next_id() const;
void set_next_id(uint32_t pin_id);
// Pin Deletion (clears all edges too)
void clear();
// Debugging
void dump(uint32_t self_id) const;
private:
// Pin (16 bytes)
// Byte 0:1
Entry_type entry_type : 2; // Free, Node, Pin, Overflow
uint8_t set_link : 1; // Indicates if set link is used
uint8_t overflow_link : 1; // When set, ledge points to overflow
uint8_t sink : 1; // Sink (!driver)
int16_t sedge_0 : 11; // Short edge (11 bits 2-complement)
int16_t portid; // Port ID
// Node ID: 4:7
uint32_t node_id; // Associated node ID
// Next Pin Pointer: 8:11
uint32_t next_id; // Pointer to the next pin
// Byte 12:15
uint32_t ledge_or_overflow_or_set; // Ledgers or overflow/set pointer
};
}; // namespace hhds
```
## Additional Notes
- **Graph Class**: In addition to `Graph_node` and `Graph_pin`, a `Graph` class should be implemented to manage the overall graph structure, including methods for node and pin creation, deletion, traversal, and edge management.
- **Overflow Structures**: The implementation of overflow structures (`emhash8::HashSet` or equivalent) should be defined to handle cases where local edge storage is insufficient.
- **Iterators**: Custom iterators are provided in both `Graph_node` and `Graph_pin` to facilitate traversal over edges.
- **Entry Types**: The `Entry_type` enumeration should be defined in `graph_base.hpp` to represent Free, Node, Pin, and Overflow entries.
- **Assertions**: Use of `IASSERT` or equivalent for debugging and ensuring data integrity.