AMF-Placer  2.0
An Open-Source Timing-driven Analytical Mixed-size FPGA Placer
GraphPartitioner.cc
Go to the documentation of this file.
1 
25 #include "GraphPartitioner.h"
26 #include <fstream>
27 #include <string>
28 #include <sys/stat.h>
29 #include <unistd.h>
30 
31 template <class NodeList, class NetList>
32 void GraphPartitioner<NodeList, NetList>::solve(int eachClusterDSPNum, int eachClusterBRAMNum)
33 {
34  clusters.clear();
35  std::vector<int> inputCluster;
36  inputCluster.clear();
37  for (unsigned int i = 0; i < nodeList.size(); i++)
38  {
39  inputCluster.push_back(i);
40  }
41  recursiveMinCutPartition(inputCluster, this, eachClusterDSPNum, eachClusterBRAMNum);
42  sortClustersBySize();
43 }
44 
45 template <class NodeList, class NetList> void GraphPartitioner<NodeList, NetList>::sortClustersBySize()
46 {
47  // std::vector<std::vector<int>>
48  int numClusters = clusters.size();
49  for (int i = 0; i < numClusters; i++)
50  {
51 
52  for (int j = i + 1; j < numClusters; j++)
53  {
54  if (clusters[i].size() > clusters[j].size())
55  {
56  std::vector<int> tmpNodes = clusters[i];
57  clusters[i] = clusters[j];
58  clusters[j] = tmpNodes;
59  }
60  }
61  }
62 }
63 
64 template <class NodeList, class NetList>
66  std::vector<int> &inputCluster, GraphPartitioner<NodeList, NetList> *graphPartitioner, int eachClusterDSPNum,
67  int eachClusterBRAMNum)
68 {
69 
70  std::array<std::vector<int>, 2> outputClusters;
71 
72  sem_wait(&graphPartitioner->partitionSem);
73  int cut = graphPartitioner->minCutBipartition(inputCluster, outputClusters, graphPartitioner, eachClusterDSPNum,
74  eachClusterBRAMNum);
75  sem_post(&graphPartitioner->partitionSem);
76 
77  if (cut == 0)
78  {
79  graphPartitioner->clustersLock.lock();
80  graphPartitioner->clusters.push_back(inputCluster);
81  graphPartitioner->clustersLock.unlock();
82  return;
83  }
84  if (outputClusters[0].size() == 0 && outputClusters[1].size() == 0)
85  {
86  graphPartitioner->clustersLock.lock();
87  graphPartitioner->clusters.push_back(inputCluster);
88  graphPartitioner->clustersLock.unlock();
89  return;
90  }
91  assert(outputClusters[0].size() > 0 && outputClusters[1].size() > 0);
92 
93  std::thread t1(recursiveMinCutPartition, std::ref(outputClusters[0]), std::ref(graphPartitioner),
94  std::ref(eachClusterDSPNum), std::ref(eachClusterBRAMNum));
95  std::thread t2(recursiveMinCutPartition, std::ref(outputClusters[1]), std::ref(graphPartitioner),
96  std::ref(eachClusterDSPNum), std::ref(eachClusterBRAMNum));
97  t1.join();
98  t2.join();
99 }
100 
101 inline bool checkFileExist(const std::string &name)
102 {
103  struct stat buffer;
104  return (stat(name.c_str(), &buffer) == 0);
105 }
106 
107 template <class NodeList, class NetList>
108 unsigned GraphPartitioner<NodeList, NetList>::minCutBipartition(const std::vector<int> &inputCluster,
109  std::array<std::vector<int>, 2> &outputClusters,
110  GraphPartitioner<NodeList, NetList> *graphPartitioner,
111  int eachClusterDSPNum, int eachClusterBRAMNum)
112 {
113 
114  static int cnt = 0;
115 
116  outputClusters[0].clear();
117  outputClusters[1].clear();
118  unsigned int totalWeight = 0;
119 
120  int totalDSPNum = 0;
121  int totalBRAMNum = 0;
122  for (auto PUId : inputCluster)
123  {
124  totalWeight += graphPartitioner->nodeList[PUId]->getWeight();
125  totalBRAMNum += graphPartitioner->nodeList[PUId]->getBRAMNum();
126  totalDSPNum += graphPartitioner->nodeList[PUId]->getDSPNum();
127  }
128 
129  // Constraints
130  unsigned minCellClusterSize = graphPartitioner->minClusterCellNum;
131 
132  if (inputCluster.size() < minCellClusterSize / 5)
133  return 0;
134 
135  if (totalDSPNum <= eachClusterDSPNum && totalBRAMNum <= eachClusterBRAMNum)
136  {
137  if (double(minCellClusterSize) > 0.8 * totalWeight)
138  return 0;
139  else
140  {
141  if (graphPartitioner->verbose)
142  print_info("partitioning iter#" + std::to_string(cnt) + " #curNode: " +
143  std::to_string(inputCluster.size()) + " totalWeight=" + std::to_string(totalWeight) +
144  " minCellClusterSize > 0.8 * totalWeight => SHOULD CONTINUE PARTITIONING");
145  }
146  }
147  else
148  {
149  graphPartitioner->cntLock.lock();
150  if (graphPartitioner->verbose)
151  print_info("partitioning iter#" + std::to_string(cnt) + " #curNode: " +
152  std::to_string(inputCluster.size()) + " #totalDSPNum: " + std::to_string(totalDSPNum) +
153  " #totalBRAMNum: " + std::to_string(totalBRAMNum) + " SHOULD CONTINUE PARTITIONING");
154  graphPartitioner->cntLock.unlock();
155  }
156 
157  if (0.4 * double(minCellClusterSize) > totalWeight)
158  return 0;
159 
160  // map placement unit to hyperNodes for PaToH
161  int numHyperNodes = 0; // num of placement unit
162  std::vector<int> placementUnit2hyperNode(graphPartitioner->nodeList.size(), -1);
163 
164  for (auto instId : inputCluster)
165  placementUnit2hyperNode[instId] = numHyperNodes++;
166 
167  int numPlacementNets = 0;
168  std::vector<bool> isCsdNet(graphPartitioner->netList.size(), false);
169  int numPlacementPins = 0;
170  for (auto net : graphPartitioner->netList)
171  {
172  bool exist = false;
173  for (auto tmpPU : net->getUnits())
174  {
175  if (placementUnit2hyperNode[tmpPU->getId()] != -1)
176  {
177  ++numPlacementPins;
178  exist = true;
179  }
180  }
181  if (exist)
182  {
183  ++numPlacementNets;
184  isCsdNet[net->getId()] = true;
185  }
186  }
187 
188  unsigned int shareMemorySize = 6 + numHyperNodes + numPlacementNets + 1 + numPlacementPins + numHyperNodes + 2;
189 
190  std::string targetPath = getExePath() + "/partitionHyperGraph";
191 
193  targetPath = getExePath() + "/partitionHyperGraph";
195  targetPath = getExePath() + "/partitionHyperGraph";
196 
198  {
199  std::cout << "try to find [" << targetPath << "] but not found.\n";
200  assert(false && "target partitioning process executable should be found!");
201  }
202 
203  ExternalProcessFunc *externalProc =
204  new ExternalProcessFunc(targetPath, shareMemorySize * 4, graphPartitioner->verbose);
205  int *shareMemory = (int *)externalProc->getSharedMemory();
206  shareMemory[0] = numHyperNodes;
207  shareMemory[1] = numPlacementNets;
208  shareMemory[2] = numPlacementPins;
209  double final_imbal = std::max(0.5 - double(minCellClusterSize) / totalWeight, 0.4);
210  *(double *)(shareMemory + 3) = final_imbal;
211  int *cut = shareMemory + 5;
212  int *cwghts = shareMemory + 6;
213  int *xpins = cwghts + numHyperNodes;
214  int *pins = xpins + numPlacementNets + 1;
215  int *partvec = pins + numPlacementPins;
216  int *partweights = partvec + numHyperNodes;
217 
218  int *hyperNode2placementUnit = new int[numHyperNodes];
219  for (auto instId : inputCluster)
220  hyperNode2placementUnit[placementUnit2hyperNode[instId]] = instId;
221 
222  for (int i = 0; i < numHyperNodes; ++i)
223  {
224  assert((unsigned int)hyperNode2placementUnit[i] < graphPartitioner->nodeList.size());
225  cwghts[i] = graphPartitioner->nodeList[hyperNode2placementUnit[i]]->getWeight();
226  partvec[i] = -1;
227  }
228 
229  // map placement nets to hypernet for PaToH
230 
231  int maxCut = maxCutRate * numPlacementNets;
232 
233  // hyper net to hyper nodes
234  xpins[0] = 0;
235  int indexNet = 0, indexPin = 0;
236  for (auto net : graphPartitioner->netList)
237  {
238  if (isCsdNet[net->getId()])
239  {
240  for (auto tmpPU : net->getUnits())
241  if (placementUnit2hyperNode[tmpPU->getId()] != -1)
242  pins[indexPin++] = placementUnit2hyperNode[tmpPU->getId()];
243  xpins[++indexNet] = indexPin;
244  }
245  }
246  assert(indexPin == numPlacementPins && indexNet == numPlacementNets);
247 
248  // call external process to execute
249  externalProc->execute();
250 
251  // Postprocessing
252  for (int i = 0; i < numHyperNodes; ++i)
253  outputClusters[partvec[i]].push_back(inputCluster[i]);
254 
255  int totalWeight0 = 0;
256  for (auto PUId : outputClusters[0])
257  totalWeight0 += graphPartitioner->nodeList[PUId]->getWeight();
258  int totalWeight1 = 0;
259  for (auto PUId : outputClusters[1])
260  totalWeight1 += graphPartitioner->nodeList[PUId]->getWeight();
261 
262  unsigned minClusterTotalWeight = std::min(totalWeight0, totalWeight1);
263  double imbal = 0.5 - double(minClusterTotalWeight) / totalWeight;
264 
265  graphPartitioner->cntLock.lock();
266  if ((*cut > maxCut || ((float)outputClusters[0].size() / (float)outputClusters[1].size() < 0.666) ||
267  ((float)outputClusters[1].size() / (float)outputClusters[0].size() < 0.666)) &&
268  (minCellClusterSize * 1.25 > totalWeight && totalDSPNum <= 3 * eachClusterDSPNum &&
269  totalBRAMNum <= 3 * eachClusterBRAMNum))
270  {
271  // if (graphPartitioner->verbose)
272  print_warning("partitioned iter#" + std::to_string(cnt) + " #node: " + std::to_string(numHyperNodes) +
273  " #net: " + std::to_string(numPlacementNets) + " max_cut: " + std::to_string(maxCut) +
274  " max_imbal: " + std::to_string(final_imbal) + " get too large cut: " + std::to_string(*cut) +
275  " failed to find cut meeting requirements of min-cut or size balance. cluster[0].size=" +
276  std::to_string(outputClusters[0].size()) +
277  " cluster[1].size=" + std::to_string(outputClusters[1].size()));
278  outputClusters[0].clear();
279  outputClusters[1].clear();
280  }
281  else
282  {
283  if (graphPartitioner->verbose)
284  print_info("partitioned iter#" + std::to_string(cnt) + " #node: " + std::to_string(numHyperNodes) +
285  " #net: " + std::to_string(numPlacementNets) + " max_cut: " + std::to_string(maxCut) +
286  " max_imbal: " + std::to_string(final_imbal) + " succeed with " +
287  std::to_string(partweights[0]) + " and " + std::to_string(partweights[1]) +
288  " (cut: " + std::to_string(*cut) + ", imbal: " + std::to_string(imbal) + ")" +
289  "cluster[0].size=" + std::to_string(outputClusters[0].size()) +
290  " cluster[1].size=" + std::to_string(outputClusters[1].size()));
291  }
292 
293  ++cnt;
294  graphPartitioner->cntLock.unlock();
295 
296  int res = *cut;
297  delete[] hyperNode2placementUnit;
298  delete externalProc;
299 
300  return res;
301 }
302 
303 // declare involved templates
305  std::vector<PlacementInfo::PlacementNet *>>;
306 template class GraphPartitioner<std::vector<PlacementInfo::ClusterUnit *>, std::vector<PlacementInfo::ClusterNet *>>;
paintPlacement.cnt
int cnt
Definition: paintPlacement.py:155
ExternalProcessFunc
ExternalProcessFunc is a wrapper of an external exectable for multi-process scenario with shared memo...
Definition: ExternalProcessFunc.h:54
checkFileExist
bool checkFileExist(const std::string &name)
Definition: GraphPartitioner.cc:101
GraphPartitioner.h
GraphPartitioner::netList
NetList & netList
Definition: GraphPartitioner.h:176
GraphPartitioner::verbose
bool verbose
Definition: GraphPartitioner.h:186
GraphPartitioner::clusters
std::vector< std::vector< int > > clusters
the resultant clusters (vectors) after partitioning
Definition: GraphPartitioner.h:158
GraphPartitioner::clustersLock
std::mutex clustersLock
Definition: GraphPartitioner.h:171
AMF-Vivado-2020-testSet.targetPath
string targetPath
Definition: AMF-Vivado-2020-testSet.py:15
GraphPartitioner
GraphPartitioner will recursively bi-partition the netlist (which could be netlist of clusters) based...
Definition: GraphPartitioner.h:52
GraphPartitioner::recursiveMinCutPartition
static void recursiveMinCutPartition(std::vector< int > &inputCluster, GraphPartitioner< NodeList, NetList > *graphPartitioner, int eachClusterDSPNum, int eachClusterBRAMNum)
a recursive function which will recursively bi-partition the input cluster into two based on connecti...
Definition: GraphPartitioner.cc:65
print_warning
void print_warning(std::string tmp_string)
Definition: strPrint.cc:57
GraphPartitioner::partitionSem
sem_t partitionSem
used to limit the number of processes for the parallel partitioning
Definition: GraphPartitioner.h:170
GraphPartitioner::minCutBipartition
unsigned minCutBipartition(const std::vector< int > &inputCluster, std::array< std::vector< int >, 2 > &outputClusters, GraphPartitioner< NodeList, NetList > *graphPartitioner, int eachClusterDSPNum, int eachClusterBRAMNum)
the actual function to call partitioner to conduct partitioning with given clusters and parameters
Definition: GraphPartitioner.cc:108
ExternalProcessFunc::getSharedMemory
void * getSharedMemory()
Definition: ExternalProcessFunc.h:113
GraphPartitioner::cntLock
std::mutex cntLock
Definition: GraphPartitioner.h:171
GraphPartitioner::nodeList
NodeList & nodeList
Definition: GraphPartitioner.h:175
ExternalProcessFunc::execute
void execute()
Definition: ExternalProcessFunc.h:118
GraphPartitioner::sortClustersBySize
void sortClustersBySize()
sort clusters by sizes to fix the output clusters' order for later processing and avoid the random fa...
Definition: GraphPartitioner.cc:45
getExePath
std::string getExePath()
Definition: sysInfo.cc:27
GraphPartitioner::solve
void solve(int eachClusterDSPNum, int eachClusterBRAMNum)
a caller which will start the partitioning procedure
Definition: GraphPartitioner.cc:32
print_info
void print_info(std::string tmp_string)
Definition: strPrint.cc:39
GraphPartitioner::minClusterCellNum
unsigned int minClusterCellNum
Definition: GraphPartitioner.h:177
checkHalfColumn.i
int i
Definition: checkHalfColumn.py:5