-
Notifications
You must be signed in to change notification settings - Fork 699
[Graph Partitioning] Add optimization to minimize communication cost and number of partitions. #2359
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@@ -30,19 +31,30 @@ namespace glow { | |||
using namespace runtime; | |||
|
|||
using MemUsageMap = std::unordered_map<Node *, unsigned>; | |||
using NodesSet = std::set<Node *>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nit: But I think we tend to call most type aliases something like TypeXyzTy
, i.e. Ty
at the end.
@@ -102,6 +124,19 @@ class Partitioner { | |||
/// function. | |||
void initOpMemUsage(); | |||
|
|||
/// Combine the partitions if necessary : if all outside uses of the nodes in | |||
/// /// partition1 is in partition2, and the sum of memory comsuption of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two ///
are used here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: comsuption -> consumption
lib/Partitioner/PartitionUtils.cpp
Outdated
namespace glow { | ||
|
||
/// Given \p nodes, return a list of nodes who use any node in this set. | ||
std::vector<Node *> getOutUsers(std::set<Node *> nodes) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you really need to pass nodes
by value? Why not const std::set<Node *> &
?
lib/Partitioner/PartitionUtils.cpp
Outdated
|
||
/// Given \p nodes, return a list of nodes who use only the nodes in this set or | ||
/// constant. | ||
std::vector<Node *> getOutUsersWithOnePre(std::set<Node *> nodes) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you really need to pass nodes
by value? Why not const std::set<Node *> &
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In getOutUsersWithOnePre
what does the Pre
part means? Predecessor? May be it should be a bit longer than Pre
to be more descriptive?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it means "Predecessor" :)
lib/Partitioner/PartitionUtils.cpp
Outdated
Node *cur = *it; | ||
// For Save onde, the only required memory is for output. | ||
if (cur->getKind() == Kinded::Kind::SaveNodeKind) { | ||
SaveNode *tmp = (SaveNode *)cur; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use llvm::cast
instead of C-style cast.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or even better:
if (auto *SN = llvm::dyn_cast<SaveNode>(cur)) {...}
lib/Partitioner/Partitioner.cpp
Outdated
continue; | ||
} | ||
Function *cur = (*it).first; | ||
uint64_t mem = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be call it memCost
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or memSize
lib/Partitioner/Partitioner.cpp
Outdated
if (memUsage_[outUsers[i]] + mem > availableMemory) { | ||
continue; | ||
} | ||
// Rule 2: this move won't cause constant dupication. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: May be it is OK to have constants duplication if the size of a constant is below a certain threshold?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: dupication -> duplication
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: May be it is OK to have constants duplication if the size of a constant is below a certain threshold?
@opti-mix Theoretically, it should be allowed if it reduce the communication cost a lot or good for workload balance. Will think about it for the next step. Thanks!
lib/Partitioner/Partitioner.cpp
Outdated
if (cont) { | ||
continue; | ||
} | ||
// Rule 3: this move won't increase communication cost. Even this move |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Even this/Even if this/
lib/Partitioner/Partitioner.cpp
Outdated
// or paritionCombine. | ||
nSet.insert(outUsers[i]); | ||
GraphMemInfo cost = getGraphMemInfo(nSet); | ||
if (cost.outMem <= communicationCost) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again, may be outMemSize
or outMemCost
instead of outMem
?
if (nodesSet[suc].empty()) { | ||
// It is possible that after moving a node from Partition2 to | ||
// Partition1, Partition2 become empty. Remove the empty partition. | ||
partitions.deletePartition(suc); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here you could reuse the partition removal logic if it would be a separate method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here nodesSet
is not partitions
's member, and this is just one circumstance under which a partition need to be removed. Therefore, I only add deletePartition
as a method.
lib/Partitioner/Partitioner.cpp
Outdated
partitions.setCost((*it).first, cost); | ||
} | ||
|
||
// Move/Exchange nodes between any two connected partions, until no gain is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: partions
-> partitions
lib/Partitioner/Partitioner.cpp
Outdated
@@ -354,12 +495,14 @@ DAGNodeList &Partitioner::Partition() { | |||
|
|||
doPartitioning(F_, partitionMap); | |||
|
|||
F_->dumpDAG("f1.dot"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stale debug code
lib/Partitioner/Partitioner.cpp
Outdated
// Remove the original function after partitioning. | ||
module_->eraseFunction(F_); | ||
|
||
auto funcList = module_->getFunctions(); | ||
for (Function *F : funcList) { | ||
(void)F; | ||
F->dumpDAG("f2.dot"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stale debug code
lib/Partitioner/CMakeLists.txt
Outdated
@@ -1,5 +1,6 @@ | |||
add_library(Partitioner | |||
Partitioner.cpp) | |||
PartitionUtils.cpp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
major nit, but: can we rename PartitionUtils.{cpp, h}
to PartitionerUtils.{cpp, h}
? Just for consistency in other file names, since we have Partitioner.{cpp, h}, PartitionerTest.cpp
, etc.
/// The memory usage of a subgraph (i.e. a list of nodes of a function). | ||
struct GraphMemInfo { | ||
// The memory usage of all input nodes (whose predecessors are not included in | ||
// this subgraph) of this subgraph |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: add period at end of comment.
lib/Partitioner/PartitionUtils.cpp
Outdated
|
||
#include "glow/Partitioner/PartitionUtils.h" | ||
|
||
#include <iostream> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you need this?
lib/Partitioner/Partitioner.cpp
Outdated
if (memUsage_[outUsers[i]] + mem > availableMemory) { | ||
continue; | ||
} | ||
// Rule 2: this move won't cause constant dupication. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: dupication -> duplication
lib/Partitioner/Partitioner.cpp
Outdated
// Step1 Move: Assume Partition1 -> Partition2, try to move nodes from | ||
// Partition2 to Partition1 if those nodes only use the nodes in | ||
// Partition1(recursively) and the move won't make Partition1's memory exceeds | ||
// the memory constratint, and the communication cost is minimized. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: constratint -> constraint
lib/Partitioner/Partitioner.cpp
Outdated
} | ||
// Rule 3: this move won't increase communication cost. Even this move | ||
// won't change communication cost, according to rule 1 and rule 2, the | ||
// memory comsuption of the partition where this node (i.e outUsers[i]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: comsuption -> consumption
lib/Partitioner/Partitioner.cpp
Outdated
@@ -139,6 +139,147 @@ static BFSLevel getBFSLevel(Function *F) { | |||
return bfs; | |||
} | |||
|
|||
// Combine the partitions if necessary : if all outside uses of the nodes in | |||
// partition1 is in partition2, and the sum of memory comsuption of partition1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: comsuption -> consumption
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lib/Partitioner/PartitionUtils.cpp
Outdated
|
||
namespace glow { | ||
|
||
/// Given \p nodes, return a list of nodes who use any node in this set. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really any nodes who use this set that are not in this set right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes :)
…and number of partitions.
Description:
Based on the initial partitions, this PR adds:
Testing:

For unittest PartitionerTest::Basic1, the communication cost is
288 bytes->256 bytes
.For PartitionerTest::Basic2, the communication cost is
160 bytes->64 bytes
. And the number of partitions is3 -> 2
. The following 2 pics shows the initial partition and optimized partitions:The initial partition ( Memory 2048 bytes. 3 partitions, with memory (1152 bytes, 1152 bytes, 1376 bytes)
The optimized partition (Memory 2048 bytes. 2 partitions, with memory (1728, 1728) :

Documentation: #2298
[Optional Fixes #issue]
Please see a detailed explanation of how to fill out the fields in the relevant sections in PULL_REQUEST.md.