15#include "llvm/Config/llvm-config.h"
24#define DEBUG_TYPE "balanced-partitioning"
27 OS <<
formatv(
"{{ID={0} Utilities={{{1:$[,]}} Bucket={2}}",
Id,
31template <
typename Func>
32void BalancedPartitioning::BPThreadPool::async(Func &&
F) {
33#if LLVM_ENABLE_THREADS
36 TheThreadPool.
async([
this,
F]() {
41 if (--NumActiveThreads == 0) {
44 std::unique_lock<std::mutex> lock(mtx);
45 assert(!IsFinishedSpawning);
46 IsFinishedSpawning =
true;
56void BalancedPartitioning::BPThreadPool::wait() {
57#if LLVM_ENABLE_THREADS
61 std::unique_lock<std::mutex> lock(mtx);
62 cv.wait(lock, [&]() {
return IsFinishedSpawning; });
63 assert(IsFinishedSpawning && NumActiveThreads == 0);
77 for (
unsigned I = 1;
I < LOG_CACHE_SIZE;
I++)
78 Log2Cache[
I] = std::log2(
I);
84 "Partitioning %d nodes using depth %d and %d iterations per split\n",
85 Nodes.size(), Config.SplitDepth, Config.IterationsPerSplit));
86 std::optional<BPThreadPool> TP;
87#if LLVM_ENABLE_THREADS
89 if (Config.TaskSplitDepth > 1)
90 TP.emplace(TheThreadPool);
94 for (
unsigned I = 0;
I < Nodes.size();
I++)
95 Nodes[
I].InputOrderIndex =
I;
98 auto BisectTask = [
this, NodesRange, &TP]() {
99 bisect(NodesRange, 0, 1, 0, TP);
102 TP->async(std::move(BisectTask));
109 return L.Bucket < R.Bucket;
115void BalancedPartitioning::bisect(
const FunctionNodeRange Nodes,
116 unsigned RecDepth,
unsigned RootBucket,
118 std::optional<BPThreadPool> &TP)
const {
120 if (NumNodes <= 1 || RecDepth >= Config.
SplitDepth) {
123 llvm::sort(Nodes, [](
const auto &L,
const auto &R) {
124 return L.InputOrderIndex < R.InputOrderIndex;
126 for (
auto &
N : Nodes)
132 NumNodes, RootBucket));
134 std::mt19937 RNG(RootBucket);
136 unsigned LeftBucket = 2 * RootBucket;
137 unsigned RightBucket = 2 * RootBucket + 1;
140 split(Nodes, LeftBucket);
142 runIterations(Nodes, LeftBucket, RightBucket, RNG);
147 unsigned MidOffset =
Offset + std::distance(Nodes.begin(), NodesMid);
152 auto LeftRecTask = [
this, LeftNodes, RecDepth, LeftBucket,
Offset, &TP]() {
153 bisect(LeftNodes, RecDepth + 1, LeftBucket,
Offset, TP);
155 auto RightRecTask = [
this, RightNodes, RecDepth, RightBucket, MidOffset,
157 bisect(RightNodes, RecDepth + 1, RightBucket, MidOffset, TP);
160 if (TP && RecDepth < Config.TaskSplitDepth && NumNodes >= 4) {
161 TP->async(std::move(LeftRecTask));
162 TP->async(std::move(RightRecTask));
169void BalancedPartitioning::runIterations(
const FunctionNodeRange Nodes,
171 unsigned RightBucket,
172 std::mt19937 &RNG)
const {
174 DenseMap<BPFunctionNode::UtilityNodeT, unsigned> UtilityNodeIndex;
175 for (
auto &
N : Nodes)
176 for (
auto &UN :
N.UtilityNodes)
177 ++UtilityNodeIndex[UN];
180 for (
auto &
N : Nodes)
182 unsigned UNI = UtilityNodeIndex[UN];
183 return UNI == 1 || UNI == NumNodes;
187 UtilityNodeIndex.
clear();
188 for (
auto &
N : Nodes)
189 for (
auto &UN :
N.UtilityNodes)
190 UN = UtilityNodeIndex.
insert({UN, UtilityNodeIndex.
size()}).first->second;
193 SignaturesT Signatures(UtilityNodeIndex.
size());
194 for (
auto &
N : Nodes) {
195 for (
auto &UN :
N.UtilityNodes) {
196 assert(UN < Signatures.size());
197 if (
N.Bucket == LeftBucket) {
198 Signatures[UN].LeftCount++;
200 Signatures[UN].RightCount++;
205 for (
unsigned I = 0;
I < Config.IterationsPerSplit;
I++) {
206 unsigned NumMovedNodes =
207 runIteration(Nodes, LeftBucket, RightBucket, Signatures, RNG);
208 if (NumMovedNodes == 0)
213unsigned BalancedPartitioning::runIteration(
const FunctionNodeRange Nodes,
215 unsigned RightBucket,
216 SignaturesT &Signatures,
217 std::mt19937 &RNG)
const {
219 for (
auto &Signature : Signatures) {
220 if (Signature.CachedGainIsValid)
222 unsigned L = Signature.LeftCount;
223 unsigned R = Signature.RightCount;
224 assert((L > 0 || R > 0) &&
"incorrect signature");
225 float Cost = logCost(L, R);
226 Signature.CachedGainLR = 0.f;
227 Signature.CachedGainRL = 0.f;
229 Signature.CachedGainLR =
Cost - logCost(L - 1, R + 1);
231 Signature.CachedGainRL =
Cost - logCost(L + 1, R - 1);
232 Signature.CachedGainIsValid =
true;
236 using GainPair = std::pair<float, BPFunctionNode *>;
237 std::vector<GainPair> Gains;
238 for (
auto &
N : Nodes) {
239 bool FromLeftToRight = (
N.Bucket == LeftBucket);
240 float Gain =
moveGain(
N, FromLeftToRight, Signatures);
241 Gains.push_back(std::make_pair(Gain, &
N));
246 Gains, [&](
const auto &GP) {
return GP.second->Bucket == LeftBucket; });
251 auto LargerGain = [](
const auto &
L,
const auto &
R) {
252 return L.first >
R.first;
257 unsigned NumMovedDataVertices = 0;
258 for (
auto [LeftPair, RightPair] :
llvm::zip(LeftRange, RightRange)) {
259 auto &[LeftGain, LeftNode] = LeftPair;
260 auto &[RightGain, RightNode] = RightPair;
262 if (LeftGain + RightGain <= 0.f)
265 if (moveFunctionNode(*LeftNode, LeftBucket, RightBucket, Signatures, RNG))
266 ++NumMovedDataVertices;
267 if (moveFunctionNode(*RightNode, LeftBucket, RightBucket, Signatures, RNG))
268 ++NumMovedDataVertices;
270 return NumMovedDataVertices;
275 unsigned RightBucket,
276 SignaturesT &Signatures,
277 std::mt19937 &RNG)
const {
279 if (std::uniform_real_distribution<float>(0.f, 1.f)(RNG) <=
280 Config.SkipProbability)
283 bool FromLeftToRight = (
N.Bucket == LeftBucket);
285 N.Bucket = (FromLeftToRight ? RightBucket : LeftBucket);
288 if (FromLeftToRight) {
289 for (
auto &UN :
N.UtilityNodes) {
290 auto &Signature = Signatures[UN];
291 Signature.LeftCount--;
292 Signature.RightCount++;
293 Signature.CachedGainIsValid =
false;
296 for (
auto &UN :
N.UtilityNodes) {
297 auto &Signature = Signatures[UN];
298 Signature.LeftCount++;
299 Signature.RightCount--;
300 Signature.CachedGainIsValid =
false;
306void BalancedPartitioning::split(
const FunctionNodeRange Nodes,
307 unsigned StartBucket)
const {
309 auto NodesMid = Nodes.begin() + (NumNodes + 1) / 2;
312 return L.InputOrderIndex <
R.InputOrderIndex;
316 N.Bucket = StartBucket;
318 N.Bucket = StartBucket + 1;
322 bool FromLeftToRight,
323 const SignaturesT &Signatures) {
325 for (
auto &UN :
N.UtilityNodes)
326 Gain += (FromLeftToRight ? Signatures[UN].CachedGainLR
327 : Signatures[UN].CachedGainRL);
331float BalancedPartitioning::logCost(
unsigned X,
unsigned Y)
const {
332 return -(
X * log2Cached(
X + 1) +
Y * log2Cached(
Y + 1));
335float BalancedPartitioning::log2Cached(
unsigned i)
const {
336 return (i < LOG_CACHE_SIZE) ? Log2Cache[i] : std::log2(i);
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static TableGen::Emitter::Opt Y("gen-skeleton-entry", EmitSkeleton, "Generate example skeleton entry")
A function with a set of utility nodes where it is beneficial to order two functions close together i...
IDT Id
The ID of this node.
SmallVector< UtilityNodeT, 4 > UtilityNodes
The list of utility nodes associated with this node.
std::optional< unsigned > Bucket
The bucket assigned by balanced partitioning.
LLVM_ABI void dump(raw_ostream &OS) const
static LLVM_ABI float moveGain(const BPFunctionNode &N, bool FromLeftToRight, const SignaturesT &Signatures)
Compute the move gain for uniform log-gap cost.
LLVM_ABI void run(std::vector< BPFunctionNode > &Nodes) const
Run recursive graph partitioning that optimizes a given objective.
LLVM_ABI BalancedPartitioning(const BalancedPartitioningConfig &Config)
std::pair< iterator, bool > insert(const std::pair< KeyT, ValueT > &KV)
auto async(Function &&F, Args &&...ArgList)
Asynchronous submission of a task to the pool.
This class implements an extremely fast bulk output stream that can only output to a stream.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
This is an optimization pass for GlobalISel generic memory operations.
detail::zippy< detail::zip_shortest, T, U, Args... > zip(T &&t, U &&u, Args &&...args)
zip iterator for two or more iteratable types.
void stable_sort(R &&Range)
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
iterator_range< T > make_range(T x, T y)
Convenience function for iterating over sub-ranges.
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
void sort(IteratorTy Start, IteratorTy End)
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
format_object< Ts... > format(const char *Fmt, const Ts &... Vals)
These are helper functions used to produce formatted output.
SingleThreadExecutor DefaultThreadPool
auto partition(R &&Range, UnaryPredicate P)
Provide wrappers to std::partition which take ranges instead of having to pass begin/end explicitly.
void erase_if(Container &C, UnaryPredicate P)
Provide a container algorithm similar to C++ Library Fundamentals v2's erase_if which is equivalent t...
Algorithm parameters; default values are tuned on real-world binaries.
unsigned SplitDepth
The depth of the recursive bisection.