void Network::buildExecutionNetwork() { E_DEBUG(ENetwork, "building execution network"); clearExecutionNetwork(); // 1- First build the visible network E_DEBUG(ENetwork, " 1- build visible network"); E_DEBUG_INDENT; FractalNode* executionNetworkRoot = visibleNetwork<FractalNode>(_generator); FNodeVector visibleNodes = depthFirstSearch(executionNetworkRoot); // 2- Expand all the nodes of this first graph E_DEBUG_OUTDENT; E_DEBUG(ENetwork, " 2- expand nodes"); E_DEBUG_INDENT; expandNodes(visibleNodes); // 3- connect the expanded versions of the nodes together E_DEBUG_OUTDENT; E_DEBUG(ENetwork, " 3- connect expanded network"); E_DEBUG_INDENT; connectExpandedNodes(visibleNodes); // 4- construct our "clean" execution network and clean up the FractalNodes E_DEBUG_OUTDENT; E_DEBUG(ENetwork, " 4- construct final network"); E_DEBUG_INDENT; FNodeVector expandedNodes = depthFirstSearch(executionNetworkRoot->expanded); E_DEBUG(ENetwork, "num connected expanded nodes: " << expandedNodes.size()); map<FractalNode*, NetworkNode*> falgoMap; // expanded → final network node for (int i=0; i<(int)expandedNodes.size(); i++) { falgoMap[expandedNodes[i]] = new NetworkNode(expandedNodes[i]->algorithm()); } for (int i=0; i<(int)expandedNodes.size(); i++) { NetworkNode* parent = falgoMap[expandedNodes[i]]; vector<FractalNode*> children = expandedNodes[i]->children(); for (int j=0; j<(int)children.size(); j++) { E_DEBUG(ENetwork, " - " << parent->algorithm()->name() << " → " << falgoMap[children[j]]->algorithm()->name()); parent->addChild(falgoMap[children[j]]); } } _executionNetworkRoot = falgoMap[executionNetworkRoot->expanded]; // delete the FractalNodes which we just used temporarily for building the network E_DEBUG(ENetwork, "cleaning up temp visible fractal nodes"); for (int i=0; i<(int)visibleNodes.size(); i++) delete visibleNodes[i]; E_DEBUG(ENetwork, "cleaning up temp expanded fractal nodes"); for (int i=0; i<(int)expandedNodes.size(); i++) delete expandedNodes[i]; E_DEBUG_OUTDENT; E_DEBUG(ENetwork, "execution network ok"); }
void Network::topologicalSortExecutionNetwork() { // Note: we don't need to do a full-fledged topological sort here, as we do not // have any DAG, we actually have a dependency tree. This way we can just do a // depth-first search, with ref-counting to account for diamond shapes in the tree. // this is similar to the wavefront design pattern used in parallelization // Using DFS here also has the advantage that it makes as much as possible use // of cache locality // 1- get all the nodes and count the number of refs they have NodeVector nodes = depthFirstSearch(_executionNetworkRoot); map<NetworkNode*, int> refs; // this initialization should be useless, but let's do it anyway for clarity for (int i=0; i<(int)nodes.size(); i++) refs[nodes[i]] = 0; // count the number of refs for each node for (int i=0; i<(int)nodes.size(); i++) { const NodeVector& children = nodes[i]->children(); for (int j=0; j<(int)children.size(); j++) { refs[children[j]] += 1; } } // 2- do DFS again, manually this time and only visit node which have no refs anymore _toposortedNetwork.clear(); NodeStack toVisit; toVisit.push(_executionNetworkRoot); refs[_executionNetworkRoot] = 1; while (!toVisit.empty()) { NetworkNode* currentNode = toVisit.top(); toVisit.pop(); if (--refs[currentNode] == 0) { _toposortedNetwork.push_back(currentNode->algorithm()); // keep this node, it is good const NodeVector& children = currentNode->children(); for (int i=0; i<(int)children.size(); i++) { toVisit.push(children[i]); } } } E_DEBUG(ENetwork, "-------------------------------------------------------------------------------------------"); for (int i=0; i<(int)_toposortedNetwork.size(); i++) { E_DEBUG_NONL(ENetwork, " → " << _toposortedNetwork[i]->name()); } E_DEBUG(ENetwork, ""); // for adding a newline E_DEBUG(ENetwork, "-------------------------------------------------------------------------------------------"); }