@@ -220,7 +220,7 @@ class MinCostMaxFlow {
220
220
Now = Pred;
221
221
}
222
222
223
- assert (PathCapacity > 0 && " found incorrect augmenting path" );
223
+ assert (PathCapacity > 0 && " found an incorrect augmenting path" );
224
224
225
225
// Update the flow along the path
226
226
Now = Target;
@@ -271,7 +271,22 @@ class MinCostMaxFlow {
271
271
uint64_t Target;
272
272
};
273
273
274
- // / Post-processing adjustment of the control flow.
274
+ // / A post-processing adjustment of control flow. It applies two steps by
275
+ // / rerouting some flow and making it more realistic:
276
+ // /
277
+ // / - First, it removes all isolated components ("islands") with a positive flow
278
+ // / that are unreachable from the entry block. For every such component, we
279
+ // / find the shortest from the entry to an exit passing through the component,
280
+ // / and increase the flow by one unit along the path.
281
+ // /
282
+ // / - Second, it identifies all "unknown subgraphs" consisting of basic blocks
283
+ // / with no sampled counts. Then it rebalnces the flow that goes through such
284
+ // / a subgraph so that each branch is taken with probability 50%.
285
+ // / An unknown subgraph is such that for every two nodes u and v:
286
+ // / - u dominates v and u is not unknown;
287
+ // / - v post-dominates u; and
288
+ // / - all inner-nodes of all (u,v)-paths are unknown.
289
+ // /
275
290
class FlowAdjuster {
276
291
public:
277
292
FlowAdjuster (FlowFunction &Func) : Func(Func) {
@@ -281,14 +296,16 @@ class FlowAdjuster {
281
296
282
297
// Run the post-processing
283
298
void run () {
284
- // / We adjust the control flow in a function so as to remove all
285
- // / "isolated" components with positive flow that are unreachable
286
- // / from the entry block. For every such component, we find the shortest
287
- // / path from the entry to an exit passing through the component, and
288
- // / increase the flow by one unit along the path.
299
+ // / Adjust the flow to get rid of isolated components.
289
300
joinIsolatedComponents ();
301
+
302
+ // / Rebalance the flow inside unknown subgraphs.
303
+ rebalanceUnknownSubgraphs ();
290
304
}
291
305
306
+ // / The probability for the first successor of a unknown subgraph
307
+ static constexpr double UnknownFirstSuccProbability = 0.5 ;
308
+
292
309
private:
293
310
void joinIsolatedComponents () {
294
311
// Find blocks that are reachable from the source
@@ -315,7 +332,7 @@ class FlowAdjuster {
315
332
}
316
333
}
317
334
318
- // / Run bfs from a given block along the jumps with a positive flow and mark
335
+ // / Run BFS from a given block along the jumps with a positive flow and mark
319
336
// / all reachable blocks.
320
337
void findReachable (uint64_t Src, std::vector<bool > &Visited) {
321
338
if (Visited[Src])
@@ -435,6 +452,164 @@ class FlowAdjuster {
435
452
436
453
uint64_t NumBlocks () const { return Func.Blocks .size (); }
437
454
455
+ // / Rebalance unknown subgraphs so as each branch splits with probabilities
456
+ // / UnknownFirstSuccProbability and 1 - UnknownFirstSuccProbability
457
+ void rebalanceUnknownSubgraphs () {
458
+ assert (UnknownFirstSuccProbability >= 0.0 &&
459
+ UnknownFirstSuccProbability <= 1.0 &&
460
+ " the share of the unknown successor should be between 0 and 1" );
461
+ // Try to find unknown subgraphs from each non-unknown block
462
+ for (uint64_t I = 0 ; I < Func.Blocks .size (); I++) {
463
+ auto SrcBlock = &Func.Blocks [I];
464
+ // Do not attempt to find unknown successors from a unknown or a
465
+ // zero-flow block
466
+ if (SrcBlock->UnknownWeight || SrcBlock->Flow == 0 )
467
+ continue ;
468
+
469
+ std::vector<FlowBlock *> UnknownSuccs;
470
+ FlowBlock *DstBlock = nullptr ;
471
+ // Find a unknown subgraphs starting at block SrcBlock
472
+ if (!findUnknownSubgraph (SrcBlock, DstBlock, UnknownSuccs))
473
+ continue ;
474
+ // At the moment, we do not rebalance subgraphs containing cycles among
475
+ // unknown blocks
476
+ if (!isAcyclicSubgraph (SrcBlock, DstBlock, UnknownSuccs))
477
+ continue ;
478
+
479
+ // Rebalance the flow
480
+ rebalanceUnknownSubgraph (SrcBlock, DstBlock, UnknownSuccs);
481
+ }
482
+ }
483
+
484
+ // / Find a unknown subgraph starting at block SrcBlock.
485
+ // / If the search is successful, the method sets DstBlock and UnknownSuccs.
486
+ bool findUnknownSubgraph (FlowBlock *SrcBlock, FlowBlock *&DstBlock,
487
+ std::vector<FlowBlock *> &UnknownSuccs) {
488
+ // Run BFS from SrcBlock and make sure all paths are going through unknown
489
+ // blocks and end at a non-unknown DstBlock
490
+ auto Visited = std::vector<bool >(NumBlocks (), false );
491
+ std::queue<uint64_t > Queue;
492
+ DstBlock = nullptr ;
493
+
494
+ Queue.push (SrcBlock->Index );
495
+ Visited[SrcBlock->Index ] = true ;
496
+ while (!Queue.empty ()) {
497
+ auto &Block = Func.Blocks [Queue.front ()];
498
+ Queue.pop ();
499
+ // Process blocks reachable from Block
500
+ for (auto Jump : Block.SuccJumps ) {
501
+ uint64_t Dst = Jump->Target ;
502
+ if (Visited[Dst])
503
+ continue ;
504
+ Visited[Dst] = true ;
505
+ if (!Func.Blocks [Dst].UnknownWeight ) {
506
+ // If we see non-unique non-unknown block reachable from SrcBlock,
507
+ // stop processing and skip rebalancing
508
+ FlowBlock *CandidateDstBlock = &Func.Blocks [Dst];
509
+ if (DstBlock != nullptr && DstBlock != CandidateDstBlock)
510
+ return false ;
511
+ DstBlock = CandidateDstBlock;
512
+ } else {
513
+ Queue.push (Dst);
514
+ UnknownSuccs.push_back (&Func.Blocks [Dst]);
515
+ }
516
+ }
517
+ }
518
+
519
+ // If the list of unknown blocks is empty, we don't need rebalancing
520
+ if (UnknownSuccs.empty ())
521
+ return false ;
522
+ // If all reachable nodes from SrcBlock are unknown, skip rebalancing
523
+ if (DstBlock == nullptr )
524
+ return false ;
525
+ // If any of the unknown blocks is an exit block, skip rebalancing
526
+ for (auto Block : UnknownSuccs) {
527
+ if (Block->isExit ())
528
+ return false ;
529
+ }
530
+
531
+ return true ;
532
+ }
533
+
534
+ // / Verify if the given unknown subgraph is acyclic, and if yes, reorder
535
+ // / UnknownSuccs in the topological order (so that all jumps are "forward").
536
+ bool isAcyclicSubgraph (FlowBlock *SrcBlock, FlowBlock *DstBlock,
537
+ std::vector<FlowBlock *> &UnknownSuccs) {
538
+ // Extract local in-degrees in the considered subgraph
539
+ auto LocalInDegree = std::vector<uint64_t >(NumBlocks (), 0 );
540
+ for (auto Jump : SrcBlock->SuccJumps ) {
541
+ LocalInDegree[Jump->Target ]++;
542
+ }
543
+ for (uint64_t I = 0 ; I < UnknownSuccs.size (); I++) {
544
+ for (auto Jump : UnknownSuccs[I]->SuccJumps ) {
545
+ LocalInDegree[Jump->Target ]++;
546
+ }
547
+ }
548
+ // A loop containing SrcBlock
549
+ if (LocalInDegree[SrcBlock->Index ] > 0 )
550
+ return false ;
551
+
552
+ std::vector<FlowBlock *> AcyclicOrder;
553
+ std::queue<uint64_t > Queue;
554
+ Queue.push (SrcBlock->Index );
555
+ while (!Queue.empty ()) {
556
+ auto &Block = Func.Blocks [Queue.front ()];
557
+ Queue.pop ();
558
+ // Stop propagation once we reach DstBlock
559
+ if (Block.Index == DstBlock->Index )
560
+ break ;
561
+
562
+ AcyclicOrder.push_back (&Block);
563
+ // Add to the queue all successors with zero local in-degree
564
+ for (auto Jump : Block.SuccJumps ) {
565
+ uint64_t Dst = Jump->Target ;
566
+ LocalInDegree[Dst]--;
567
+ if (LocalInDegree[Dst] == 0 ) {
568
+ Queue.push (Dst);
569
+ }
570
+ }
571
+ }
572
+
573
+ // If there is a cycle in the subgraph, AcyclicOrder contains only a subset
574
+ // of all blocks
575
+ if (UnknownSuccs.size () + 1 != AcyclicOrder.size ())
576
+ return false ;
577
+ UnknownSuccs = AcyclicOrder;
578
+ return true ;
579
+ }
580
+
581
+ // / Rebalance a given subgraph.
582
+ void rebalanceUnknownSubgraph (FlowBlock *SrcBlock, FlowBlock *DstBlock,
583
+ std::vector<FlowBlock *> &UnknownSuccs) {
584
+ assert (SrcBlock->Flow > 0 && " zero-flow block in unknown subgraph" );
585
+ assert (UnknownSuccs.front () == SrcBlock && " incorrect order of unknowns" );
586
+
587
+ for (auto Block : UnknownSuccs) {
588
+ // Block's flow is the sum of incoming flows
589
+ uint64_t TotalFlow = 0 ;
590
+ if (Block == SrcBlock) {
591
+ TotalFlow = Block->Flow ;
592
+ } else {
593
+ for (auto Jump : Block->PredJumps ) {
594
+ TotalFlow += Jump->Flow ;
595
+ }
596
+ Block->Flow = TotalFlow;
597
+ }
598
+
599
+ // Process all successor jumps and update corresponding flow values
600
+ for (uint64_t I = 0 ; I < Block->SuccJumps .size (); I++) {
601
+ auto Jump = Block->SuccJumps [I];
602
+ if (I + 1 == Block->SuccJumps .size ()) {
603
+ Jump->Flow = TotalFlow;
604
+ continue ;
605
+ }
606
+ uint64_t Flow = uint64_t (TotalFlow * UnknownFirstSuccProbability);
607
+ Jump->Flow = Flow;
608
+ TotalFlow -= Flow;
609
+ }
610
+ }
611
+ }
612
+
438
613
// / A constant indicating an arbitrary exit block of a function.
439
614
static constexpr uint64_t AnyExitBlock = uint64_t (-1 );
440
615
@@ -622,7 +797,7 @@ void verifyWeights(const FlowFunction &Func) {
622
797
}
623
798
}
624
799
625
- // Run bfs from the source along edges with positive flow
800
+ // Run BFS from the source along edges with positive flow
626
801
std::queue<uint64_t > Queue;
627
802
auto Visited = std::vector<bool >(NumBlocks, false );
628
803
Queue.push (Func.Entry );
0 commit comments